<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>SwiftUI Widgets - AppMakers.Dev</title>
	<atom:link href="https://appmakers.dev/category/swiftui/swiftui-widgets/feed/" rel="self" type="application/rss+xml" />
	<link>https://appmakers.dev/category/swiftui/swiftui-widgets/</link>
	<description>SwiftUI Tutorials, iOS App Development, SwiftUI, Swift</description>
	<lastBuildDate>Fri, 13 Jun 2025 17:56:33 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://appmakers.dev/wp-content/uploads/2024/10/cropped-AppMakersDev-32x32.jpg</url>
	<title>SwiftUI Widgets - AppMakers.Dev</title>
	<link>https://appmakers.dev/category/swiftui/swiftui-widgets/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>How to Build a SwiftUI Widget with App Intents and SwiftData ( Configurable Widget )</title>
		<link>https://appmakers.dev/how-to-build-a-swiftui-widget-with-app-intents-and-swiftdata-configurable-widget/</link>
		
		<dc:creator><![CDATA[AppMakers]]></dc:creator>
		<pubDate>Fri, 13 Jun 2025 17:56:33 +0000</pubDate>
				<category><![CDATA[iOS Development]]></category>
		<category><![CDATA[Source Code]]></category>
		<category><![CDATA[Swift]]></category>
		<category><![CDATA[SwiftUI]]></category>
		<category><![CDATA[SwiftUI Widgets]]></category>
		<category><![CDATA[WidgetKit]]></category>
		<category><![CDATA[Widgets]]></category>
		<guid isPermaLink="false">https://appmakers.dev/?p=1994</guid>

					<description><![CDATA[<p>In this comprehensive tutorial, we’ll guide you through the process of constructing a fully functional Configurable Widget utilizing the latest SwiftUI technologies. This widget will empower users to select content from your app, such as a motivational quote, and display it conveniently on their Home Screen. Additionally, users will have the option to select a&#8230;</p>
<p>The post <a href="https://appmakers.dev/how-to-build-a-swiftui-widget-with-app-intents-and-swiftdata-configurable-widget/">How to Build a SwiftUI Widget with App Intents and SwiftData ( Configurable Widget )</a> appeared first on <a href="https://appmakers.dev">AppMakers.Dev</a>.</p>
]]></description>
										<content:encoded><![CDATA[<div class="gp go hi hj hk">
<div class="ac cb">
<div class="ci bh gv gw gx gy">
<p id="6f98" class="pw-post-body-paragraph ni nj hn nk b il nl nm nn io no np nq fi nr ns nt fl nu nv nw fo nx ny nz oa gp bk" data-selectable-paragraph="">In this comprehensive tutorial, we’ll guide you through the process of constructing a fully functional Configurable Widget utilizing the latest SwiftUI technologies. This widget will empower users to select content from your app, such as a motivational quote, and display it conveniently on their Home Screen. Additionally, users will have the option to select a background color and toggle the Favorite state for the widget.</p>
<h2 id="ecdb" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph=""><a class="ag gn" href="https://appmakers.gumroad.com/l/swiftui-widget-swiftdata-appintents-configurable" target="_blank" rel="noopener ugc nofollow">📦 Download the Code for this Tutorial 📥</a></h2>
<h1 id="162b" class="pc og hn bf oh pd pe in fe pf pg iq fh ph pi pj pk pl pm pn po pp pq pr ps pt bk" data-selectable-paragraph="">🛠 Step 1: Project Setup</h1>
<h1 id="e291" class="pc og hn bf oh pd pe in fe pf pg iq fh ph pi pj pk pl pm pn po pp pq pr ps pt bk" data-selectable-paragraph="">1. Create the Base App</h1>
<ul class="">
<li id="cbf1" class="ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa pz qa qb bk" data-selectable-paragraph="">Open Xcode, navigate to File, select New, and choose Project. Set the name to ConfigurableWidgetsApp. For the Interface, select SwiftUI, and for the Language, select Swift.</li>
</ul>
<h1 id="087d" class="pc og hn bf oh pd pe in fe pf pg iq fh ph pi pj pk pl pm pn po pp pq pr ps pt bk" data-selectable-paragraph="">2. Add a Widget Extension</h1>
<ul class="">
<li id="8b12" class="ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa pz qa qb bk" data-selectable-paragraph="">Go to File → New → Target → Select Widget Extension. Uncheck Live Activity and Control, and check Include Configuration App Intent. Name it ConfigurableWidget. If prompted, activate the ConfigurableWidgetExtension scheme.</li>
</ul>
<h1 id="8898" class="pc og hn bf oh pd pe in fe pf pg iq fh ph pi pj pk pl pm pn po pp pq pr ps pt bk" data-selectable-paragraph="">3. Enable App Groups</h1>
<p id="6452" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">We utilize App Groups to facilitate data sharing between your app and widget. To proceed, navigate to your app target, select Signing &amp; Capabilities, and then click on Add App Groups. Next, create a new App Group with the name group.com.yourname.ConfigurableWidgets. Repeat the same process for your widget target. It’s crucial to ensure that both appGroups share the same group container name to enable data sharing between your app and widget.</p>
<h1 id="7ac4" class="pc og hn bf oh pd pe in fe pf pg iq fh ph pi pj pk pl pm pn po pp pq pr ps pt bk" data-selectable-paragraph="">🧱 Step 2: Create the SwiftData Model</h1>
<p id="fb6c" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">Inside your app, create a file <code class="" data-line="">Motivation.swift.</code></p>
<p id="71ce" class="pw-post-body-paragraph ni nj hn nk b il nl nm nn io no np nq fi nr ns nt fl nu nv nw fo nx ny nz oa gp bk" data-selectable-paragraph="">This SwiftData model represents a single motivational message.</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="5d27" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-type">Model</span>
<span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title.class">Motivation</span>: <span class="hljs-title.class">Identifiable</span> {
    
    <span class="hljs-keyword">var</span> id: <span class="hljs-type">UUID</span>?
    <span class="hljs-keyword">var</span> message: <span class="hljs-type">String</span>?
    <span class="hljs-keyword">var</span> timestamp: <span class="hljs-type">Date</span>?

    <span class="hljs-keyword">init</span>(<span class="hljs-params">message</span>: <span class="hljs-type">String</span>?, <span class="hljs-params">timestamp</span>: <span class="hljs-type">Date</span> <span class="hljs-operator">=</span> .now) {
        <span class="hljs-keyword">self</span>.id <span class="hljs-operator">=</span> <span class="hljs-type">UUID</span>()
        <span class="hljs-keyword">self</span>.message <span class="hljs-operator">=</span> message
        <span class="hljs-keyword">self</span>.timestamp <span class="hljs-operator">=</span> timestamp
    }
}</span></pre>
<p id="1fc3" class="pw-post-body-paragraph ni nj hn nk b il nl nm nn io no np nq fi nr ns nt fl nu nv nw fo nx ny nz oa gp bk" data-selectable-paragraph="">To ensure that your file is accessible within your WidgetExtension, you must attach the “Motivation.swift” file to the Target. This process applies to any file you intend to use within your extension.</p>
<h2 id="467c" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">Register the model in your app</h2>
<p id="e07d" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">Open <code class="" data-line="">ConfigurableWidgetsApp.swift</code> and configure the model container with <code class="" data-line="">.modelContainer(for: [Motivation.self])</code> Don&#8217;t forget to add <code class="" data-line="">import SwiftData</code></p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="3b1f" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-keyword">import</span> SwiftData

<span class="hljs-keyword">@main</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title.class">ConfigurableWidgetsApp</span>: <span class="hljs-title.class">App</span> {
    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">Scene</span> {
        <span class="hljs-type">WindowGroup</span> {
            <span class="hljs-type">ContentView</span>()
        }
        .modelContainer(for: [<span class="hljs-type">Motivation</span>.<span class="hljs-keyword">self</span>])
    }
}</span></pre>
<p id="be44" class="pw-post-body-paragraph ni nj hn nk b il nl nm nn io no np nq fi nr ns nt fl nu nv nw fo nx ny nz oa gp bk" data-selectable-paragraph="">This sets up your SwiftData model and prepares it for storage and syncing.</p>
<h1 id="7daf" class="pc og hn bf oh pd pe in fe pf pg iq fh ph pi pj pk pl pm pn po pp pq pr ps pt bk" data-selectable-paragraph="">📲 Step 3: Build the Motivation List UI</h1>
<p id="62e9" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">Now, let’s construct the user interface that enables you to manage your list of motivations within the app.</p>
<p id="62e9" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">We’ll create or update ContentView.swift to implement these features.</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="220f" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-keyword">import</span> SwiftUI
<span class="hljs-keyword">import</span> SwiftData

<span class="hljs-keyword">struct</span> <span class="hljs-title.class">ContentView</span>: <span class="hljs-title.class">View</span> {
    
    <span class="hljs-meta">@Environment</span>(\.modelContext) <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> modelContext
    <span class="hljs-meta">@Query</span>(sort: \<span class="hljs-type">Motivation</span>.timestamp, order: .reverse) <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> motivations: [<span class="hljs-type">Motivation</span>]
    
    <span class="hljs-meta">@State</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> showAllUsedAlert <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>
    
    <span class="hljs-keyword">let</span> motivationSamples <span class="hljs-operator">=</span> [
        <span class="hljs-string">"Keep going!"</span>,
        <span class="hljs-string">"You got this!"</span>,
        <span class="hljs-string">"Make it happen!"</span>,
        <span class="hljs-string">"Dream big, act bigger."</span>,
        <span class="hljs-string">"Stay hungry, stay foolish."</span>,
        <span class="hljs-string">"Progress, not perfection."</span>,
        <span class="hljs-string">"Push beyond limits."</span>,
        <span class="hljs-string">"Your only limit is you."</span>,
        <span class="hljs-string">"Success is a habit."</span>,
        <span class="hljs-string">"Discipline &gt; Motivation."</span>,
        <span class="hljs-string">"Every step counts."</span>,
        <span class="hljs-string">"Don’t stop now."</span>,
        <span class="hljs-string">"You were made for more."</span>,
        <span class="hljs-string">"Do it scared."</span>,
        <span class="hljs-string">"Tiny wins every day."</span>,
        <span class="hljs-string">"No zero days."</span>,
        <span class="hljs-string">"Trust the process."</span>,
        <span class="hljs-string">"The future is yours to shape."</span>,
        <span class="hljs-string">"Create your own momentum."</span>,
        <span class="hljs-string">"Consistency is your superpower."</span>
    ]

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
            <span class="hljs-type">NavigationStack</span> {
                <span class="hljs-type">List</span> {
                    <span class="hljs-type">ForEach</span>(motivations) { motivation <span class="hljs-keyword">in</span>
                        <span class="hljs-type">VStack</span>(alignment: .leading) {
                            <span class="hljs-type">Text</span>(motivation.message <span class="hljs-operator">??</span> <span class="hljs-string">"No message"</span>)
                                .font(.headline)
                            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> timestamp <span class="hljs-operator">=</span> motivation.timestamp {
                                <span class="hljs-type">Text</span>(timestamp.formatted(date: .abbreviated, time: .shortened))
                                    .font(.caption)
                                    .foregroundStyle(.gray)
                            }
                        }
                        .padding(.vertical, <span class="hljs-number">4</span>)
                    }
                    .onDelete(perform: deleteItems)
                }
                .navigationTitle(<span class="hljs-string">"Your Motivations"</span>)
                .toolbar {
                    <span class="hljs-type">ToolbarItem</span>(placement: .bottomBar) {
                        <span class="hljs-type">Button</span>(action: addItem) {
                            <span class="hljs-type">Label</span>(<span class="hljs-string">"Add Motivation"</span>, systemImage: <span class="hljs-string">"plus.circle.fill"</span>)
                                .font(.headline)
                        }
                    }
                }
                .alert(<span class="hljs-string">"All Motivations Added"</span>, isPresented: <span class="hljs-variable">$showAllUsedAlert</span>) {
                    <span class="hljs-type">Button</span>(<span class="hljs-string">"OK"</span>, role: .cancel) {}
                } message: {
                    <span class="hljs-type">Text</span>(<span class="hljs-string">"You've already added all available motivation samples."</span>)
                }
            }
        }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title.function">addItem</span>() {
        
        <span class="hljs-comment">// Check which messages are not yet in the list</span>
        <span class="hljs-keyword">let</span> existingMessages <span class="hljs-operator">=</span> <span class="hljs-type">Set</span>(motivations.compactMap { <span class="hljs-variable">$0</span>.message })
        <span class="hljs-keyword">let</span> availableMessages <span class="hljs-operator">=</span> motivationSamples.filter { <span class="hljs-operator">!</span>existingMessages.contains(<span class="hljs-variable">$0</span>) }

        <span class="hljs-comment">// If no new messages left, show alert</span>
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> newMessage <span class="hljs-operator">=</span> availableMessages.randomElement() <span class="hljs-keyword">else</span> {
            showAllUsedAlert <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>
            <span class="hljs-keyword">return</span>
        }

        <span class="hljs-keyword">let</span> newMotivation <span class="hljs-operator">=</span> <span class="hljs-type">Motivation</span>(message: newMessage)
        modelContext.insert(newMotivation)
        <span class="hljs-keyword">try?</span> modelContext.save()
        
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title.function">deleteItems</span>(<span class="hljs-params">at</span> <span class="hljs-params">offsets</span>: <span class="hljs-type">IndexSet</span>) {
        
        <span class="hljs-keyword">for</span> index <span class="hljs-keyword">in</span> offsets {
            <span class="hljs-keyword">let</span> item <span class="hljs-operator">=</span> motivations[index]
            modelContext.delete(item)
            <span class="hljs-keyword">try?</span> modelContext.save()
        }
        
    }
    
}</span></pre>
<h1 id="b448" class="pc og hn bf oh pd pe in fe pf pg iq fh ph pi pj pk pl pm pn po pp pq pr ps pt bk" data-selectable-paragraph="">🧠 Step 4: Let Users Choose a Motivation in the Widget Configuration Panel</h1>
<p>Widgets powered by AppIntentConfiguration can display dynamic content that users can choose from, such as motivational quotes from your SwiftData model. To achieve this, we need to ensure that our SwiftData model (Motivation) is compatible with the widget configuration UI.</p>
<p>AppEntity and EntityQuery play a crucial role in this process.</p>
<h2 id="e110" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">4.1. Define an AppEntity Wrapper: <code class="" data-line="">MotivationEntity</code></h2>
<p id="0636" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">Inside <code class="" data-line="">AppIntent.swift</code>, add this struct:</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="354f" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-keyword">struct</span> <span class="hljs-title.class">MotivationEntity</span>: <span class="hljs-title.class">AppEntity</span> {
    <span class="hljs-keyword">var</span> id: <span class="hljs-type">String</span>
    <span class="hljs-keyword">var</span> message: <span class="hljs-type">String</span>

    <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> typeDisplayRepresentation: <span class="hljs-type">TypeDisplayRepresentation</span> <span class="hljs-operator">=</span> <span class="hljs-string">"Motivation"</span>
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> defaultQuery <span class="hljs-operator">=</span> <span class="hljs-type">MotivationQuery</span>()

    <span class="hljs-keyword">var</span> displayRepresentation: <span class="hljs-type">DisplayRepresentation</span> {
        <span class="hljs-type">DisplayRepresentation</span>(
            title: <span class="hljs-type">LocalizedStringResource</span>(<span class="hljs-string">"<span class="hljs-subst">\(message)</span>"</span>),
            subtitle: <span class="hljs-type">LocalizedStringResource</span>(<span class="hljs-string">"<span class="hljs-subst">\(id)</span>"</span>)
        )
    }
}</span><code class="" data-line=""></code></pre>
<h2 id="a101" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">4.2. Add a Query to Load Motivations from SwiftData</h2>
<p id="e68b" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">Still in <code class="" data-line="">AppIntent.swift</code>, define the following below <code class="" data-line="">MotivationEntity</code>:</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="c03b" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-keyword">struct</span> <span class="hljs-title.class">MotivationQuery</span>: <span class="hljs-title.class">EntityQuery</span> {
    
    <span class="hljs-keyword">func</span> <span class="hljs-title.function">suggestedEntities</span>() <span class="hljs-keyword">async</span> <span class="hljs-keyword">throws</span> -&gt; [<span class="hljs-type">MotivationEntity</span>] {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> <span class="hljs-keyword">await</span> fetchMotivations()
    }

    <span class="hljs-keyword">func</span> <span class="hljs-title.function">defaultResult</span>() <span class="hljs-keyword">async</span> -&gt; <span class="hljs-type">MotivationEntity</span>? {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">try?</span> <span class="hljs-keyword">await</span> fetchMotivations().first
    }

    <span class="hljs-keyword">func</span> <span class="hljs-title.function">entities</span>(<span class="hljs-params">for</span> <span class="hljs-params">identifiers</span>: [<span class="hljs-type">String</span>]) <span class="hljs-keyword">async</span> <span class="hljs-keyword">throws</span> -&gt; [<span class="hljs-type">MotivationEntity</span>] {
        <span class="hljs-keyword">let</span> motivations <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-keyword">await</span> fetchMotivations()
        <span class="hljs-keyword">return</span> motivations.filter { identifiers.contains(<span class="hljs-variable">$0</span>.id) }
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title.function">fetchMotivations</span>() <span class="hljs-keyword">async</span> <span class="hljs-keyword">throws</span> -&gt; [<span class="hljs-type">MotivationEntity</span>] {
        <span class="hljs-keyword">let</span> container <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">ModelContainer</span>(for: <span class="hljs-type">Motivation</span>.<span class="hljs-keyword">self</span>)
        <span class="hljs-keyword">let</span> context <span class="hljs-operator">=</span> <span class="hljs-type">ModelContext</span>(container)
        <span class="hljs-keyword">let</span> results <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> context.fetch(<span class="hljs-type">FetchDescriptor</span>&lt;<span class="hljs-type">Motivation</span>&gt;())

        <span class="hljs-keyword">return</span> results.compactMap {
            <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> id <span class="hljs-operator">=</span> <span class="hljs-variable">$0</span>.id<span class="hljs-operator">?</span>.uuidString, <span class="hljs-keyword">let</span> message <span class="hljs-operator">=</span> <span class="hljs-variable">$0</span>.message <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span> }
            <span class="hljs-keyword">return</span> <span class="hljs-type">MotivationEntity</span>(id: id, message: message)
        }
    }
}</span><code class="" data-line=""></code></pre>
<h2 id="d791" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">4.3. Update the Intent Itself</h2>
<p id="0721" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">Finally, let’s replace the default emoji parameter with our motivation selection.</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="4a0b" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph="">
<span class="hljs-keyword">struct</span> <span class="hljs-title.class">ConfigurationAppIntent</span>: <span class="hljs-title.class">WidgetConfigurationIntent</span> {
    
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> title: <span class="hljs-type">LocalizedStringResource</span> <span class="hljs-operator">=</span> <span class="hljs-string">"Choose Motivation"</span>
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> description <span class="hljs-operator">=</span> <span class="hljs-type">IntentDescription</span>(<span class="hljs-string">"Pick a motivation message to show in your widget."</span>)

    <span class="hljs-meta">@Parameter</span>(title: <span class="hljs-string">"Motivation"</span>)
    <span class="hljs-keyword">var</span> motivation: <span class="hljs-type">MotivationEntity</span>?
    
}</span></pre>
<p id="ffd0" class="pw-post-body-paragraph ni nj hn nk b il nl nm nn io no np nq fi nr ns nt fl nu nv nw fo nx ny nz oa gp bk" data-selectable-paragraph="">Now, when users long-press your widget and tap “Edit,” they’ll see a dropdown list of motivational quotes directly pulled from your app’s database. However, to test this feature, we need to make some adjustments in our Configurable Widget to align with our new Intent.</p>
<h1 id="63f5" class="pc og hn bf oh pd pe in fe pf pg iq fh ph pi pj pk pl pm pn po pp pq pr ps pt bk" data-selectable-paragraph="">Step 5: Update the Widget Provider to Display Selected Motivation</h1>
<p>Users can now select a motivational message in the widget’s configuration screen using ConfigurationAppIntent. To display their selection, we need to update the actual widget logic in ConfigurableWidget.swift.</p>
<h2 id="12cb" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">Step 5.1: Update <code class="" data-line="">TimelineEntry</code></h2>
<p id="f017" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph=""><strong class="nk ho">SimpleEntry: TimelineEntry</strong> is a struct — the data model for a widget at a specific point in time.</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="4e72" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-keyword">struct</span> <span class="hljs-title.class">SimpleEntry</span>: <span class="hljs-title.class">TimelineEntry</span> {
    <span class="hljs-keyword">let</span> date: <span class="hljs-type">Date</span>
    <span class="hljs-keyword">let</span> configuration: <span class="hljs-type">ConfigurationAppIntent</span>
}</span></pre>
<h2 id="59eb" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">Step 5.2: Update AppIntentTimelineProvider</h2>
<p>This is your AppIntentTimelineProvider, which is responsible for creating placeholder, snapshot, and timeline entries. It serves as the central component of your widget, determining what content to display and when to display it to WidgetKit.</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="618a" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-keyword">struct</span> <span class="hljs-title.class">Provider</span>: <span class="hljs-title.class">AppIntentTimelineProvider</span> {

<span class="hljs-operator">...</span> }</span></pre>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="959b" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-keyword">func</span> <span class="hljs-title.function">placeholder</span>(<span class="hljs-params">in</span> <span class="hljs-params">context</span>: <span class="hljs-type">Context</span>) -&gt; <span class="hljs-type">SimpleEntry</span> {
    <span class="hljs-type">SimpleEntry</span>(date: .now, configuration: <span class="hljs-type">ConfigurationAppIntent</span>())
}</span></pre>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="099c" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-keyword">func</span> <span class="hljs-title.function">snapshot</span>(<span class="hljs-params">for</span> <span class="hljs-params">configuration</span>: <span class="hljs-type">ConfigurationAppIntent</span>, <span class="hljs-params">in</span> <span class="hljs-params">context</span>: <span class="hljs-type">Context</span>) <span class="hljs-keyword">async</span> -&gt; <span class="hljs-type">SimpleEntry</span> {
    <span class="hljs-type">SimpleEntry</span>(date: .now, configuration: configuration)
}</span></pre>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="bc5e" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-keyword">func</span> <span class="hljs-title.function">timeline</span>(<span class="hljs-params">for</span> <span class="hljs-params">configuration</span>: <span class="hljs-type">ConfigurationAppIntent</span>, <span class="hljs-params">in</span> <span class="hljs-params">context</span>: <span class="hljs-type">Context</span>) <span class="hljs-keyword">async</span> -&gt; <span class="hljs-type">Timeline</span>&lt;<span class="hljs-type">SimpleEntry</span>&gt; {
    <span class="hljs-keyword">let</span> entry <span class="hljs-operator">=</span> <span class="hljs-type">SimpleEntry</span>(date: .now, configuration: configuration)
    <span class="hljs-keyword">return</span> <span class="hljs-type">Timeline</span>(entries: [entry], policy: .never)
}</span></pre>
<h1 id="58f8" class="pc og hn bf oh pd pe in fe pf pg iq fh ph pi pj pk pl pm pn po pp pq pr ps pt bk" data-selectable-paragraph="">🖼 Step 6: Design the Widget View</h1>
<p>Now that your timeline provider is ready, let’s enhance the widget’s visual appeal. Our objective is to present the user-selected motivational quote in a clean and visually appealing layout.</p>
<h2 id="61d1" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">6.1 Update <code class="" data-line="">ConfigurableWidgetEntryView</code></h2>
<p id="aa4a" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">We’ll update the widget view to display:</p>
<ul class="">
<li id="ed42" class="ni nj hn nk b il nl nm nn io no np nq fi nr ns nt fl nu nv nw fo nx ny nz oa pz qa qb bk" data-selectable-paragraph="">A motivational message (from <code class="" data-line="">entry.configuration.motivation.message</code>)</li>
<li id="18cb" class="ni nj hn nk b il qc nm nn io qd np nq fi qe ns nt fl qf nv nw fo qg ny nz oa pz qa qb bk" data-selectable-paragraph="">A timestamp for when the widget was last refreshed (optional but helpful for debugging or dynamic updates later)</li>
</ul>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="4053" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-comment">/// The actual content shown in the widget on the Home Screen</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title.class">ConfigurableWidgetEntryView</span>: <span class="hljs-title.class">View</span> {
   
    <span class="hljs-keyword">var</span> entry: <span class="hljs-type">Provider</span>.<span class="hljs-type">Entry</span>

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
        <span class="hljs-type">ZStack</span> {

            <span class="hljs-type">VStack</span>(alignment: .leading, spacing: <span class="hljs-number">8</span>) {
                <span class="hljs-comment">// Main motivation text</span>
                <span class="hljs-type">Text</span>(entry.configuration.motivation<span class="hljs-operator">?</span>.message <span class="hljs-operator">??</span> <span class="hljs-string">"Test"</span>)
                    .font(.headline)
                    .lineLimit(<span class="hljs-number">3</span>)
                    .minimumScaleFactor(<span class="hljs-number">0.5</span>)
                    .bold()
                    .multilineTextAlignment(.leading)

                <span class="hljs-type">Spacer</span>()

                <span class="hljs-comment">// Optional timestamp (can remove in production)</span>
                <span class="hljs-type">Text</span>(entry.date, style: .time)
                    .font(.caption2)
                    .foregroundStyle(.gray)
            }
            .padding()
        }
    }
    
}</span><code class="" data-line=""></code></pre>
<h2 id="1374" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">6.2 <code class="" data-line="">ConfigurableWidget</code> Definition — Widget</h2>
<p id="213a" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">ConfigurableWidget struct registers your widget with the system, connecting your Provider, ConfigurableWidgetEntryView, and ConfigurationAppIntent.</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="fa5c" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-keyword">struct</span> <span class="hljs-title.class">ConfigurableWidget</span>: <span class="hljs-title.class">Widget</span> {
    
    <span class="hljs-keyword">let</span> kind: <span class="hljs-type">String</span> <span class="hljs-operator">=</span> <span class="hljs-string">"ConfigurableWidget"</span>

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">WidgetConfiguration</span> {
        <span class="hljs-type">AppIntentConfiguration</span>(
            kind: kind,
            intent: <span class="hljs-type">ConfigurationAppIntent</span>.<span class="hljs-keyword">self</span>,
            provider: <span class="hljs-type">Provider</span>()
        ) { entry <span class="hljs-keyword">in</span>
            <span class="hljs-type">ConfigurableWidgetEntryView</span>(entry: entry)
                .containerBackground(<span class="hljs-type">Color</span>(.systemBackground), for: .widget)
        }
    }
    
}</span></pre>
<h2 id="5dba" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">6.3 Widget Preview With Motivation Entities</h2>
<p id="7bb9" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">Update the widget preview block to use the sample motivations:</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="2347" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph="">#<span class="hljs-type">Preview</span>(as: .systemSmall) {
    <span class="hljs-type">ConfigurableWidget</span>()
} timeline: {
    <span class="hljs-type">SimpleEntry</span>(date: .now, configuration: .sampleMotivation1)
    <span class="hljs-type">SimpleEntry</span>(date: .now, configuration: .sampleMotivation2)
}</span></pre>
<pre class="gm qi oe qj bp qk bb bk"><span id="0e38" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-keyword">extension</span> <span class="hljs-title.class">ConfigurationAppIntent</span> {
    
    <span class="hljs-comment">/// Sample configuration using a fake MotivationEntity</span>
    <span class="hljs-keyword">fileprivate</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> sampleMotivation1: <span class="hljs-type">ConfigurationAppIntent</span> {
        <span class="hljs-keyword">let</span> intent <span class="hljs-operator">=</span> <span class="hljs-type">ConfigurationAppIntent</span>()
        intent.motivation <span class="hljs-operator">=</span> <span class="hljs-type">MotivationEntity</span>(
            id: <span class="hljs-type">UUID</span>().uuidString,
            message: <span class="hljs-string">"Push beyond limits."</span>
        )
        <span class="hljs-keyword">return</span> intent
    }

    <span class="hljs-keyword">fileprivate</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> sampleMotivation2: <span class="hljs-type">ConfigurationAppIntent</span> {
        <span class="hljs-keyword">let</span> intent <span class="hljs-operator">=</span> <span class="hljs-type">ConfigurationAppIntent</span>()
        intent.motivation <span class="hljs-operator">=</span> <span class="hljs-type">MotivationEntity</span>(
            id: <span class="hljs-type">UUID</span>().uuidString,
            message: <span class="hljs-string">"The future is yours to shape."</span>
        )
        <span class="hljs-keyword">return</span> intent
    }
}</span></pre>
<h1 id="4fb6" class="pc og hn bf oh pd pe in fe pf pg iq fh ph pi pj pk pl pm pn po pp pq pr ps pt bk" data-selectable-paragraph="">Step 7: Test the Widget in Simulator (and Improve Selection UI)</h1>
<p id="da85" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">Before we refine the widget configuration UI, test your widget setup in the Simulator to ensure everything functions correctly.</p>
<h1 id="74ec" class="pc og hn bf oh pd pe in fe pf pg iq fh ph pi pj pk pl pm pn po pp pq pr ps pt bk" data-selectable-paragraph="">🔍 How to Test in Simulator</h1>
<ol class="">
<li id="a8cd" class="ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa qv qa qb bk" data-selectable-paragraph=""><strong class="nk ho">Run your app first</strong> to populate SwiftData with some motivations.</li>
<li id="9096" class="ni nj hn nk b il qc nm nn io qd np nq fi qe ns nt fl qf nv nw fo qg ny nz oa qv qa qb bk" data-selectable-paragraph=""><strong class="nk ho">Long-press the Home Screen</strong> in the Simulator.</li>
<li id="b798" class="ni nj hn nk b il qc nm nn io qd np nq fi qe ns nt fl qf nv nw fo qg ny nz oa qv qa qb bk" data-selectable-paragraph="">Tap <strong class="nk ho">“+” (Add Widget)</strong> → choose your widget → <strong class="nk ho">Add to Home Screen</strong>.</li>
<li id="565f" class="ni nj hn nk b il qc nm nn io qd np nq fi qe ns nt fl qf nv nw fo qg ny nz oa qv qa qb bk" data-selectable-paragraph=""><strong class="nk ho">Tap and hold the widget</strong>, choose <strong class="nk ho">Edit</strong>, and you’ll see the motivational message dropdown.</li>
</ol>
<figure class="or os ot ou ov ow oo op paragraph-image"></figure>
<p>Right now, the widget configuration uses the default dropdown UI, which works well, but it becomes cumbersome and difficult to search when you have numerous motivation entries.</p>
<p>To address this issue, we aim to enhance the configuration panel by implementing the following changes:</p>
<p>&#8211; Displaying motivations in a popup list.</p>
<p>&#8211; Enabling a search bar.</p>
<h1 id="08e9" class="pc og hn bf oh pd pe in fe pf pg iq fh ph pi pj pk pl pm pn po pp pq pr ps pt bk" data-selectable-paragraph="">Step 8: Enable Searchable Picker for MotivationEntity</h1>
<p id="0a8d" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">To enable a searchable experience within the widget configuration, we must update the MotivationQuery to support live filtering.</p>
<p id="0a8d" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">Firstly, ensure that your query struct adheres to the EntityStringQuery protocol, not just the EntityQuery protocol. Modify the declaration accordingly:</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="5030" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-keyword">struct</span> <span class="hljs-title.class">MotivationQuery</span>: <span class="hljs-title.class">EntityStringQuery</span> {</span></pre>
<p id="2f64" class="pw-post-body-paragraph ni nj hn nk b il nl nm nn io no np nq fi nr ns nt fl nu nv nw fo nx ny nz oa gp bk" data-selectable-paragraph="">Then, implement the required <code class="" data-line="">entities(matching:)</code> method to support real-time search:</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="49a7" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph="">    <span class="hljs-keyword">func</span> <span class="hljs-title.function">entities</span>(<span class="hljs-params">matching</span> <span class="hljs-params">string</span>: <span class="hljs-type">String</span>) <span class="hljs-keyword">async</span> <span class="hljs-keyword">throws</span> -&gt; [<span class="hljs-type">MotivationEntity</span>] {
          <span class="hljs-keyword">let</span> motivations <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-keyword">await</span> fetchMotivations()
          <span class="hljs-keyword">return</span> motivations.filter {
              <span class="hljs-variable">$0</span>.message.localizedCaseInsensitiveContains(string)
          }
      }</span></pre>
<p id="ae68" class="pw-post-body-paragraph ni nj hn nk b il nl nm nn io no np nq fi nr ns nt fl nu nv nw fo nx ny nz oa gp bk" data-selectable-paragraph="">Rebuild the app, run it in the simulator or on a physical device, and edit your widget. As you type in the Motivation field, the results should now appear in real time.</p>
<h1 id="19fe" class="pc og hn bf oh pd pe in fe pf pg iq fh ph pi pj pk pl pm pn po pp pq pr ps pt bk" data-selectable-paragraph="">🎨 Step 9: Add Custom Background Color and Favorite Toggle</h1>
<p id="92b5" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">To make your widget more personalized, let’s give users control over the following:</p>
<p id="92b5" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">&#8211; Custom background color (stored in SwiftData)</p>
<p id="92b5" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">&#8211; Marking a motivation as a favorite</p>
<h2 id="fbb7" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">9.1: Update the SwiftData Model</h2>
<p id="0b4b" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">Open <code class="" data-line="">Motivation.swift</code> and update the model:</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="8835" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-meta">@Model</span>
<span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title.class">Motivation</span>: <span class="hljs-title.class">Identifiable</span> {
    
    <span class="hljs-keyword">var</span> id: <span class="hljs-type">String</span>?
    <span class="hljs-keyword">var</span> message: <span class="hljs-type">String</span>?
    <span class="hljs-keyword">var</span> timestamp: <span class="hljs-type">Date</span>?
    
    <span class="hljs-keyword">var</span> isFavorite: <span class="hljs-type">Bool</span>?
    <span class="hljs-keyword">var</span> backgroundColorHex: <span class="hljs-type">String</span>?

    <span class="hljs-keyword">init</span>(<span class="hljs-params">message</span>: <span class="hljs-type">String</span>?, <span class="hljs-params">timestamp</span>: <span class="hljs-type">Date</span> <span class="hljs-operator">=</span> .now, <span class="hljs-params">isFavorite</span>: <span class="hljs-type">Bool</span> <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>, <span class="hljs-params">backgroundColorHex</span>: <span class="hljs-type">String</span>? <span class="hljs-operator">=</span> <span class="hljs-literal">nil</span>) {
        
        <span class="hljs-keyword">self</span>.id <span class="hljs-operator">=</span> <span class="hljs-type">UUID</span>().uuidString
        <span class="hljs-keyword">self</span>.message <span class="hljs-operator">=</span> message
        <span class="hljs-keyword">self</span>.timestamp <span class="hljs-operator">=</span> timestamp
        <span class="hljs-keyword">self</span>.isFavorite <span class="hljs-operator">=</span> isFavorite
        <span class="hljs-keyword">self</span>.backgroundColorHex <span class="hljs-operator">=</span> backgroundColorHex
        
    }
    
}</span></pre>
<h2 id="20c6" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">9.2 Update the MotivationEntity: AppEntity Wrapper</h2>
<p id="06ab" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">Since the widget relies on <code class="" data-line="">MotivationEntity</code>, we’ll add these fields to pass to the widget:</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="0a4b" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-keyword">struct</span> <span class="hljs-title.class">MotivationEntity</span>: <span class="hljs-title.class">AppEntity</span> {
    
    <span class="hljs-keyword">var</span> id: <span class="hljs-type">String</span>
    <span class="hljs-keyword">var</span> message: <span class="hljs-type">String</span>
    <span class="hljs-keyword">var</span> isFavorite: <span class="hljs-type">Bool</span>
    <span class="hljs-keyword">var</span> backgroundColorHex: <span class="hljs-type">String</span>?
    
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> typeDisplayRepresentation: <span class="hljs-type">TypeDisplayRepresentation</span> <span class="hljs-operator">=</span> <span class="hljs-string">"Motivation"</span>
   
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> defaultQuery <span class="hljs-operator">=</span> <span class="hljs-type">MotivationQuery</span>()
    
    <span class="hljs-keyword">var</span> displayRepresentation: <span class="hljs-type">DisplayRepresentation</span> {
        <span class="hljs-type">DisplayRepresentation</span>(
            title: <span class="hljs-type">LocalizedStringResource</span>(<span class="hljs-string">"<span class="hljs-subst">\(message)</span>"</span>),  
            subtitle: <span class="hljs-type">LocalizedStringResource</span>(isFavorite <span class="hljs-operator">?</span> <span class="hljs-string">"⭐️ <span class="hljs-subst">\(id)</span>"</span> : <span class="hljs-string">"<span class="hljs-subst">\(id)</span>"</span>)   
        )
    }
}</span></pre>
<h2 id="cd3f" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">9.3: Update fetchMotivations() Logic</h2>
<p id="64b8" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">In <code class="" data-line="">MotivationQuery</code>, update the conversion method to include the new properties:</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="b7f4" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph="">        <span class="hljs-keyword">return</span> results.compactMap {
               <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> id <span class="hljs-operator">=</span> <span class="hljs-variable">$0</span>.id, <span class="hljs-keyword">let</span> message <span class="hljs-operator">=</span> <span class="hljs-variable">$0</span>.message <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span> }
               <span class="hljs-keyword">return</span> <span class="hljs-type">MotivationEntity</span>(
                   id: id,
                   message: message,
                   isFavorite: <span class="hljs-variable">$0</span>.isFavorite <span class="hljs-operator">??</span> <span class="hljs-literal">false</span>,
                   backgroundColorHex: <span class="hljs-variable">$0</span>.backgroundColorHex
               )
           }</span></pre>
<h2 id="bc00" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">9.4 Update Preview Samples for Widget Testing</h2>
<p id="d183" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">To test your widget with styled previews, update your <code class="" data-line="">.sampleMotivation1</code> and <code class="" data-line="">.sampleMotivation2</code> values:</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="b927" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-keyword">extension</span> <span class="hljs-title.class">ConfigurationAppIntent</span> {
    
    <span class="hljs-comment">/// Preview: Configuration with sample motivation 1</span>
    <span class="hljs-keyword">fileprivate</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> sampleMotivation1: <span class="hljs-type">ConfigurationAppIntent</span> {
        <span class="hljs-keyword">let</span> intent <span class="hljs-operator">=</span> <span class="hljs-type">ConfigurationAppIntent</span>()
        intent.motivation <span class="hljs-operator">=</span> <span class="hljs-type">MotivationEntity</span>(
            id: <span class="hljs-type">UUID</span>().uuidString,
            message: <span class="hljs-string">"Push beyond limits."</span>, isFavorite: <span class="hljs-literal">true</span>, backgroundColorHex: <span class="hljs-string">"#D3D3D3"</span>
        )
        <span class="hljs-keyword">return</span> intent
    }

    <span class="hljs-comment">/// Preview: Configuration with sample motivation 2</span>
    <span class="hljs-keyword">fileprivate</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> sampleMotivation2: <span class="hljs-type">ConfigurationAppIntent</span> {
        <span class="hljs-keyword">let</span> intent <span class="hljs-operator">=</span> <span class="hljs-type">ConfigurationAppIntent</span>()
        intent.motivation <span class="hljs-operator">=</span> <span class="hljs-type">MotivationEntity</span>(
            id: <span class="hljs-type">UUID</span>().uuidString,
            message: <span class="hljs-string">"The future is yours to shape."</span>, isFavorite: <span class="hljs-literal">false</span>, backgroundColorHex: <span class="hljs-string">"#FFFFFF"</span>
        )
        <span class="hljs-keyword">return</span> intent
    }
}</span></pre>
<h2 id="26dd" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">9.5 Update the Widget View</h2>
<p id="5cfd" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">Check the <code class="" data-line="">isFavorite</code> flag to display a star</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="33ce" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-keyword">struct</span> <span class="hljs-title.class">ConfigurableWidgetEntryView</span>: <span class="hljs-title.class">View</span> {
   
    <span class="hljs-keyword">var</span> entry: <span class="hljs-type">Provider</span>.<span class="hljs-type">Entry</span>

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
        <span class="hljs-type">ZStack</span> {
            <span class="hljs-type">VStack</span>(alignment: .leading, spacing: <span class="hljs-number">8</span>) {

                <span class="hljs-comment">// Motivation message text (user-selected)</span>
                <span class="hljs-type">Text</span>(entry.configuration.motivation<span class="hljs-operator">?</span>.message <span class="hljs-operator">??</span> <span class="hljs-string">"Stay motivated!"</span>)
                    .font(.headline)
                    .lineLimit(<span class="hljs-number">3</span>)
                    .minimumScaleFactor(<span class="hljs-number">0.5</span>)
                    .bold()
                    .multilineTextAlignment(.leading)

                <span class="hljs-type">Spacer</span>()
                
                <span class="hljs-type">HStack</span> {
                    
                    <span class="hljs-comment">// Timestamp (optional: shows when widget was last updated)</span>
                    <span class="hljs-type">Text</span>(entry.date, style: .time)
                        .font(.caption2)
                        .foregroundStyle(.gray)
                    
                    <span class="hljs-type">Spacer</span>()
                    
                    <span class="hljs-keyword">if</span> entry.configuration.motivation<span class="hljs-operator">?</span>.isFavorite <span class="hljs-operator">==</span> <span class="hljs-literal">true</span> {
                                     <span class="hljs-type">Text</span>(<span class="hljs-string">"⭐️"</span>)
                                         .font(.caption)
                                         .foregroundStyle(.yellow)
                                 }
                    
                }
            }
            .padding()
        }
    }
}</span></pre>
<p id="1ad8" class="pw-post-body-paragraph ni nj hn nk b il nl nm nn io no np nq fi nr ns nt fl nu nv nw fo nx ny nz oa gp bk" data-selectable-paragraph="">Create a new Helpers.swift file and add a Color extension to it, allowing you to create a Color from a String. Remember to check the Widget target for this file to ensure it’s visible within the Widgets Extension.</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="60a7" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-keyword">extension</span> <span class="hljs-title.class">Color</span> {
    <span class="hljs-keyword">init</span>(<span class="hljs-params">hex</span>: <span class="hljs-type">String</span>) {
        <span class="hljs-keyword">let</span> hex <span class="hljs-operator">=</span> hex.trimmingCharacters(in: <span class="hljs-type">CharacterSet</span>.alphanumerics.inverted)
        <span class="hljs-keyword">var</span> int <span class="hljs-operator">=</span> <span class="hljs-type">UInt64</span>()
        <span class="hljs-type">Scanner</span>(string: hex).scanHexInt64(<span class="hljs-operator">&amp;</span>int)
        
        <span class="hljs-keyword">let</span> r, g, b: <span class="hljs-type">UInt64</span>
        <span class="hljs-keyword">switch</span> hex.count {
        <span class="hljs-keyword">case</span> <span class="hljs-number">6</span>: <span class="hljs-comment">// RGB (24-bit)</span>
            (r, g, b) <span class="hljs-operator">=</span> ((int <span class="hljs-operator">&gt;&gt;</span> <span class="hljs-number">16</span>) <span class="hljs-operator">&amp;</span> <span class="hljs-number">0xFF</span>, (int <span class="hljs-operator">&gt;&gt;</span> <span class="hljs-number">8</span>) <span class="hljs-operator">&amp;</span> <span class="hljs-number">0xFF</span>, int <span class="hljs-operator">&amp;</span> <span class="hljs-number">0xFF</span>)
        <span class="hljs-keyword">default</span>:
            (r, g, b) <span class="hljs-operator">=</span> (<span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">255</span>)
        }

        <span class="hljs-keyword">self</span>.<span class="hljs-keyword">init</span>(.sRGB, red: <span class="hljs-type">Double</span>(r) <span class="hljs-operator">/</span> <span class="hljs-number">255</span>, green: <span class="hljs-type">Double</span>(g) <span class="hljs-operator">/</span> <span class="hljs-number">255</span>, blue: <span class="hljs-type">Double</span>(b) <span class="hljs-operator">/</span> <span class="hljs-number">255</span>)
    }
}</span></pre>
<p id="d74e" class="pw-post-body-paragraph ni nj hn nk b il nl nm nn io no np nq fi nr ns nt fl nu nv nw fo nx ny nz oa gp bk" data-selectable-paragraph="">Next, let’s make sure our widget uses the entity background</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="c4d6" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-keyword">struct</span> <span class="hljs-title.class">ConfigurableWidget</span>: <span class="hljs-title.class">Widget</span> {
    
    <span class="hljs-comment">// Unique identifier for this widget</span>
    <span class="hljs-keyword">let</span> kind: <span class="hljs-type">String</span> <span class="hljs-operator">=</span> <span class="hljs-string">"ConfigurableWidget"</span>

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">WidgetConfiguration</span> {
        <span class="hljs-type">AppIntentConfiguration</span>(
            kind: kind,
            intent: <span class="hljs-type">ConfigurationAppIntent</span>.<span class="hljs-keyword">self</span>,  <span class="hljs-comment">// Our custom intent with motivation</span>
            provider: <span class="hljs-type">Provider</span>()                  <span class="hljs-comment">// The logic that supplies timeline entries</span>
        ) { entry <span class="hljs-keyword">in</span>
            <span class="hljs-type">ConfigurableWidgetEntryView</span>(entry: entry)
                .containerBackground(<span class="hljs-type">Color</span>(hex: entry.configuration.motivation<span class="hljs-operator">?</span>.backgroundColorHex <span class="hljs-operator">??</span> <span class="hljs-string">"#D3D3D3"</span>), for: .widget) 
        }
    }
}</span></pre>
<h1 id="ecab" class="pc og hn bf oh pd pe in fe pf pg iq fh ph pi pj pk pl pm pn po pp pq pr ps pt bk" data-selectable-paragraph="">🧭 Step 10: Display Favorites and Background Color in the Main App</h1>
<h2 id="598e" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">🔧 10.1 Update the List Row in <code class="" data-line="">ContentView.swift</code></h2>
<p id="9d8a" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">Replace the <code class="" data-line="">VStack</code> for each motivation in your <code class="" data-line="">ForEach</code> block with this:</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="0bb7" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""> <span class="hljs-type">ZStack</span> {
                            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> hex <span class="hljs-operator">=</span> motivation.backgroundColorHex {
                                <span class="hljs-type">Color</span>(hex: hex)                                    .cornerRadius(<span class="hljs-number">8</span>)
                            }

                                <span class="hljs-type">HStack</span> {
                                    
                                    <span class="hljs-type">VStack</span>(alignment: .leading, spacing: <span class="hljs-number">8</span>) {
                                        
                                        <span class="hljs-type">Text</span>(motivation.message <span class="hljs-operator">??</span> <span class="hljs-string">"No message"</span>)
                                            .font(.headline)
                                            .foregroundStyle(.primary)
                                        
                                        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> timestamp <span class="hljs-operator">=</span> motivation.timestamp {
                                            <span class="hljs-type">Text</span>(timestamp.formatted(date: .abbreviated, time: .shortened))
                                                .font(.caption)
                                                .foregroundStyle(.gray)
                                        }
                                        
                                    }
                                 
                                    <span class="hljs-type">Spacer</span>()
                                    
                                    <span class="hljs-keyword">if</span> motivation.isFavorite <span class="hljs-operator">==</span> <span class="hljs-literal">true</span> {
                                        <span class="hljs-type">Spacer</span>()
                                        <span class="hljs-type">Text</span>(<span class="hljs-string">"⭐️"</span>)
                                            .foregroundStyle(.yellow)
                                            .font(.headline)
                                    }
                                }
                                .background(<span class="hljs-type">Color</span>(hex: motivation.backgroundColorHex <span class="hljs-operator">??</span> <span class="hljs-string">"F1F1F1"</span>))

                               
                            .padding(<span class="hljs-number">20</span>)
                        }
                        .listRowInsets(<span class="hljs-type">EdgeInsets</span>())
                        .listRowBackground(<span class="hljs-type">Color</span>.clear)</span></pre>
<h1 id="89f8" class="pc og hn bf oh pd pe in fe pf pg iq fh ph pi pj pk pl pm pn po pp pq pr ps pt bk" data-selectable-paragraph="">Step 11: Add Background Color Picker to Each Row</h1>
<p id="ea8e" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">We’ll do 3 things:</p>
<ol class="">
<li id="4caa" class="ni nj hn nk b il nl nm nn io no np nq fi nr ns nt fl nu nv nw fo nx ny nz oa qv qa qb bk" data-selectable-paragraph="">Move the full row UI into a reusable <code class="" data-line="">MotivationRowView</code>.</li>
<li id="de2f" class="ni nj hn nk b il qc nm nn io qd np nq fi qe ns nt fl qf nv nw fo qg ny nz oa qv qa qb bk" data-selectable-paragraph="">Show a <code class="" data-line="">ColorPicker</code> inline inside each row.</li>
<li id="55e1" class="ni nj hn nk b il qc nm nn io qd np nq fi qe ns nt fl qf nv nw fo qg ny nz oa qv qa qb bk" data-selectable-paragraph="">Save the picked color as a hex string to SwiftData.</li>
</ol>
<h2 id="b4d6" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">🔧 11.1 Create a Row View</h2>
<p id="910b" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">In a new file (or below <code class="" data-line="">ContentView</code>), create this:</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="98f6" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-keyword">struct</span> <span class="hljs-title.class">MotivationRowView</span>: <span class="hljs-title.class">View</span> {
    <span class="hljs-meta">@Bindable</span> <span class="hljs-keyword">var</span> motivation: <span class="hljs-type">Motivation</span>
    <span class="hljs-meta">@Environment</span>(\.modelContext) <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> modelContext

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
        <span class="hljs-type">VStack</span>(alignment: .leading, spacing: <span class="hljs-number">6</span>) {
            <span class="hljs-type">HStack</span> {
                <span class="hljs-type">Text</span>(motivation.message <span class="hljs-operator">??</span> <span class="hljs-string">"No message"</span>)
                    .font(.headline)
                    .foregroundStyle(.primary)
                    .lineLimit(<span class="hljs-number">2</span>)

                <span class="hljs-keyword">if</span> motivation.isFavorite <span class="hljs-operator">==</span> <span class="hljs-literal">true</span> {
                    <span class="hljs-type">Spacer</span>()
                    <span class="hljs-type">Text</span>(<span class="hljs-string">"⭐️"</span>)
                        .foregroundStyle(.yellow)
                }
            }

            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> timestamp <span class="hljs-operator">=</span> motivation.timestamp {
                <span class="hljs-type">Text</span>(timestamp.formatted(date: .abbreviated, time: .shortened))
                    .font(.caption)
                    .foregroundStyle(.gray)
            }

            <span class="hljs-comment">// Inline Color Picker</span>
            <span class="hljs-type">ColorPicker</span>(motivation.backgroundColorHex <span class="hljs-operator">??</span> <span class="hljs-string">"#FFFFFF"</span>, selection: <span class="hljs-type">Binding</span>(
                get: {
                    <span class="hljs-type">Color</span>(hex: motivation.backgroundColorHex <span class="hljs-operator">??</span> <span class="hljs-string">"#FFFFFF"</span>)
                },
                set: { newColor <span class="hljs-keyword">in</span>
                    motivation.backgroundColorHex <span class="hljs-operator">=</span> newColor.toHex()
                    <span class="hljs-keyword">try?</span> modelContext.save()
                }
            ))
            .font(.caption)
        }
        .padding()
        .background(
            <span class="hljs-type">Color</span>(hex: motivation.backgroundColorHex <span class="hljs-operator">??</span> <span class="hljs-string">"#F2F2F7"</span>)
                .cornerRadius(<span class="hljs-number">10</span>)
        )
    }
}</span></pre>
<h2 id="4081" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">📦 11.2 Update <code class="" data-line="">ContentView.swift</code></h2>
<p id="b2a0" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">Replace your <code class="" data-line="">ForEach</code> block with this:</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="13b7" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-type">ForEach</span>(<span class="hljs-variable">$motivations</span>) { <span class="hljs-variable">$motivation</span> <span class="hljs-keyword">in</span>
     <span class="hljs-type">MotivationRowView</span>(motivation: motivation)
                        .listRowBackground(<span class="hljs-type">Color</span>.clear)
}
.onDelete(perform: deleteItems)</span></pre>
<h2 id="8f3f" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">🧩 11.3 Add <code class="" data-line="">Color.toHex()</code> Extension</h2>
<p id="1be7" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">In <code class="" data-line="">Helpers.swift</code> (shared with widget), add:</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="8f64" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph="">   <span class="hljs-keyword">func</span> <span class="hljs-title.function">toHex</span>() -&gt; <span class="hljs-type">String</span> {
        <span class="hljs-keyword">let</span> uiColor <span class="hljs-operator">=</span> <span class="hljs-type">UIColor</span>(<span class="hljs-keyword">self</span>)
        <span class="hljs-keyword">var</span> r: <span class="hljs-type">CGFloat</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>, g: <span class="hljs-type">CGFloat</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>, b: <span class="hljs-type">CGFloat</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>, a: <span class="hljs-type">CGFloat</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>
        uiColor.getRed(<span class="hljs-operator">&amp;</span>r, green: <span class="hljs-operator">&amp;</span>g, blue: <span class="hljs-operator">&amp;</span>b, alpha: <span class="hljs-operator">&amp;</span>a)
        <span class="hljs-keyword">return</span> <span class="hljs-type">String</span>(format: <span class="hljs-string">"#%02X%02X%02X"</span>, <span class="hljs-type">Int</span>(r <span class="hljs-operator">*</span> <span class="hljs-number">255</span>), <span class="hljs-type">Int</span>(g <span class="hljs-operator">*</span> <span class="hljs-number">255</span>), <span class="hljs-type">Int</span>(b <span class="hljs-operator">*</span> <span class="hljs-number">255</span>))
    }</span></pre>
<h1 id="df75" class="pc og hn bf oh pd pe in fe pf pg iq fh ph pi pj pk pl pm pn po pp pq pr ps pt bk" data-selectable-paragraph="">Step 12: Test it and force Updates</h1>
<p id="111a" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">Delete the app, create 4 new Motivations and set colors to Yellow, Green, Red, Blue inside the app. After that create 4 widgets for these 4 motivations. Works great right.</p>
<figure class="or os ot ou ov ow oo op paragraph-image">
<div class="ox oy dy oz bh pa" tabindex="0" role="button"></div>
</figure>
<h1 id="e8fe" class="pc og hn bf oh pd pe in fe pf pg iq fh ph pi pj pk pl pm pn po pp pq pr ps pt bk" data-selectable-paragraph="">Manually Reload the Widget Timeline</h1>
<ul class="">
<li id="cec6" class="ni nj hn nk b il nl nm nn io no np nq fi nr ns nt fl nu nv nw fo nx ny nz oa pz qa qb bk" data-selectable-paragraph="">Tell WidgetKit to reload the timeline of our widget whenever a Motivation is updated.</li>
<li id="89c2" class="ni nj hn nk b il qc nm nn io qd np nq fi qe ns nt fl qf nv nw fo qg ny nz oa pz qa qb bk" data-selectable-paragraph="">Trigger this in a clean and efficient way inside SwiftUI, <strong class="nk ho">every time a user picks a new color</strong> (or you update any field).</li>
</ul>
<h2 id="d651" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">🛠 Trigger Timeline Reload on Color Change</h2>
<p id="c448" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">Update the <code class="" data-line="">ColorPicker</code> binding’s setter:</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="5378" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-type">ColorPicker</span>(<span class="hljs-string">"Background Color"</span>, selection: <span class="hljs-type">Binding</span>(
    get: {
        <span class="hljs-type">Color</span>(hex: motivation.backgroundColorHex <span class="hljs-operator">??</span> <span class="hljs-string">"#FFFFFF"</span>)
    },
    set: { newColor <span class="hljs-keyword">in</span>
        motivation.backgroundColorHex <span class="hljs-operator">=</span> newColor.toHex()
        <span class="hljs-keyword">try?</span> modelContext.save()

        <span class="hljs-comment">// Reload widget</span>
        <span class="hljs-type">WidgetCenter</span>.shared.reloadAllTimelines()
    }
))
.font(.caption)</span></pre>
<p id="317f" class="pw-post-body-paragraph ni nj hn nk b il nl nm nn io no np nq fi nr ns nt fl nu nv nw fo nx ny nz oa gp bk" data-selectable-paragraph="">Repeat this pattern for any other motivation updates that will appear in the future</p>
<h2 id="d329" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">🔄 Optional: Refresh on App Become Active</h2>
<p id="ef5f" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">You can also call: <code class="" data-line="">WidgetCenter.shared.reloadAllTimelines()</code>inside <code class="" data-line="">onChange(of: scenePhase)</code> in your <code class="" data-line="">App</code> :</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="5373" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph="">.onChange(of: scenePhase) { oldPhase, newPhase <span class="hljs-keyword">in</span>
            
            <span class="hljs-keyword">if</span> newPhase <span class="hljs-operator">==</span> .background { 
                
                <span class="hljs-comment">// Optional</span>
                <span class="hljs-type">WidgetCenter</span>.shared.reloadAllTimelines()
            }
            
        }</span></pre>
<p id="c04d" class="pw-post-body-paragraph ni nj hn nk b il nl nm nn io no np nq fi nr ns nt fl nu nv nw fo nx ny nz oa gp bk" data-selectable-paragraph="">If your app becomes more complex, consider calling: <code class="" data-line="">WidgetCenter.shared.reloadTimelines(ofKind: “ConfigurableWidget”)</code> instead of <code class="" data-line="">.reloadAllTimelines()</code> to avoid refreshing other widgets unnecessarily.</p>
<h1 id="74f5" class="pc og hn bf oh pd pe in fe pf pg iq fh ph pi pj pk pl pm pn po pp pq pr ps pt bk" data-selectable-paragraph="">⭐️ Step 13: Make the “Favorite” Toggle Configurable from the Widget</h1>
<p>Let’s take your configurable widget to the next level by enabling users to toggle the “Favorite” status directly from the widget itself. This feature synchronizes the change with your SwiftData model shared with the main app, providing powerful bidirectional control and enhancing the user experience.</p>
<p>Previously, widget configuration was limited to reading from SwiftData. Now, we’re introducing write-back capabilities. When a user taps the star button in the widget, it will toggle the favorite status and persist the change to SwiftData. Consequently, the app UI and the widget will automatically update with the changes.</p>
<h3>🧱 13.1 Add a Shared SwiftData Container</h3>
<p>To enable the widget extension to write to your SwiftData store, create a singleton container shared via App Group.</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="8e90" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-comment">// MARK: - Shared Model Container</span>
<span class="hljs-comment">/// Singleton container for shared access across app and widget</span>
<span class="hljs-keyword">class</span> <span class="hljs-title.class">SharedModelContainer</span> {
    
   <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> shared <span class="hljs-operator">=</span> <span class="hljs-type">SharedModelContainer</span>()
   
   <span class="hljs-keyword">let</span> container: <span class="hljs-type">ModelContainer</span>
   
   <span class="hljs-keyword">private</span> <span class="hljs-keyword">init</span>() {
       <span class="hljs-comment">// Configure the container for shared access via App Group</span>
       <span class="hljs-keyword">let</span> schema <span class="hljs-operator">=</span> <span class="hljs-type">Schema</span>([<span class="hljs-type">Motivation</span>.<span class="hljs-keyword">self</span>])
       <span class="hljs-keyword">let</span> modelConfiguration <span class="hljs-operator">=</span> <span class="hljs-type">ModelConfiguration</span>(schema: schema, groupContainer: .identifier(<span class="hljs-string">"group.com.adelmaer.ConfigurableWidgetsApp"</span>))
       
       <span class="hljs-keyword">do</span> {
           container <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">ModelContainer</span>(for: schema, configurations: modelConfiguration)
       } <span class="hljs-keyword">catch</span> {
           <span class="hljs-built_in">fatalError</span>(<span class="hljs-string">"Could not create ModelContainer: <span class="hljs-subst">\(error)</span>"</span>)
       }
   }
}</span></pre>
<p id="7979" class="pw-post-body-paragraph ni nj hn nk b il nl nm nn io no np nq fi nr ns nt fl nu nv nw fo nx ny nz oa gp bk" data-selectable-paragraph="">Next, within your main application add <code class="" data-line="">.modelContainer(SharedModelContainer.shared.container)</code></p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="2cf3" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-keyword">import</span> SwiftUI
<span class="hljs-keyword">import</span> SwiftData
<span class="hljs-keyword">import</span> WidgetKit

<span class="hljs-keyword">@main</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title.class">ConfigurableWidgetsAppApp</span>: <span class="hljs-title.class">App</span> {
    
    <span class="hljs-meta">@Environment</span>(\.scenePhase) <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> scenePhase

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">Scene</span> {
        <span class="hljs-type">WindowGroup</span> {
            <span class="hljs-type">ContentView</span>()
        }
        .modelContainer(<span class="hljs-type">SharedModelContainer</span>.shared.container)
        .onChange(of: scenePhase) { <span class="hljs-keyword">_</span>, newPhase <span class="hljs-keyword">in</span>
            <span class="hljs-keyword">if</span> newPhase <span class="hljs-operator">==</span> .background {
                <span class="hljs-type">WidgetCenter</span>.shared.reloadAllTimelines()
            }
        }
    }
}</span></pre>
<p id="fe16" class="pw-post-body-paragraph ni nj hn nk b il nl nm nn io no np nq fi nr ns nt fl nu nv nw fo nx ny nz oa gp bk" data-selectable-paragraph="">This ensures both the app and widget share the same SwiftData storage, keeping everything in sync.</p>
<h2 id="7492" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">🧠 13.2 Create an AppIntent to Toggle Favorite</h2>
<p id="8476" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">We’ll now create a standalone intent that, when the user taps the star icon in the widget, toggles the favorite status. This intent retrieves data from SwiftData using the shared model container, toggles the isFavorite field, and saves the result. AppIntent is a protocol in Swift that defines actions an app can perform, allowing interaction with system features like widgets and shortcuts.</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="a993" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph="">
<span class="hljs-keyword">struct</span> <span class="hljs-title.class">ToggleMotivationIsFavoriteIntent</span>: <span class="hljs-title.class">AppIntent</span> {
    
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> title: <span class="hljs-type">LocalizedStringResource</span> <span class="hljs-operator">=</span> <span class="hljs-string">"Toggle Favorite Status"</span>
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> description <span class="hljs-operator">=</span> <span class="hljs-type">IntentDescription</span>(<span class="hljs-string">"Toggle favorite status for a motivation message"</span>)
    
    <span class="hljs-meta">@Parameter</span>(title: <span class="hljs-string">"Motivation"</span>)
    <span class="hljs-keyword">var</span> motivation: <span class="hljs-type">MotivationEntity</span>
    
    <span class="hljs-keyword">init</span>(<span class="hljs-params">motivation</span>: <span class="hljs-type">MotivationEntity</span>) {
          <span class="hljs-keyword">self</span>.motivation <span class="hljs-operator">=</span> motivation
      }

    <span class="hljs-keyword">init</span>() {}

    <span class="hljs-keyword">func</span> <span class="hljs-title.function">perform</span>() <span class="hljs-keyword">async</span> <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-keyword">some</span> <span class="hljs-type">IntentResult</span> {
       
        <span class="hljs-keyword">let</span> modelContext <span class="hljs-operator">=</span> <span class="hljs-type">ModelContext</span>(<span class="hljs-type">SharedModelContainer</span>.shared.container)
        <span class="hljs-keyword">let</span> id <span class="hljs-operator">=</span> motivation.id
        
        <span class="hljs-comment">// Find and update the motivation</span>
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> motivation <span class="hljs-operator">=</span> <span class="hljs-keyword">try!</span> modelContext.fetch(
            <span class="hljs-type">FetchDescriptor</span>&lt;<span class="hljs-type">Motivation</span>&gt;(
                predicate: #<span class="hljs-type">Predicate</span> { <span class="hljs-variable">$0</span>.id <span class="hljs-operator">==</span> id }
            )).first <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> .result()
        }
        
        <span class="hljs-comment">// Toggle favorite status</span>
        motivation.isFavorite <span class="hljs-operator">=</span> <span class="hljs-operator">!</span>(motivation.isFavorite <span class="hljs-operator">??</span> <span class="hljs-literal">false</span>)
        
        <span class="hljs-comment">// Save changes</span>
        <span class="hljs-keyword">try?</span> modelContext.save()
        
        <span class="hljs-comment">// Refresh widget</span>
        <span class="hljs-type">WidgetCenter</span>.shared.reloadAllTimelines()
        
        <span class="hljs-keyword">return</span> .result()
  
    }

}</span></pre>
<h2 id="158f" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">⭐️ 13.3 Add a Star Toggle Button to the Widget</h2>
<p id="3ccc" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">Now add a custom button view to trigger the intent:</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="137c" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph="">
<span class="hljs-keyword">struct</span> <span class="hljs-title.class">ToggleFavoriteButton</span>: <span class="hljs-title.class">View</span> {
    <span class="hljs-keyword">let</span> motivation: <span class="hljs-type">MotivationEntity</span>
    
    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
        <span class="hljs-type">Button</span>(
            intent: <span class="hljs-type">ToggleMotivationIsFavoriteIntent</span>(motivation: motivation)
        ) {
            <span class="hljs-type">Image</span>(systemName: motivation.isFavorite <span class="hljs-operator">?</span> <span class="hljs-string">"star.fill"</span> : <span class="hljs-string">"star"</span>)
                .foregroundStyle(motivation.isFavorite <span class="hljs-operator">?</span> .yellow : .gray)
                .contentTransition(.symbolEffect(.replace))
        }
        .buttonStyle(.plain)
    }
    
}</span></pre>
<h2 id="55ff" class="of og hn bf oh fc oi fd fe ff oj fg fh fi ok fj fk fl ol fm fn fo om fp fq on bk" data-selectable-paragraph="">🎨 13.4 Integrate ToggleFavoriteButton Into the Widget View</h2>
<p id="f1bf" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">Inside your <code class="" data-line="">ConfigurableWidgetEntryView</code>, update the UI to conditionally show the star toggle if a motivation was selected:</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="9989" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> motivation <span class="hljs-operator">=</span> entry.configuration.motivation {
    <span class="hljs-type">ToggleFavoriteButton</span>(motivation: motivation)
}</span></pre>
<p id="2d61" class="pw-post-body-paragraph ni nj hn nk b il nl nm nn io no np nq fi nr ns nt fl nu nv nw fo nx ny nz oa gp bk" data-selectable-paragraph="">This allows users to mark a message as a favorite directly from the widget by tapping the star — no app opening needed.</p>
<h1 id="da36" class="pc og hn bf oh pd pe in fe pf pg iq fh ph pi pj pk pl pm pn po pp pq pr ps pt bk" data-selectable-paragraph="">⭐️ Step 14: Add a Show/Hide Timestamp Toggle to Widget Configuration</h1>
<p id="2627" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">Do you want to give users complete control over the widget’s appearance? If so, let’s add a new toggle in the configuration screen that allows users to choose whether to display or hide the timestamp.</p>
<h1 id="6a64" class="pc og hn bf oh pd pe in fe pf pg iq fh ph pi pj pk pl pm pn po pp pq pr ps pt bk" data-selectable-paragraph="">🧱 14.1 Update the ConfigurationAppIntent</h1>
<p id="b8e9" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">Open <code class="" data-line="">AppIntent.swift</code> and update your <code class="" data-line="">ConfigurationAppIntent</code>:</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="a634" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-keyword">struct</span> <span class="hljs-title.class">ConfigurationAppIntent</span>: <span class="hljs-title.class">WidgetConfigurationIntent</span> {
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> title: <span class="hljs-type">LocalizedStringResource</span> <span class="hljs-operator">=</span> <span class="hljs-string">"Choose Motivation"</span>
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> description <span class="hljs-operator">=</span> <span class="hljs-type">IntentDescription</span>(<span class="hljs-string">"Pick a motivation message to show in your widget."</span>)

    <span class="hljs-meta">@Parameter</span>(title: <span class="hljs-string">"Motivation"</span>)
    <span class="hljs-keyword">var</span> motivation: <span class="hljs-type">MotivationEntity</span>?

    <span class="hljs-meta">@Parameter</span>(title: <span class="hljs-string">"Show Timestamp"</span>)
    <span class="hljs-keyword">var</span> showTimestamp: <span class="hljs-type">Bool</span>?
}</span></pre>
<p id="90d0" class="pw-post-body-paragraph ni nj hn nk b il nl nm nn io no np nq fi nr ns nt fl nu nv nw fo nx ny nz oa gp bk" data-selectable-paragraph="">By adding <code class="" data-line="">showTimestamp</code>, the user will now see a toggle when editing the widget.</p>
<h1 id="abc4" class="pc og hn bf oh pd pe in fe pf pg iq fh ph pi pj pk pl pm pn po pp pq pr ps pt bk" data-selectable-paragraph="">🧠 14.2 Update the Widget Entry and View</h1>
<p id="45f5" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">Since SimpleEntry now includes the showTimestamp feature, you’re already passing configuration into it. Therefore, you need to update your view to respond accordingly.</p>
<p id="45f5" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">In ConfigurableWidgetEntryView.swift, update this block:</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="4232" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph=""><span class="hljs-keyword">if</span> entry.configuration.showTimestamp <span class="hljs-operator">??</span> <span class="hljs-literal">false</span> {
    <span class="hljs-type">Text</span>(entry.date, style: .time)
        .font(.caption2)
        .foregroundStyle(.gray)
}</span></pre>
<p id="aa0f" class="pw-post-body-paragraph ni nj hn nk b il nl nm nn io no np nq fi nr ns nt fl nu nv nw fo nx ny nz oa gp bk" data-selectable-paragraph="">This conditionally displays the timestamp only when <code class="" data-line="">showTimestamp</code> is enabled.</p>
<h1 id="f536" class="pc og hn bf oh pd pe in fe pf pg iq fh ph pi pj pk pl pm pn po pp pq pr ps pt bk" data-selectable-paragraph="">🧪 14.3 Update Widget Previews</h1>
<p id="00bd" class="pw-post-body-paragraph ni nj hn nk b il pu nm nn io pv np nq fi pw ns nt fl px nv nw fo py ny nz oa gp bk" data-selectable-paragraph="">Update your <code class="" data-line="">sampleMotivation1</code> and <code class="" data-line="">sampleMotivation2</code> preview values to test this:</p>
<pre class="or os ot ou ov qi oe qj bp qk bb bk"><span id="20a8" class="ql og hn oe b bg qm qn m qo qp" data-selectable-paragraph="">    <span class="hljs-comment">/// Preview: Configuration with sample motivation 1</span>
    <span class="hljs-keyword">fileprivate</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> sampleMotivation1: <span class="hljs-type">ConfigurationAppIntent</span> {
        <span class="hljs-keyword">let</span> intent <span class="hljs-operator">=</span> <span class="hljs-type">ConfigurationAppIntent</span>()
        intent.motivation <span class="hljs-operator">=</span> <span class="hljs-type">MotivationEntity</span>(
            id: <span class="hljs-type">UUID</span>().uuidString,
            message: <span class="hljs-string">"Push beyond limits."</span>,
            isFavorite: <span class="hljs-literal">true</span>,
            backgroundColorHex: <span class="hljs-string">"#D3D3D3"</span>
        )
        intent.showTimestamp <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>
        <span class="hljs-keyword">return</span> intent
    }</span></pre>
<p>✅ App Groups are required for shared data widgets (e.g., SwiftData + AppIntent + AppEntity) — like the one we built.</p>
<p>✅ Your iPhone must be running iOS 14 or later (iOS 17+ recommended for best performance and widget interaction).</p>
<p><a href="https://appmakers.gumroad.com/l/swiftui-widget-swiftdata-appintents-configurable">📦 Download the Code 📥</a></p>
<p>Want to support this SwiftUI learning journey or get the full commented source code for this Tutorial? Consider purchasing the complete example.</p>
<p>It includes:</p>
<p>SwiftData + WidgetKit integration</p>
<p>AppIntent + AppEntity patterns</p>
<p>Color + favorite + timestamp configuration</p>
</div>
</div>
</div>
<div class="gp go hi hj hk">
<div class="ac cb">
<div class="ci bh gv gw gx gy">
<p id="ebab" class="pw-post-body-paragraph ni nj hn nk b il nl nm nn io no np nq fi nr ns nt fl nu nv nw fo nx ny nz oa gp bk" data-selectable-paragraph=""><a class="ag gn" href="https://appmakers.gumroad.com/l/swiftui-widget-swiftdata-appintents-configurable" target="_blank" rel="noopener ugc nofollow">📥 <strong class="nk ho">DOWNLOAD SOURCE CODE for this Tutorial</strong></a></p>
</div>
</div>
</div>
<p>The post <a href="https://appmakers.dev/how-to-build-a-swiftui-widget-with-app-intents-and-swiftdata-configurable-widget/">How to Build a SwiftUI Widget with App Intents and SwiftData ( Configurable Widget )</a> appeared first on <a href="https://appmakers.dev">AppMakers.Dev</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Build Your First Widget in SwiftUI — A Guide with Source Code</title>
		<link>https://appmakers.dev/build-your-first-widget-in-swiftui-a-guide-with-source-code/</link>
		
		<dc:creator><![CDATA[AppMakers]]></dc:creator>
		<pubDate>Mon, 09 Jun 2025 21:17:47 +0000</pubDate>
				<category><![CDATA[iOS Development]]></category>
		<category><![CDATA[Source Code]]></category>
		<category><![CDATA[SwiftUI]]></category>
		<category><![CDATA[SwiftUI Tutorials]]></category>
		<category><![CDATA[SwiftUI Widgets]]></category>
		<guid isPermaLink="false">https://appmakers.dev/?p=1985</guid>

					<description><![CDATA[<p>Want to bring widgets to your SwiftUI app but not sure where to begin? This updated tutorial walks you through the entire process of creating a SwiftUI Widget using WidgetKit, complete with step-by-step guidance and working code. By the end, you’ll have a motivational widget that refreshes every 10 minutes with custom colors and messages.&#8230;</p>
<p>The post <a href="https://appmakers.dev/build-your-first-widget-in-swiftui-a-guide-with-source-code/">Build Your First Widget in SwiftUI — A Guide with Source Code</a> appeared first on <a href="https://appmakers.dev">AppMakers.Dev</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Want to bring widgets to your SwiftUI app but not sure where to begin? This updated tutorial walks you through the entire process of creating a SwiftUI Widget using WidgetKit, complete with step-by-step guidance and working code. By the end, you’ll have a motivational widget that refreshes every 10 minutes with custom colors and messages.</p>
<p><a href="https://appmakers.gumroad.com/l/swiftui-widget-basic">🔗 <strong data-start="9287" data-end="9330">Download the full commented source code (Xcode Project) for this Tutorial</strong></a></p>
<h2 data-start="688" data-end="742">🧰 Getting Started — Create a Fresh SwiftUI Project</h2>
<p data-start="744" data-end="775">Open Xcode and start a new app:</p>
<ul data-start="777" data-end="903">
<li data-start="777" data-end="815">
<p data-start="779" data-end="815">Go to <strong data-start="785" data-end="815">File → New → Project → App</strong></p>
</li>
<li data-start="816" data-end="862">
<p data-start="818" data-end="862">Name your project something like <code class="" data-line="">WidgetApp</code></p>
</li>
<li data-start="863" data-end="885">
<p data-start="865" data-end="885">Interface: SwiftUI</p>
</li>
<li data-start="886" data-end="903">
<p data-start="888" data-end="903">Language: Swift</p>
</li>
</ul>
<p data-start="905" data-end="951">Save the project and move on to the next step.</p>
<h2 data-start="958" data-end="987">➕ Add the Widget Extension</h2>
<p data-start="989" data-end="1026">Now it’s time to add a widget target:</p>
<ul data-start="1028" data-end="1259">
<li data-start="1028" data-end="1053">
<p data-start="1030" data-end="1053"><strong data-start="1030" data-end="1053">File → New → Target</strong></p>
</li>
<li data-start="1054" data-end="1083">
<p data-start="1056" data-end="1083">Select <strong data-start="1063" data-end="1083">Widget Extension</strong></p>
</li>
<li data-start="1084" data-end="1165">
<p data-start="1086" data-end="1106">Uncheck the options:</p>
<ul data-start="1109" data-end="1165">
<li data-start="1109" data-end="1124">
<p data-start="1111" data-end="1124">Live Activity</p>
</li>
<li data-start="1127" data-end="1136">
<p data-start="1129" data-end="1136">Control</p>
</li>
<li data-start="1139" data-end="1165">
<p data-start="1141" data-end="1165">Configuration App Intent</p>
</li>
</ul>
</li>
<li data-start="1166" data-end="1208">
<p data-start="1168" data-end="1208">Name it something like <code class="" data-line="">WidgetAppWidget</code></p>
</li>
<li data-start="1209" data-end="1259">
<p data-start="1211" data-end="1259">Confirm and activate the new scheme if prompted.</p>
</li>
</ul>
<h2 data-start="1266" data-end="1307">🔍 Understand the Auto-Generated Files</h2>
<p data-start="1309" data-end="1341">Xcode will create two key files:</p>
<h3 data-start="1343" data-end="1384"><code class="" data-line="">AppWidgetBundle.swift</code> – Entry Point</h3>
<p data-start="1386" data-end="1461">This struct defines your widget bundle and serves as the extension&#8217;s entry:</p>
<pre><code class="language-swift" data-line="">import WidgetKit
import SwiftUI

@main
struct AppWidgetBundle: WidgetBundle {
    var body: some Widget {
        AppWidget()
    }
}</code></pre>
<div class="contain-inline-size rounded-2xl border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary"></div>
<ul data-start="1635" data-end="1791">
<li data-start="1635" data-end="1722">
<p data-start="1637" data-end="1722">The <code class="" data-line="">@main</code> attribute designates this as the starting point of your widget extension.</p>
</li>
<li data-start="1723" data-end="1791">
<p data-start="1725" data-end="1791">You can group multiple widgets into this bundle in future updates.</p>
</li>
</ul>
<h3 data-start="1798" data-end="1842"><code class="" data-line="">AppWidget.swift</code> – The Core Widget File</h3>
<p data-start="1844" data-end="1892">This file contains everything your widget needs:</p>
<ul data-start="1893" data-end="2076">
<li data-start="1893" data-end="1927">
<p data-start="1895" data-end="1927">A <strong data-start="1897" data-end="1911">data model</strong> (<code class="" data-line="">SimpleEntry</code>)</p>
</li>
<li data-start="1928" data-end="1953">
<p data-start="1930" data-end="1953">A <strong data-start="1932" data-end="1953">timeline provider</strong></p>
</li>
<li data-start="1954" data-end="1984">
<p data-start="1956" data-end="1984">A <strong data-start="1958" data-end="1984">view for the widget UI</strong></p>
</li>
<li data-start="1985" data-end="2034">
<p data-start="1987" data-end="2034">The actual <strong data-start="1998" data-end="2015">widget struct</strong> with configuration</p>
</li>
<li data-start="2035" data-end="2076">
<p data-start="2037" data-end="2076">A <strong data-start="2039" data-end="2051">#Preview</strong> section to test in Xcode</p>
</li>
</ul>
<h2 data-start="2083" data-end="2122">📦 1. Define the Widget’s Data Model</h2>
<p data-start="2124" data-end="2202">The <code class="" data-line="">SimpleEntry</code> struct represents the snapshot of data shown at each update:</p>
<pre><code class="language-swift" data-line="">struct SimpleEntry: TimelineEntry {
    let date: Date
    let emoji: String
}
</code></pre>
<ul>
<li data-start="2297" data-end="2348">
<p data-start="2299" data-end="2348"><code class="" data-line="">date</code>: Tells the system when to show this entry.</p>
</li>
<li data-start="2349" data-end="2448">
<p data-start="2351" data-end="2448"><code class="" data-line="">emoji</code>: Temporary example content — later, we’ll replace it with a message and background color.</p>
</li>
</ul>
<h2 data-start="2455" data-end="2494">🧠 2. Implement the TimelineProvider</h2>
<p data-start="2496" data-end="2594">The <code class="" data-line="">TimelineProvider</code> tells WidgetKit what to display and when. It includes three main functions:</p>
<pre><code class="language-swift" data-line="">struct Provider: TimelineProvider {
   // 1️⃣ func placeholder(in context: Context) -&gt; SimpleEntry { … }
   // 2️⃣ func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -&gt; ()) { … }
   // 3️⃣ func getTimeline(in context: Context, completion: @escaping (Timeline&lt;SimpleEntry&gt;) -&gt; ()) { … }
}
</code></pre>
<h3 data-start="2921" data-end="2947">1️⃣ <code class="" data-line="">placeholder(in:)</code></h3>
<p data-start="2948" data-end="2994">Shows temporary content in the widget gallery.</p>
<pre><code class="language-swift" data-line="">func placeholder(in context: Context) -&gt; SimpleEntry {
    SimpleEntry(date: Date(), emoji: &quot;😀&quot;)
}
</code></pre>
<h3 data-start="3110" data-end="3136">2️⃣ <code class="" data-line="">getSnapshot(in:)</code></h3>
<p data-start="3137" data-end="3172">Provides a static snapshot of data.</p>
<pre><code class="language-swift" data-line="">func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -&gt; ()) {
    let entry = SimpleEntry(date: Date(), emoji: &quot;😀&quot;)
    completion(entry)
}
</code></pre>
<h3 data-start="3350" data-end="3376">3️⃣ <code class="" data-line="">getTimeline(in:)</code></h3>
<p data-start="3377" data-end="3414">Generates a list of timeline entries.</p>
<pre><code class="language-swift" data-line="">func getTimeline(in context: Context, completion: @escaping (Timeline&lt;Entry&gt;) -&gt; ()) {
    var entries: [SimpleEntry] = []

    let currentDate = Date()
    for hourOffset in 0 ..&lt; 5 {
        let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
        let entry = SimpleEntry(date: entryDate, emoji: &quot;😀&quot;)
        entries.append(entry)
    }

    let timeline = Timeline(entries: entries, policy: .atEnd)
    completion(timeline)
}
</code></pre>
<h2 data-start="3908" data-end="3951">🖼️ 3. Create the Widget UI with SwiftUI</h2>
<p data-start="3953" data-end="4018">Use SwiftUI to define how your widget appears on the home screen.</p>
<pre><code class="language-swift" data-line="">struct AppWidgetEntryView : View {
    var entry: Provider.Entry

    var body: some View {
        VStack {
            Text(&quot;Time:&quot;)
            Text(entry.date, style: .time)
            Text(&quot;Emoji:&quot;)
            Text(entry.emoji)
        }
    }
}
</code></pre>
<h2 data-start="4292" data-end="4331">🧱 4. Configure the Widget Structure</h2>
<p data-start="4333" data-end="4405">This section ties everything together and registers the widget with iOS.</p>
<pre><code class="language-swift" data-line="">struct AppWidget: Widget {
    let kind: String = &quot;AppWidget&quot;

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: Provider()) { entry in
            if #available(iOS 17.0, *) {
                AppWidgetEntryView(entry: entry)
                    .containerBackground(.fill.tertiary, for: .widget)
            } else {
                AppWidgetEntryView(entry: entry)
                    .padding()
                    .background()
            }
        }
        .configurationDisplayName(&quot;My Widget&quot;)
        .description(&quot;This is an example widget.&quot;)
    }
}
</code></pre>
<h2 data-start="5029" data-end="5060">👀 5. Previewing Your Widget</h2>
<p data-start="5062" data-end="5125">SwiftUI’s new <code class="" data-line="">#Preview</code> lets you simulate the widget in Xcode.</p>
<pre><code class="language-swift" data-line="">#Preview(as: .systemSmall) {
    AppWidget()
} timeline: {
    SimpleEntry(date: .now, emoji: &quot;😀&quot;)
    SimpleEntry(date: .now, emoji: &quot;🤩&quot;)
}
</code></pre>
<h2 data-start="5289" data-end="5343">🎯 Make It Useful — Show Random Motivational Quotes</h2>
<p data-start="5345" data-end="5383">Let’s make this widget more inspiring.</p>
<p data-start="5385" data-end="5483">Instead of an emoji, we’ll show motivational quotes every 10 minutes and change background colors.</p>
<p data-start="5385" data-end="5483">🛠 Update SimpleEntry</p>
<pre><code class="language-swift" data-line="">struct SimpleEntry: TimelineEntry {
    let date: Date
    let message: String
    let color: Color
}
</code></pre>
<h3 data-start="5638" data-end="5660">🧠 Update Provider</h3>
<p data-start="5662" data-end="5693">Add sample messages and colors:</p>
<pre><code class="language-swift" data-line="">let messages = [
    &quot;Keep going 💪&quot;, &quot;You&#039;re doing great!&quot;, &quot;Just one more step 🚶&quot;,
    &quot;Stay focused 🎯&quot;, &quot;You’ve got this 🔥&quot;, &quot;Make it count 💥&quot;,
    &quot;Smile, breathe, move 🌿&quot;, &quot;Every moment matters ⏳&quot;
]

let backgroundColors: [Color] = [
    .blue, .green, .orange, .pink, .purple, .yellow, .teal, .mint
]

let defaultColor: Color = .gray
let defaultMessage = &quot;Keep going 💪&quot;
</code></pre>
<p>Update <code class="" data-line="">placeholder</code> and <code class="" data-line="">getSnapshot</code>:</p>
<pre><code class="language-swift" data-line="">func placeholder(in context: Context) -&gt; SimpleEntry {
    SimpleEntry(date: Date(), message: defaultMessage, color: defaultColor)
}

func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -&gt; ()) {
    let entry = SimpleEntry(date: Date(), message: defaultMessage, color: defaultColor)
    completion(entry)
}
</code></pre>
<p>Update <code class="" data-line="">getTimeline</code>:</p>
<pre><code class="language-swift" data-line="">func getTimeline(in context: Context, completion: @escaping (Timeline&lt;Entry&gt;) -&gt; ()) {
    var entries: [SimpleEntry] = []
    let currentDate = Date()

    for minuteOffset in 0 ..&lt; 6 {
        let entryDate = Calendar.current.date(byAdding: .minute, value: minuteOffset * 10, to: currentDate)!
        let message = messages.randomElement() ?? defaultMessage
        let backgroundColor = backgroundColors.randomElement() ?? defaultColor
        let entry = SimpleEntry(date: entryDate, message: message, color: backgroundColor)
        entries.append(entry)
    }

    let timeline = Timeline(entries: entries, policy: .atEnd)
    completion(timeline)
}
</code></pre>
<p>🎨 Redesign the Widget View</p>
<pre><code class="language-swift" data-line="">struct AppWidgetEntryView : View {
    var entry: Provider.Entry

    var body: some View {
        ZStack {
            VStack(spacing: 10) {
                Text(entry.message)
                    .font(.headline)
                    .multilineTextAlignment(.center)
                    .minimumScaleFactor(0.5)
                    .lineLimit(1)
                    .foregroundStyle(.primary)

                Text(entry.date, style: .time)
                    .font(.caption)
                    .foregroundStyle(.secondary)
            }
        }
    }
}
</code></pre>
<p>⚙️ Update Widget Config</p>
<pre><code class="language-swift" data-line="">struct AppWidget: Widget {
    let kind: String = &quot;AppWidget&quot;

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: Provider()) { entry in
            let backgroundColor = entry.color.opacity(0.25)

            if #available(iOS 17.0, *) {
                AppWidgetEntryView(entry: entry)
                    .containerBackground(backgroundColor, for: .widget)
            } else {
                AppWidgetEntryView(entry: entry)
                    .padding()
                    .background(backgroundColor)
            }
        }
        .configurationDisplayName(&quot;Motivational Widget&quot;)
        .description(&quot;This Widget Will Keep You Motivated&quot;)
    }
}
</code></pre>
<p>🧪 Preview It</p>
<pre><code class="language-swift" data-line="">#Preview(&quot;Small Widget&quot;, as: .systemSmall) {
    AppWidget()
} timeline: {
    SimpleEntry(date: .now, message: &quot;Keep going 💪&quot;, color: .orange)
    SimpleEntry(date: .now, message: &quot;Stay focused 🎯&quot;, color: .purple)
}
</code></pre>
<h2 data-start="8790" data-end="8817">📱 Test on a Real Device</h2>
<ol data-start="8819" data-end="9001">
<li data-start="8819" data-end="8868">
<p data-start="8822" data-end="8868">Run your <strong data-start="8831" data-end="8850">main app target</strong> on a real device.</p>
</li>
<li data-start="8869" data-end="8940">
<p data-start="8872" data-end="8940">Long-press the Home Screen → Tap &#8220;+&#8221; → Search for your widget → Add.</p>
</li>
<li data-start="8941" data-end="9001">
<p data-start="8944" data-end="9001">Optionally tap and <strong data-start="8963" data-end="8982">edit the widget</strong> for configuration.</p>
</li>
</ol>
<p data-start="9003" data-end="9090"><strong data-start="9003" data-end="9011">Note</strong>: App Groups aren’t required if your widget shows static or internal data only.</p>
<h2 data-start="9097" data-end="9113">✅ Final Notes</h2>
<ul data-start="9115" data-end="9282">
<li data-start="9115" data-end="9176">
<p data-start="9117" data-end="9176">Code is tested on Xcode 16.2 and iOS 18.2 (as of June 2025)</p>
</li>
<li data-start="9177" data-end="9231">
<p data-start="9179" data-end="9231">Always check compatibility with newer Xcode versions</p>
</li>
<li data-start="9232" data-end="9282">
<p data-start="9234" data-end="9282">If you run into issues, let us know via comments</p>
</li>
</ul>
<p><a href="https://appmakers.gumroad.com/l/swiftui-widget-basic">DOWNLOAD SOURCE CODE FOR THIS TUTORIAL</a></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>The post <a href="https://appmakers.dev/build-your-first-widget-in-swiftui-a-guide-with-source-code/">Build Your First Widget in SwiftUI — A Guide with Source Code</a> appeared first on <a href="https://appmakers.dev">AppMakers.Dev</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
