<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tyler Hackbart</title>
    <description>The latest articles on DEV Community by Tyler Hackbart (@tyler_hackbart_0929384).</description>
    <link>https://dev.to/tyler_hackbart_0929384</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3948507%2F9f0333c9-7843-403f-9cf8-89c5c516103e.jpg</url>
      <title>DEV Community: Tyler Hackbart</title>
      <link>https://dev.to/tyler_hackbart_0929384</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tyler_hackbart_0929384"/>
    <language>en</language>
    <item>
      <title>Goodbye, Manual State Notifications. Hello SwiftData</title>
      <dc:creator>Tyler Hackbart</dc:creator>
      <pubDate>Mon, 22 Jun 2026 15:09:46 +0000</pubDate>
      <link>https://dev.to/tyler_hackbart_0929384/goodbye-manual-state-notifications-hello-swiftdata-3lf9</link>
      <guid>https://dev.to/tyler_hackbart_0929384/goodbye-manual-state-notifications-hello-swiftdata-3lf9</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmenphzwy9w1ltm8kafnr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmenphzwy9w1ltm8kafnr.png" alt="SwiftData - Apple (https://developer.apple.com/documentation/swiftdata)" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've been carrying iCloud sync around in my apps for years, and for most of that time it was the part of the stack I dreaded touching. The setup was Core Data with &lt;code&gt;NSPersistentCloudKitContainer&lt;/code&gt;, wired into a fairly traditional Model-View-Controller structure. It worked, eventually, but "worked" did a lot of heavy lifting in that sentence. SwiftData has changed how that part of my apps feels day to day, and I want to be honest about both what got better and what I gave up.&lt;/p&gt;

&lt;h2&gt;
  
  
  The old way: state was the hard part
&lt;/h2&gt;

&lt;p&gt;The thing nobody tells you about Core Data plus CloudKit in an MVC app is that the persistence wasn't really the problem. The problem was state. My view controllers were responsible for knowing the current truth, but the truth could change underneath them at any moment because a sync had landed from another device. So I was constantly reconciling two ideas of "now": what the controller had loaded, and what the store actually held after CloudKit did its thing in the background.&lt;/p&gt;

&lt;p&gt;That meant a lot of plumbing that had nothing to do with my actual app. I was listening for &lt;code&gt;NSPersistentStoreRemoteChange&lt;/code&gt; notifications, refetching, diffing, and then telling the UI to reload. Half the time the reload was unnecessary and the whole screen flickered or scrolled itself back to the top for no reason the user could see. The other half it was necessary and I'd missed it, so a record showed up stale until the user backed out and came in again.&lt;/p&gt;

&lt;p&gt;It felt like overkill, and that feeling was the tell. Refreshing the entire view every time a change came in was a sledgehammer, but doing anything more surgical meant hand-writing change tracking that I didn't trust and didn't want to maintain. The controller layer became the place where all of this anxiety lived. It knew about the store, the sync, the merge policy, the fetch, and the UI, and any one of those changing could break the other four.&lt;/p&gt;

&lt;h2&gt;
  
  
  The new way: a fraction of the code
&lt;/h2&gt;

&lt;p&gt;The first thing that struck me moving to SwiftData was how much code simply disappeared. A model that used to be an &lt;code&gt;.xcdatamodeld&lt;/code&gt; file, a generated &lt;code&gt;NSManagedObject&lt;/code&gt; subclass, a fetch request, a fetched results controller, and the notification handling around it collapses into a single &lt;code&gt;@Model&lt;/code&gt; class and a &lt;code&gt;@Query&lt;/code&gt; in the view. Honestly it's somewhere around a fifth of what I used to write to get the same behaviour. The container setup that used to be a careful block of options is now a &lt;code&gt;.modelContainer&lt;/code&gt; modifier with iCloud configured alongside it.&lt;/p&gt;

&lt;p&gt;But the real change isn't the line count, it's that the state problem mostly went away. &lt;code&gt;@Query&lt;/code&gt; observes the context and refreshes the UI when the underlying data changes, including changes that arrive from another device. The thing I used to hand-build, that whole remote-change-to-refetch-to-reload loop, is now just how the framework behaves. A record syncs in from my iPad and the list on my iPhone updates, and I didn't write a single line to make that happen. The first time I saw it work I genuinely didn't believe it had synced, because I kept waiting for the manual refresh I no longer had to trigger.&lt;/p&gt;

&lt;p&gt;That's the part that feels almost magic compared to the old days. The UI staying in step with the store across devices used to be the hardest thing to get right, and now it's the default.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I gave up, honestly
&lt;/h2&gt;

&lt;p&gt;It isn't free. With the old setup I had complete control over the merge policy and exactly when and how the UI updated. SwiftData takes that control back in exchange for the convenience, and when something does go wrong, there's less surface area to reach in and fix it. The framework is younger, the edge cases around CloudKit are still rougher than the mature Core Data path, and I've hit sync quirks where my only real debugging tool was waiting and watching rather than inspecting.&lt;/p&gt;

&lt;p&gt;I also lost some of the explicitness. When everything was manual, I could see the entire flow of a change from store to screen. Now a lot of that happens inside the framework, which is wonderful right up until you need to understand why a particular update didn't propagate. There's a real tradeoff between "I don't have to think about it" and "I can't see what it's doing."&lt;/p&gt;

&lt;h2&gt;
  
  
  Worth it anyway
&lt;/h2&gt;

&lt;p&gt;For the kind of apps I build, it's been a clear win. The maintenance burden that used to sit in my controllers, all that state reconciliation that felt like overkill every time it fired, is gone, and it took most of the code with it. I traded fine-grained control for a system that mostly just keeps the UI honest across devices on its own. After years of fighting &lt;code&gt;NSPersistentCloudKitContainer&lt;/code&gt; to get that, I'll take the trade. Turns out the secret to syncing state across all my devices was to stop managing it myself and just let SwiftData take the iCloud.&lt;/p&gt;




&lt;p&gt;I'm &lt;strong&gt;Tyler Hackbart&lt;/strong&gt;, the founder and developer behind &lt;strong&gt;Juice Box Monkey Designs&lt;/strong&gt;, where I build indie iOS and macOS apps under the Just Apps name. Most of what I write comes straight out of whatever I'm currently shipping, like this one did. You can see what I'm building at &lt;a href="https://www.juiceboxmonkeydesigns.com" rel="noopener noreferrer"&gt;juiceboxmonkeydesigns.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>swift</category>
      <category>ios</category>
      <category>swiftdata</category>
      <category>coredata</category>
    </item>
  </channel>
</rss>
