<?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/"
	xmlns:media="http://search.yahoo.com/mrss/"
>

<channel>
	<title>SQLite &#8211; Wade Tregaskis</title>
	<atom:link href="https://wadetregaskis.com/tags/sqlite/feed/" rel="self" type="application/rss+xml" />
	<link>https://wadetregaskis.com</link>
	<description></description>
	<lastBuildDate>Mon, 24 Jun 2024 23:39:21 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://wadetregaskis.com/wp-content/uploads/2016/03/Stitch-512x512-1-256x256.png</url>
	<title>SQLite &#8211; Wade Tregaskis</title>
	<link>https://wadetregaskis.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">226351702</site>	<item>
		<title>When all you have is a Core Data, everything looks like…</title>
		<link>https://wadetregaskis.com/when-all-you-have-is-a-core-data-everything-looks-like/</link>
					<comments>https://wadetregaskis.com/when-all-you-have-is-a-core-data-everything-looks-like/#comments</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Mon, 24 Jun 2024 23:32:00 +0000</pubDate>
				<category><![CDATA[Ancient History]]></category>
		<category><![CDATA[Coding]]></category>
		<category><![CDATA[Apple]]></category>
		<category><![CDATA[Core Data]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[NSCoding]]></category>
		<category><![CDATA[Shark]]></category>
		<category><![CDATA[SQLite]]></category>
		<category><![CDATA[SwiftData]]></category>
		<category><![CDATA[Time Profile]]></category>
		<category><![CDATA[XML]]></category>
		<guid isPermaLink="false">https://wadetregaskis.com/?p=8235</guid>

					<description><![CDATA[Reading SwiftData vs Realm: Performance Comparison reminded me of an anecdote from my days working on Shark, at Apple. I don&#8217;t really remember the timing &#8211; sometime between 2006 and 2010 &#8211; but presumably around 2006 as I recall it was when Core Data was still relatively new. For whatever reason, there was a huge&#8230; <a class="read-more-link" href="https://wadetregaskis.com/when-all-you-have-is-a-core-data-everything-looks-like/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p>Reading <a href="https://www.emergetools.com/blog/posts/swiftdata-vs-realm-performance-comparison" data-wpel-link="external" target="_blank" rel="external noopener">SwiftData vs Realm: Performance Comparison</a> reminded me of an anecdote from my days working on <a href="https://leopard-adc.pepas.com/documentation/DeveloperTools/Conceptual/SharkUserGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40005233-CH1-DontLinkElementID_6" data-wpel-link="external" target="_blank" rel="external noopener">Shark</a>, at Apple.</p>



<p>I don&#8217;t really remember the timing &#8211; sometime between 2006 and 2010 &#8211; but presumably around 2006 as I recall it was when <a href="https://en.wikipedia.org/wiki/Core_Data" data-wpel-link="external" target="_blank" rel="external noopener">Core Data</a> was still relatively new.  For whatever reason, there was a huge push internal to Apple to use Core Data <em>everywhere</em>.  People were running around all over the place asking &#8220;can it be made to use Core Data?&#8221;, for Apple&#8217;s frameworks and applications.</p>



<p>Keep in mind that Core Data at that time was similar to <a href="https://developer.apple.com/documentation/swiftdata" data-wpel-link="external" target="_blank" rel="external noopener">SwiftData</a> now &#8211; very limited functionality, and <em>chock full</em> of bugs.  But of course it&#8217;s the nature of &#8216;shiny&#8217; new things that their proponents think it&#8217;s the second coming and the cure for all ills.</p>



<p>So, I recall sitting down with a couple of folks from the Core Data team, that were there to see if Shark could adopt Core Data.  A little like letting the missionaries in, if only out of morbid curiosity.</p>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="1200" height="675" src="https://wadetregaskis.com/wp-content/uploads/2024/06/orgazmo-mormon-missionaries-have-you-heard-the-good-news-about-core-data.avif" alt="Still from the scene in Orgazmo with the Mormon Missionaries greeting a homeowner at their door and asking &quot;Have you heard the good news about Core Data?&quot;." class="wp-image-8240" srcset="https://wadetregaskis.com/wp-content/uploads/2024/06/orgazmo-mormon-missionaries-have-you-heard-the-good-news-about-core-data.avif 1200w, https://wadetregaskis.com/wp-content/uploads/2024/06/orgazmo-mormon-missionaries-have-you-heard-the-good-news-about-core-data-256x144.avif 256w, https://wadetregaskis.com/wp-content/uploads/2024/06/orgazmo-mormon-missionaries-have-you-heard-the-good-news-about-core-data-1024x576.avif 1024w, https://wadetregaskis.com/wp-content/uploads/2024/06/orgazmo-mormon-missionaries-have-you-heard-the-good-news-about-core-data-768x432.avif 768w, https://wadetregaskis.com/wp-content/uploads/2024/06/orgazmo-mormon-missionaries-have-you-heard-the-good-news-about-core-data@2x.avif 2400w, https://wadetregaskis.com/wp-content/uploads/2024/06/orgazmo-mormon-missionaries-have-you-heard-the-good-news-about-core-data-256x144@2x.avif 512w" sizes="(max-width: 1200px) 100vw, 1200px" /></figure>



<p>Have you heard the good news?  Core Data is here to save your very data.  It&#8217;s effortless and divine and its unintuitive, thread-unsafe API will definitely not be the bane of all its users for the next fifteen years.</p>



<p>Jokes aside, they were in fact earnestly curious if Shark could use Core Data, instead of its own purpose-built binary formats, for storing &amp; querying its profiling data.  It was perhaps the classic case of naively underestimating the complexity of a foreign domain.  By my recollection, they assumed our profiling data was just a small handful of homogenous, relatively trivial records.  &#8220;At second N, the program ran the function named XYZ&#8221; or somesuch.</p>



<p>I think we (Shark engineers) tried to be open-minded and kind.  We were sceptical, but you never know until you actually look.  We could see some potential for a more general query capability, for example.  But of course the first and most obvious hurdle was: how well does Core Data handle sizeable numbers of records?  Oh yes, was the response, it&#8217;s great even with tens of thousands of records.</p>



<figure class="wp-block-image size-full"><img decoding="async" width="1128" height="480" src="https://wadetregaskis.com/wp-content/uploads/2024/06/star-trek-iv-the-voyage-home-is-that-a-lot.avif" alt="Still image of the pawn shop scene from Star Trek IV (The Voyage Home) showing Kirk &amp; Spock responding to the offer of $100 for the antique spectacles with &quot;Is that a lot?&quot;." class="wp-image-8236" srcset="https://wadetregaskis.com/wp-content/uploads/2024/06/star-trek-iv-the-voyage-home-is-that-a-lot.avif 1128w, https://wadetregaskis.com/wp-content/uploads/2024/06/star-trek-iv-the-voyage-home-is-that-a-lot-256x109.avif 256w, https://wadetregaskis.com/wp-content/uploads/2024/06/star-trek-iv-the-voyage-home-is-that-a-lot-1024x436.avif 1024w, https://wadetregaskis.com/wp-content/uploads/2024/06/star-trek-iv-the-voyage-home-is-that-a-lot-768x327.avif 768w, https://wadetregaskis.com/wp-content/uploads/2024/06/star-trek-iv-the-voyage-home-is-that-a-lot@2x.avif 2256w, https://wadetregaskis.com/wp-content/uploads/2024/06/star-trek-iv-the-voyage-home-is-that-a-lot-256x109@2x.avif 512w" sizes="(max-width: 1128px) 100vw, 1128px" /></figure>



<p>We asked how it did with tens of <em>millions</em> of records, and that was pretty much the end of the conversation.</p>



<details class="wp-block-details is-layout-flow wp-block-details-is-layout-flow"><summary>Background on the Time Profile data structure</summary>
<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>For context, the data in a Shark Time Profile (for example) was basically an array of samples, where each sample records the process &amp; thread IDs, and the callstack (expressed as an array of pointer-sized values; the first being the current PC and the rest the return addresses found by walking back up the thread&#8217;s stack).</p>



<p>Callstacks back then were relatively small, by modern standards &#8211; this was predominately C/C++/Objective-C code which tended to be far simpler in its structure than e.g. Swift; <em>way</em> fewer closures (blocks), no async suspension points to split logical functions up into numerous implementation functions, etc.  So the average was probably something in the low tens.  A hundred frames was considered a <em>big</em> callstack (which is sadly funny in hindsight, given that&#8217;s trivial by e.g. SwiftUI&#8217;s standards 😒).</p>



<p>A useful profile had at least thousands of such samples, and typical profiles were in the tens to hundreds of thousands (the latter usually for All Threads States profiles, particularly those of the whole system).  Some profiles could run into the millions or tens of millions (it&#8217;s not always easy or predictable as to when a performance problem will exhibit itself, so recording sometimes had to start early and run long).</p>



<p>I&#8217;m pretty sure Shark used <code><a href="https://developer.apple.com/documentation/foundation/nscoding" data-wpel-link="external" target="_blank" rel="external noopener">NSCoding</a></code> for the overall serdes, but a lot of that serdes was of huge chunks of (as far as <code>NSCoding</code> was concerned) arbitrary bytes. The file format was overall fairly efficient (though I don&#8217;t recall it ever using explicit data compression, nor even delta encoding for callstacks).</p>
</div></div>
</details>



<p>It wasn&#8217;t just the volume of data, it was also the dramatic difference in representation efficiency. The in-memory representation in Shark was basically as efficient as it could be &#8211; basically just arrays of compact structs, sometimes with pointers to other arrays (which might share a <code>malloc</code> block to avoid the overhead of small allocations) which were usually just of <code>uint32_t</code> or <code>uint64_t</code>. The most important operations &#8211; indexing to an arbitrary point in the profile&#8217;s timeline, then scanning forward over the data &#8211; were about as fast as they can possibly be.</p>



<p>In contrast, Core Data would have required an entire <em>object</em> (<code><a href="https://developer.apple.com/documentation/coredata/nsmanagedobject" data-wpel-link="external" target="_blank" rel="external noopener">NSManagedObject</a></code> subclass) for at least every sample, if not every <code>uintXX_t</code> in the callstack (depending on how &#8216;pure&#8217; you wanted the design to be). It would have increased memory usage by at least an order of magnitude &#8211; and Shark already struggled with big profiles on the hardware of the day, which typically had just a couple of GiB of RAM. Even the most trivial operations &#8211; like reading the data in from disk and iterating it sequentially would have been <em>thousands</em> of times slower.</p>



<p>In defence of the Core Data folks in the meeting &#8211; and I don&#8217;t remember who specifically it was &#8211; they never tried to misrepresent or exaggerate what Core Data could do.  I seem to recall them being quite nice people.  But as soon as we started explaining the type and volume of data that we worked with, they clearly gave up on any kind of pitch.  Core Data was designed for <em>developer convenience</em>, not runtime efficiency or performance.</p>



<p>It&#8217;s never ceased to surprise and disappoint me how many folks try to arbitrarily apply generalised data storage systems &#8211; <em>particularly</em> SQLite and MySQL, or wrappers thereover.  Usually for the same reasons &#8211; perceived convenience to them, right now, not necessarily efficiency (nor the convenience of their successors).</p>



<p>I guess by modern standards SQLite is considered efficient and fast, but &#8211; hah &#8211; <em>back in my day</em> SQLite was what you used when you didn&#8217;t have time to write your own, <em>efficient and fast</em> persistent data management system.</p>



<p>See also JSON and its older sister XML. 😔</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/when-all-you-have-is-a-core-data-everything-looks-like/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			<media:content url="https://wadetregaskis.com/wp-content/uploads/2024/06/orgazmo-mormon-missionaries-have-you-heard-the-good-news-about-core-data.avif" medium="image" />
<post-id xmlns="com-wordpress:feed-additions:1">8235</post-id>	</item>
		<item>
		<title>SwiftData pitfalls</title>
		<link>https://wadetregaskis.com/swiftdata-pitfalls/</link>
					<comments>https://wadetregaskis.com/swiftdata-pitfalls/#comments</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Wed, 15 Nov 2023 21:04:36 +0000</pubDate>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Broken by design]]></category>
		<category><![CDATA[Bugs!]]></category>
		<category><![CDATA[Core Data]]></category>
		<category><![CDATA[MySQL Workbench]]></category>
		<category><![CDATA[Snafu]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[SQLite]]></category>
		<category><![CDATA[Swift]]></category>
		<category><![CDATA[SwiftData]]></category>
		<category><![CDATA[Undocumented]]></category>
		<guid isPermaLink="false">https://blog.wadetregaskis.com/?p=5378</guid>

					<description><![CDATA[I&#8217;ve been exploring SwiftData lately, and I&#8217;ve been unpleasantly surprised by how many sharp edges it has. I&#8217;m going to try to describe some of them here, so that hopefully others can avoid them (or perhaps be dissuaded from using SwiftData to begin with). I&#8217;m using Xcode 15.0.1 (Swift 5.9) on macOS 14.1 (Sonoma). Background&#8230; <a class="read-more-link" href="https://wadetregaskis.com/swiftdata-pitfalls/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p>I&#8217;ve been exploring SwiftData lately, and I&#8217;ve been unpleasantly surprised by how many sharp edges it has.  I&#8217;m going to try to describe some of them here, so that hopefully others can avoid them (or perhaps be dissuaded from using SwiftData to begin with).</p>



<p>I&#8217;m using Xcode 15.0.1 (Swift 5.9) on macOS 14.1 (Sonoma).</p>



<h2 class="wp-block-heading">Background</h2>



<p>I&#8217;ve dabbled in Core Data over the years &#8211; mostly pre-Swift &#8211; and have a kind of begrudging, distant respect.  I understand what it&#8217;s trying to do and I can appreciate some of the ways in which it makes that easier, but I&#8217;m also all too familiar with its limitations and caveats.  So mostly I&#8217;ve ignored it, preferring to use other approaches &#8211; whether that be JSON or plists for simple cases, or just directly using a SQL database when that feature-set genuinely is warranted.</p>



<p>When SwiftData was introduced, I was intrigued.  On the surface it seemed like it greatly reduced the boilerplate and some of the complexity of using Core Data.  It at least seemed like an appealing option for light use, with relatively simple models and especially with SwiftUI.  It seems pretty obviously targeted at the typical, pretty trivial patterns that most iOS apps have &#8211; i.e. not much more than lists of objects.</p>



<p>But it&#8217;s only recently that I actually tried using SwiftData for more than just some trivial toy cases.  I had a textbook case of a hierarchical type tree, with fairly simple 1-to-1 or many-to-1 relationships, the need for cascade deletes in some cases but not others, and a dataset a little larger than what I could get away with just stuffing into User Defaults.</p>



<p>Alas, what I&#8217;ve found is that SwiftData has so many glaring bugs and limitations, that even for this textbook example case, it just doesn&#8217;t make any sense to use it.</p>



<h2 class="wp-block-heading">Example use case</h2>



<p>My model is fairly simple &#8211; a root object called <code>House</code> which can contain multiple <code>Floor</code>s each of which has multiple <code>Room</code>s.  Each of those has a few other attributes &#8211; simple strings and numbers &#8211; and the <code>Room</code>s can reference a few other model types, like <code>Weapon</code>s and <code>Monster</code>s, as 1-to-1 relationships.</p>



<p>The order of floors and rooms matters &#8211; they correspond to top to bottom (attic to basement) and left to right as will be rendered in the GUI.</p>



<h2 class="wp-block-heading">Design flaws</h2>



<h3 class="wp-block-heading" id="autosave-does-not-work">Auto-save doesn&#8217;t work</h3>



<p>By default, model containers are <em>supposed</em> to auto-save.  That&#8217;s what the documentation says, both in <a href="https://developer.apple.com/documentation/swiftui/view/modelcontainer(for:inmemory:isautosaveenabled:isundoenabled:onsetup:)-8oc48" data-wpel-link="external" target="_blank" rel="external noopener">the API reference</a> and the tutorials:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>In both instances, the returned context periodically checks whether it contains unsaved changes, and if so, implicitly saves those changes on your behalf. For contexts you create manually, set the&nbsp;<a class="" href="https://developer.apple.com/documentation/swiftdata/modelcontext/autosaveenabled"><code>autosaveEnabled</code></a>&nbsp;property to&nbsp;<code>true</code>&nbsp;to get the same behavior [sic].</p>
<cite>Apple&#8217;s <a href="https://developer.apple.com/documentation/swiftdata/preserving-your-apps-model-data-across-launches#Save-models-for-later-use" data-type="link" data-id="https://developer.apple.com/documentation/swiftdata/preserving-your-apps-model-data-across-launches#Save-models-for-later-use" data-wpel-link="external" target="_blank" rel="external noopener">Save models for later use</a> (subsection of <a href="https://developer.apple.com/documentation/swiftdata/preserving-your-apps-model-data-across-launches" data-wpel-link="external" target="_blank" rel="external noopener">Preserving your app’s model data across launches</a>)</cite></blockquote>



<p>I suppose it hinges on the word &#8220;periodically&#8221;.  In my testing, that period must be tens of seconds, or worse.  It&#8217;s trivial to see that it doesn&#8217;t work &#8211; just make some changes and close the window, or quit your app.  Virtually all the time &#8211; even if you wait several seconds &#8211; those changes are silently lost!</p>



<p>It&#8217;s clear that SwiftData has no hooks into deinitialisation, view / window closure, app exit, etc.  You would think it <em>has</em> to, in order to save things reliably even in the happy-path cases (as opposed to e.g. app crashes).  But it simply does not, and thus it does not.</p>



<h4 class="wp-block-heading">Workarounds</h4>



<p>If you want full resilience to unexpected app termination (e.g. crashes) then <em>any</em> time you make a change to <em>any</em> SwiftData-managed object, you have to immediately call <code>context.save()</code> manually.  Which is especially annoying since that method throws, so you have to figure out some sane way to handle that failure possibility in every situation in which you make any changes to your models.</p>



<p>For non-trivial applications, this leads to your code being <em>riddled</em> with save-management code.  Think about every SwiftUI field which reflects an element of your model and enables mutation of it.  Every button that adds or removes or reorders anything.  Every editable list view.  Etc.</p>



<p>It&#8217;s easy to miss a spot where you make a modification, leading to silent data loss.</p>



<p>If you&#8217;re willing to lose data when your app crashes (which you really <em>shouldn&#8217;t</em> be willing to do so easily do), you can limit your saves to certain key paths &#8211; e.g. window closure, app exit, etc.  But you have to find and hook into all those things manually.</p>



<p>This is nominally no different to some SwiftData alternatives (e.g. <code>NSCoding</code>), although below par compared to User Defaults and most other SQL database frameworks.</p>



<h3 class="wp-block-heading">Arrays of <code>@Model</code> objects are randomly shuffled</h3>



<p><code>Array</code> in Swift is very explicitly an <em>ordered</em> collection, in the sense that the elements within have a specific albeit arbitrary order, based on how they&#8217;re inserted.  This is the same as arrays / vectors / lists in practically every programming language.</p>



<p>SwiftData violates this by randomly reordering elements at various times, such as when [re]loading model objects from persist storage.</p>



<p>The reason is pretty simple &#8211; it fails to record the order of elements in its underlying SQLite database, instead using a randomly-assigned, arbitrary integer as a uniquing key, and nothing else.</p>



<p>This is a pretty startling design flaw.  I&#8217;m not sure how SwiftData got out the door with this.</p>



<h4 class="wp-block-heading">Workarounds</h4>



<p>I haven&#8217;t found any good workarounds.  The advice you&#8217;ll generally see online (e.g. <a href="https://stackoverflow.com/questions/76889986/swiftdata-ios-17-array-in-random-order" data-wpel-link="external" target="_blank" rel="external noopener">on StackOverflow</a>) is to manually load the objects in the array, specifying a sort order as part of the query.  But this basically has you manually reimplementing the relational aspects of SwiftData:</p>



<ul class="wp-block-list">
<li>You have to add a member variable to the target class in question <em>just</em> to store its index in the array.  You need one of these variables for <em>every</em> many-to relationship it can be a part of.</li>



<li>You have to then somehow populate those variables <em>and</em> keep them in sync with any changes to the arrays.</li>



<li>You can&#8217;t actually use the array member variables &#8211; since the objects are in random order &#8211; and instead have to manually query the database separately.  Or, you can sort the array member variables at some point, but it&#8217;s difficult to do that efficiently (you can&#8217;t really be sure when SwiftData is going to mangle the sort order, so you basically have to do the sorting on the fly every single time you access the array).</li>
</ul>



<p>Rather than going through all that hassle, it&#8217;s easier to just not use SwiftData &#8211; e.g. you can simply use a normal object graph that&#8217;s persisted to disk as JSON, or in property list form, or using <code>NSCoding</code>, etc.</p>



<h3 class="wp-block-heading"><code>let</code> cannot be used for bidirectional relationships</h3>



<p>All member variables (of <code>@Model</code> classes) that refer bidirectionally to other model classes <em>must</em> be variables (<code>var</code>), not constants (<code>let</code>).  Irrespective of their actual intended semantics.</p>



<p>This is seemingly just an inherent part of SwiftData (and Core Data underneath) &#8211; the presumption that <em>all</em> relationships are optional <em>and</em> can change arbitrarily.  It worked okay in Objective-C because that was closer to the reality of Objective-C objects, but it flies in the face of Swift&#8217;s much stronger controls and expectations on mutability and optionality.</p>



<p>It would be a relatively minor annoyance &#8211; losing compile-time protection of immutability is frustrating but not technically a show-stopper &#8211; except that there are <em>no</em> warnings or indications otherwise, of any kind, that you&#8217;re &#8220;holding it wrong&#8221;.  If you declare the member with <code>let</code>, everything will compile without so much as a warning, but you&#8217;ll run into obtuse runtime errors and crashes, e.g.:</p>



<pre class="wp-block-preformatted">💣 Could not cast value of type 'Swift.KeyPath&lt;Game.House, Swift.Array&lt;Game.Floor&gt;&gt;' (0x12e00cc98) to 'Swift.ReferenceWritableKeyPath&lt;Game.House, Swift.Array&lt;Floor.Region&gt;&gt;' (0x13e0b4198).</pre>



<p>What that error message is <em>trying</em> but failing to convey is that you cannot (outside of initialisation) write to a <code>let</code>, of course, so its attempt to modify the property failed.  So it crashed your app.</p>



<h4 class="wp-block-heading">Workarounds</h4>



<p>None, really.  You just have to remember, <em>every time</em>, to avoid <code>let</code>.  Possibly you could obtain &amp; configure some linter to check this for you.</p>



<h3 class="wp-block-heading" id="relationships-are-always-optional">Relationships are secretly implicitly unwrapped by default</h3>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">@Model</span></span>
<span class="line"><span style="color: #0000FF">final</span><span style="color: #000000"> </span><span style="color: #0000FF">class</span><span style="color: #000000"> </span><span style="color: #267F99">Room</span><span style="color: #000000"> {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">let</span><span style="color: #000000"> monster: Monster</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">    …</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>



<p>That&#8217;s fine, right &#8211; every room has a <code>Monster</code>.  Simple.</p>



<p>Except it&#8217;s not, if <code>Monster</code> is a <code>@Model</code>.  In that case, despite the fact you&#8217;ve declared it as non-optional, it actually is optional.  <code>@Model</code> essentially makes it implicitly unwrapped without telling you (and without the important <code>!</code> character after the type to warn you).</p>



<p>The technical reason it&#8217;s implicitly and unavoidably optional is because <code>Room</code> and <code>Monster</code> are stored in separate SQLite tables, with a nullable foreign key relationship between them.  SwiftData does not support non-nullable foreign keys.  So it&#8217;s totally valid, as far as the actual database layer and persistent storage are concerned, for the relevant <code>Monster</code> to disappear entirely.  But that means your model instance is basically corrupt when you load it, which can have a variety of ill-defined effects such as crashing your app.</p>



<p>You might think this is largely hypothetical, or academic &#8211; SwiftData&#8217;s not going to erroneously delete your <code>Monster</code> out from under the <code>Room</code> (you assume), and you&#8217;re not going to do it yourself, so it&#8217;ll never happen, right?  Well, maybe.  Consider what happens as your app evolves, undergoes refactors, etc.  It&#8217;s quite possible you unwittingly create a situation in which what <em>was</em> previously valid data is no longer valid to your app.</p>



<p>Or, you might run into a SwiftData design flaw or bug which causes the data to be corrupted on save, such as is detailed in the next section.  Even if the root cause is that your data was corrupted at save time, it&#8217;s frustrating to have that manifest only through obtuse crashes at <em>some ill-defined time</em> in the future after your app is relaunched.</p>



<h4 class="wp-block-heading">Workarounds</h4>



<p>None, really.</p>



<p>At the very least, you can explicitly make <em>all</em> relationships optional.  That way you can code defensively around them, by putting in appropriate unwrapping code and guards.  If nothing else you can at the very least crash with a clear failure message (there is no log message at all when SwiftData crashes your app via implicit unwrapping).</p>



<h3 class="wp-block-heading" id="you-cannot-directly-initialise-bidirectional-relationship-properties">You cannot directly initialise bidirectional relationship properties</h3>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">@Model</span></span>
<span class="line"><span style="color: #0000FF">final</span><span style="color: #000000"> </span><span style="color: #0000FF">class</span><span style="color: #000000"> </span><span style="color: #267F99">House</span><span style="color: #000000"> {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">@Relationship</span><span style="color: #000000">(deleteRule: .</span><span style="color: #001080">cascade</span><span style="color: #000000">,</span></span>
<span class="line"><span style="color: #000000">                  inverse: \Floor.</span><span style="color: #001080">house</span><span style="color: #000000">)</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">let</span><span style="color: #000000"> floors: [Floor]</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">init</span><span style="color: #000000">(</span><span style="color: #795E26">floors</span><span style="color: #000000">: [Floor]) {</span></span>
<span class="line"><span style="color: #000000">        </span><span style="color: #0000FF">self</span><span style="color: #000000">.</span><span style="color: #001080">floors</span><span style="color: #000000"> = floors</span></span>
<span class="line"><span style="color: #000000">    }</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>



<p>If you do something like the above &#8211; making use of Xcode to generate your initialiser for you, like you normally would for classes &#8211; you&#8217;ll get the wonderful behaviour that everything works fine until you quit your app and reload it.  Then you&#8217;ll discover that <code>floors</code> is empty.</p>



<p>There are no error messages, neither at compile time nor runtime.  No warnings.  <em>Nothing</em>.</p>



<p>Looking at the underlying SQLite database, its apparent that <em>something</em> is seriously broken because while it <em>does</em> save the <code>Floor</code> instances, their foreign key back to <code>House</code> is <code>NULL</code> (even if logically that should be impossible &#8211; refer back to the prior point on how <a href="#relationships-are-always-optional" data-type="internal" data-id="#relationships-are-always-optional">SwiftData forces <em>all</em> relationships to be optional</a>).</p>



<p>What&#8217;s happening is that the initialisation of <code>floors</code> in <code>init</code> is in some sense bypassing SwiftData&#8217;s custom hooks; it&#8217;s &#8220;directly&#8221; setting the value in memory without SwiftData updating its corresponding Core Data model underneath (this is another place SwiftData&#8217;s abstraction is leaky &#8211; your <code>@Model</code> classes aren&#8217;t the <em>real</em> model classes, they&#8217;re merely a layer atop the <em>actual</em> Core Data models).</p>



<h4 class="wp-block-heading">Workarounds</h4>



<p>Thankfully this <em>does</em> have a simple workaround (iff you know it and can remember to always use it):  never <em>assign</em> the relationship in <code>init</code>.  You can <em>append</em> to it, and you can assign it <em>outside</em> of <code>init</code>.  So e.g.:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">@Model</span></span>
<span class="line"><span style="color: #0000FF">final</span><span style="color: #000000"> </span><span style="color: #0000FF">class</span><span style="color: #000000"> </span><span style="color: #267F99">House</span><span style="color: #000000"> {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">@Relationship</span><span style="color: #000000">(deleteRule: .</span><span style="color: #001080">cascade</span><span style="color: #000000">,</span></span>
<span class="line"><span style="color: #000000">                  inverse: \Floor.</span><span style="color: #001080">house</span><span style="color: #000000">)</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">let</span><span style="color: #000000"> floors: [Floor] = []</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">init</span><span style="color: #000000">(</span><span style="color: #795E26">floors</span><span style="color: #000000">: [Floor]) {</span></span>
<span class="line"><span style="color: #000000">        </span><span style="color: #0000FF">self</span><span style="color: #000000">.</span><span style="color: #001080">floors</span><span style="color: #000000">.</span><span style="color: #795E26">append</span><span style="color: #000000">(</span><span style="color: #795E26">contentsOf</span><span style="color: #000000">: floors)</span></span>
<span class="line"><span style="color: #000000">    }</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>



<h3 class="wp-block-heading">Relationship constraints are only enforced at <em>save</em> time</h3>



<p><code>@Relationship</code> lets you specify the arity of a relationship &#8211; the minimum and maximum number of related objects that are permitted.  However, this is not actually enforced at any time <em>except</em> save time (when you <em>explicitly</em> call <code>context.save()</code>).</p>



<p>Arguably this is better than nothing &#8211; vanilla <code>Array</code> member variables provide no way to constrain the array&#8217;s contents &#8211; but it can lead to mistakes if you unwittingly rely on more rigorous enforcement.</p>



<h4 class="wp-block-heading">Workarounds</h4>



<p>None, really, that I&#8217;ve been able to come up with.</p>



<p>It sort of helps to think of these &#8216;constraints&#8217; as actually just &#8216;guidelines&#8217;; to not <em>actually</em> rely on them being enforced.  That may mean you have to do manual validation, that&#8217;s in principle redundant, in order to ensure the rules are enforced.</p>



<p>That said, since you need to manually save aggressively anyway (since <a href="#autosave-does-not-work">auto-save doesn&#8217;t work</a>), you might be able to conjoin the two workarounds.</p>



<h3 class="wp-block-heading">Singletons are unsupported</h3>



<p>In some use-cases your root object is a singleton &#8211; you don&#8217;t ever actually want to have multiple instances of it, it&#8217;s merely the top-level of your object graph, the object which organises all other objects.</p>



<p>Unfortunately SwiftData doesn&#8217;t support this.  Every <code>@Model</code> class can have multiple instances.</p>



<p>This can make things pretty awkward when dealing with your singleton, as you cannot do intuitive and simple things like:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">struct</span><span style="color: #000000"> </span><span style="color: #267F99">Map</span><span style="color: #000000">: View {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">@Environment</span><span style="color: #000000">(\.</span><span style="color: #001080">modelContext</span><span style="color: #000000">) </span><span style="color: #0000FF">private</span><span style="color: #000000"> </span><span style="color: #0000FF">var</span><span style="color: #000000"> context</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">@Query</span><span style="color: #000000"> </span><span style="color: #0000FF">var</span><span style="color: #000000"> house: House</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">var</span><span style="color: #000000"> body: some View {</span></span>
<span class="line"><span style="color: #000000">        VStack {</span></span>
<span class="line"><span style="color: #000000">            </span><span style="color: #795E26">ForEach</span><span style="color: #000000">(house.</span><span style="color: #001080">floors</span><span style="color: #000000">) {</span></span>
<span class="line"><span style="color: #000000">                …</span></span>
<span class="line"><span style="color: #000000">            }</span></span>
<span class="line"><span style="color: #000000">        }</span></span>
<span class="line"><span style="color: #000000">    }</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>



<h4 class="wp-block-heading">Workarounds</h4>



<p>Basically you have three choices:</p>



<ul class="wp-block-list">
<li>Remove all your singletons, redesigning your model and app to accomodate.  This <em>might</em> make some contrived sense if you rationalise it as a way to have multiple game sessions concurrently, or to facilitate concurrent unit testing, or somesuch.  But you&#8217;ll know in your heart that it&#8217;s not real.</li>



<li>Use force-unwrapping (or equivalent), making your app crash or otherwise fail if your singleton expectations are violated by SwiftData.</li>



<li>Silently ignore all but an arbitrary instance of the model class in question.</li>
</ul>



<p>These are all pretty flawed.  And no matter which approach you choose, they introduce some significant boilerplate into your code.  e.g.:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">struct</span><span style="color: #000000"> </span><span style="color: #267F99">Map</span><span style="color: #000000">: View {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">@Environment</span><span style="color: #000000">(\.</span><span style="color: #001080">modelContext</span><span style="color: #000000">) </span><span style="color: #0000FF">private</span><span style="color: #000000"> </span><span style="color: #0000FF">var</span><span style="color: #000000"> context</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">@Query</span><span style="color: #000000"> </span><span style="color: #0000FF">var</span><span style="color: #000000"> houses: [House]</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">var</span><span style="color: #000000"> body: some View {</span></span>
<span class="line"><span style="color: #000000">        VStack {</span></span>
<span class="line"><span style="color: #000000">            </span><span style="color: #795E26">ForEach</span><span style="color: #000000">(house[</span><span style="color: #098658">0</span><span style="color: #000000">].</span><span style="color: #001080">floors</span><span style="color: #000000">) { </span><span style="color: #008000">// Silently ignore other houses.</span></span>
<span class="line"><span style="color: #000000">                …</span></span>
<span class="line"><span style="color: #000000">            }</span></span>
<span class="line"><span style="color: #000000">        }</span></span>
<span class="line"><span style="color: #000000">    }</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>



<p>In the above example, which house is chosen is arbitrary and may change between view updates.  You <em>can</em> do extra work to pick a house deterministically &#8211; e.g. sort by some unique attribute and pick the first &#8211; and you probably <em>should</em> if you&#8217;re going to use this hack, but you&#8217;ll still be operating in a messed-up state.</p>



<h3 class="wp-block-heading">You cannot create model objects outside of SwiftData contexts</h3>



<p>This limitation is explicable and reasonable on the face of it, but does limit your app architecture in ways that can be annoying.  e.g. you cannot initialise your views with test models like you normally would:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #000000">#Preview {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #795E26">MapView</span><span style="color: #000000">(</span><span style="color: #795E26">house</span><span style="color: #000000">: House.</span><span style="color: #795E26">default</span><span style="color: #000000">())</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>



<p>That will compile without any complaints, but when you try to actually view the preview in Xcode you&#8217;ll get the dreaded &#8220;Cannot preview in this file&#8221; error, with absolutely no indication why.</p>



<p><em>If</em> you happen to try a similar thing in your app initialisation, e.g.:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">@main</span></span>
<span class="line"><span style="color: #0000FF">struct</span><span style="color: #000000"> </span><span style="color: #267F99">Game</span><span style="color: #000000">: App {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">var</span><span style="color: #000000"> body: some Scene {</span></span>
<span class="line"><span style="color: #000000">        WindowGroup {</span></span>
<span class="line"><span style="color: #000000">            </span><span style="color: #795E26">Overview</span><span style="color: #000000">(</span><span style="color: #795E26">house</span><span style="color: #000000">: House.</span><span style="color: #795E26">default</span><span style="color: #000000">())</span></span>
<span class="line"><span style="color: #000000">                .</span><span style="color: #795E26">modelContainer</span><span style="color: #000000">(</span><span style="color: #795E26">for</span><span style="color: #000000">: [House.self])</span></span>
<span class="line"><span style="color: #000000">        }</span></span>
<span class="line"><span style="color: #000000">    }</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>



<p>…then you can at least run your app and will see an error message before it crashes, like:</p>



<pre class="wp-block-preformatted">💣 SwiftData/ModelContainer.swift:144: Fatal error: failed to find a currently active container for Room
Failed to find any currently loaded container for Room) [sic]</pre>



<p>So far as Apple error messages go, this is practically perfect &#8211; it at least does mention <em>almost</em> the pertinent object (the [SwiftData] container is <em>similar</em> to the SwiftData context) and provide some explanation of the genuine problem.</p>



<h4 class="wp-block-heading">Workarounds</h4>



<p>I haven&#8217;t explored this in great detail, so there might be other options, but the best I could promptly come up with is to move initialisation inside your SwiftUI views.  Whether that be through explicit user interaction (e.g. a &#8220;New Game&#8221; GUI in my use case, when there&#8217;s no existing <code>House</code> in the database), or in a suitable <code>onAppear</code> handler, etc.</p>



<p>That&#8217;s reasonable for actual app execution, but doesn&#8217;t immediately help you for SwiftUI previews.  However, if you attach a suitable model container to your preview instance:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #000000">#Preview {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #795E26">MapView</span><span style="color: #000000">()</span></span>
<span class="line"><span style="color: #000000">        .</span><span style="color: #795E26">modelContainer</span><span style="color: #000000">(</span><span style="color: #795E26">for</span><span style="color: #000000">: [House.self])</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>



<p>…then your preview will actually use the real app data.  This can actually be handy sometimes, as you can manipulate the state &#8211; either in the preview or your actual app execution &#8211; which can be handy for debugging view problems (e.g. you&#8217;re running your app, playing around, and you notice a rendering error &#8211; you can quit your app and its saved state will be used in your Xcode previews, automatically showing you the view in its broken state).</p>



<p>Alternatively, you can go through a more laborious process of [more-]manually creating the SwiftData context, in order to allow you to use a <em>distinct</em> database and to create test objects, e.g.:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #000000">#Preview {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">let</span><span style="color: #000000"> container = </span><span style="color: #AF00DB">try</span><span style="color: #000000">! </span><span style="color: #795E26">ModelContainer</span><span style="color: #000000">(</span><span style="color: #795E26">for</span><span style="color: #000000">: House.self,</span></span>
<span class="line"><span style="color: #000000">                                         </span><span style="color: #795E26">configurations</span><span style="color: #000000">: </span><span style="color: #795E26">ModelConfiguration</span><span style="color: #000000">(</span><span style="color: #795E26">isStoredInMemoryOnly</span><span style="color: #000000">: </span><span style="color: #0000FF">true</span><span style="color: #000000">))</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #795E26">MapView</span><span style="color: #000000">(</span><span style="color: #795E26">house</span><span style="color: #000000">: House.</span><span style="color: #795E26">forPreviewing</span><span style="color: #000000">())</span></span>
<span class="line"><span style="color: #000000">        .</span><span style="color: #795E26">modelContainer</span><span style="color: #000000">(container)</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>



<p>I <em>believe</em> using an in-memory-only store will prevent it touching your actual app&#8217;s saved state, but I haven&#8217;t poked at it much.  I also believe you can use <a href="https://developer.apple.com/documentation/swiftdata/modelconfiguration/init(_:schema:url:allowssave:cloudkitdatabase:)" data-wpel-link="external" target="_blank" rel="external noopener">the more complicated configuration initialiser</a> to explicitly specify a URL, if you want a <em>persistent</em> test dataset that&#8217;s nonetheless separate from your app&#8217;s real dataset, but I haven&#8217;t played with it myself.</p>



<p>There are various tutorials online that go into more detail, e.g. <a href="https://www.hackingwithswift.com/about" data-wpel-link="external" target="_blank" rel="external noopener">Paul Hudson</a>&#8216;s <a href="https://www.hackingwithswift.com/quick-start/swiftdata/how-to-use-swiftdata-in-swiftui-previews" data-type="link" data-id="https://www.hackingwithswift.com/quick-start/swiftdata/how-to-use-swiftdata-in-swiftui-previews" data-wpel-link="external" target="_blank" rel="external noopener">How to use SwiftData in SwiftUI previews</a>.</p>



<h3 class="wp-block-heading">Apple&#8217;s documentation is wrong</h3>



<p>This one barely rates a mention since Apple&#8217;s documentation is pretty infamously unreliable (what little of it exists at all).  Still, it&#8217;s important to point out a couple of particularly glaring errors that will hit most SwiftData newbies:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">@Model</span></span>
<span class="line"><span style="color: #0000FF">class</span><span style="color: #000000"> </span><span style="color: #267F99">Trip</span><span style="color: #000000"> {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">var</span><span style="color: #000000"> name: </span><span style="color: #267F99">String</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">var</span><span style="color: #000000"> destination: </span><span style="color: #267F99">String</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">var</span><span style="color: #000000"> startDate: Date</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">var</span><span style="color: #000000"> endDate: Date</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">var</span><span style="color: #000000"> accommodation: Accommodation?</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>
<cite>Apple&#8217;s <a href="https://developer.apple.com/documentation/swiftdata/preserving-your-apps-model-data-across-launches" data-type="link" data-id="https://developer.apple.com/documentation/swiftdata/preserving-your-apps-model-data-across-launches" data-wpel-link="external" target="_blank" rel="external noopener">Preserving your app’s model data across launches</a></cite></blockquote>



<p>That is Apple&#8217;s <em>very first</em> code example in introducing SwiftData, and it&#8217;s wrong.  It has no initialiser, therefore you cannot actually use this <code>Trip</code> class.</p>



<p>Now, you might say that&#8217;s pedantic, but it&#8217;s actually quite pertinent &#8211; Apple <em>never</em> show a complete example of a <code>@Model</code> class, so they never show you how you&#8217;re supposed to initialise one, and thus they never even try to prevent people falling into pits such as <a href="#you-cannot-directly-initialise-bidirectional-relationship-properties">the aforementioned issues with arrays</a>.</p>



<p>Another poignant example:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">@Relationship</span><span style="color: #000000">(.</span><span style="color: #001080">cascade</span><span style="color: #000000">) </span><span style="color: #0000FF">var</span><span style="color: #000000"> accommodation: Accommodation?</span></span></code></pre></div>
<cite>Apple&#8217;s <a href="https://developer.apple.com/documentation/swiftdata/preserving-your-apps-model-data-across-launches" data-type="link" data-id="https://developer.apple.com/documentation/swiftdata/preserving-your-apps-model-data-across-launches" data-wpel-link="external" target="_blank" rel="external noopener">Preserving your app’s model data across launches</a></cite></blockquote>



<p>That&#8217;s not valid; it doesn&#8217;t even compile.  The syntax <em>actually</em> is:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">@Relationship</span><span style="color: #000000">(deleteRule: .</span><span style="color: #001080">cascade</span><span style="color: #000000">) </span><span style="color: #0000FF">var</span><span style="color: #000000"> accommodation: Accommodation?</span></span></code></pre></div>



<h4 class="wp-block-heading">Workarounds</h4>



<p>There are quite a few tutorials available online which provide an alternative onboarding guide, to Apple&#8217;s official documentation.  I did find a broad perusal of them useful in gleaning bits and pieces that Apple themselves don&#8217;t cover (or are wrong about).  Unfortunately many are essentially just copy-pastes or paraphrases of Apple&#8217;s documentation, and even those that aren&#8217;t tend to make the same mistakes.</p>



<p>But, since you&#8217;ve now read this post, you&#8217;re better equipped than most to try diving into SwiftData.</p>



<h2 class="wp-block-heading">Non-pitfalls</h2>



<p>Just as a bit of a bonus section, I want to call out one aspect of SwiftData which <em>actually</em> works as you might expect.</p>



<h3 class="wp-block-heading">You only need to register the root(s) of your model graph</h3>



<p>When setting up your model container, you don&#8217;t have to manually enumerate <em>every</em> model object you use, merely some root(s).  SwiftData automatically walks your class(es) member variables to find relationships to other <code>@Model</code> classes, and implicitly registers those too.  So e.g.:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">@main</span></span>
<span class="line"><span style="color: #0000FF">struct</span><span style="color: #000000"> </span><span style="color: #267F99">Game</span><span style="color: #000000">: App {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">var</span><span style="color: #000000"> body: some Scene {</span></span>
<span class="line"><span style="color: #000000">        WindowGroup {</span></span>
<span class="line"><span style="color: #000000">            </span><span style="color: #795E26">Map</span><span style="color: #000000">()</span></span>
<span class="line"><span style="color: #000000">                .</span><span style="color: #795E26">modelContainer</span><span style="color: #000000">(</span><span style="color: #795E26">for</span><span style="color: #000000">: [House.self])</span></span>
<span class="line"><span style="color: #000000">        }</span></span>
<span class="line"><span style="color: #000000">    }</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>



<p>You don&#8217;t need to list <code>Floor.self</code>, <code>Room.self</code>, etc.  You <em>can</em> &#8211; it doesn&#8217;t hurt anything, and might make your app a little more robust against future refactors &#8211; but it&#8217;s nice that you don&#8217;t have to.</p>



<h2 class="wp-block-heading">Bonus: TablePlus recommendation</h2>



<p>It&#8217;s unfortunately critical, when working with SwiftData (or Core Data), to be able to view &amp; edit the underlying SQLite database.  You can do that out-of-the-box on your Mac using just the built-in <code>sqlite3</code> CLI, but that&#8217;s pretty awkward.</p>



<p>There are <em>many</em> GUI apps for working with SQLite databases.  Quite a few are free.  Most are functional but clumsy.</p>



<p>Years ago &#8211; when I was mostly working with MySQL &#8211; I came across <a href="https://tableplus.com" data-type="link" data-id="https://tableplus.com" data-wpel-link="external" target="_blank" rel="external noopener">TablePlus</a>.  <a href="https://tableplus.com/pricing" data-wpel-link="external" target="_blank" rel="external noopener">It&#8217;s a bit pricey</a> at $90 up front &#8211; plus $50 per subsequent year if you want to keep getting software updates &#8211; but it&#8217;s by far the best database client I&#8217;ve found, on any platform.  It&#8217;s easily the most native-feeling on a Mac, even though it&#8217;s cross-platform (macOS, iOS / iPadOS, Linux, and Windows!), and one of the fastest.  It doesn&#8217;t <em>quite</em> have all the features &#8211; I do sometimes fire up <a href="https://www.mysql.com/products/workbench/" data-type="link" data-id="https://www.mysql.com/products/workbench/" data-wpel-link="external" target="_blank" rel="external noopener">MySQL Workbench</a> to access its query performance visualisation &#8211; but it covers 99% of what I need.</p>



<p>My only notable complaint with it is that it has historically been a tad buggy.  Not dramatically, but consistently.  That said, I only just got back into it as a result of this SwiftData work &#8211; and it&#8217;s worked flawlessly so far &#8211; so it may well have upped its game since I last properly used it a couple of years ago. 🤞</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/swiftdata-pitfalls/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">5378</post-id>	</item>
		<item>
		<title>SQLite table constraints must be specified after all columns</title>
		<link>https://wadetregaskis.com/sqlite-table-constraints-must-be-specified-after-all-columns/</link>
					<comments>https://wadetregaskis.com/sqlite-table-constraints-must-be-specified-after-all-columns/#respond</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Sat, 03 Dec 2016 03:53:25 +0000</pubDate>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[SQLite]]></category>
		<category><![CDATA[Useless error message]]></category>
		<guid isPermaLink="false">https://blog.wadetregaskis.com/?p=3747</guid>

					<description><![CDATA[Marking this one for future reference. TL;DR:  You have to specify all the columns in your table, in a CREATE TABLE statement, before you specify table constraints like primary or foreign keys. SQLite gives the most useless error messages most of the time.  Case in point: CREATE TABLE "Foo" ( "ColumnA" TEXT NOT NULL UNIQUE, "ColumnB"&#8230; <a class="read-more-link" href="https://wadetregaskis.com/sqlite-table-constraints-must-be-specified-after-all-columns/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[<p>Marking this one for future reference.</p>
<p>TL;DR:  You have to specify all the columns in your table, in a CREATE TABLE statement, <em>before</em> you specify table constraints like primary or foreign keys.</p>
<p>SQLite gives the most useless error messages most of the time.  Case in point:</p>
<pre><span style="color: #003300;"><strong>CREATE TABLE</strong></span> "Foo" (
    "ColumnA" <span style="color: #003300;"><strong>TEXT NOT NULL UNIQUE</strong></span>,
    "ColumnB" <span style="color: #003300;"><strong>TEXT</strong></span>)
<span style="color: #003300;"><strong>CREATE TABLE</strong></span> "Bar" (
    "Column0" <span style="color: #003300;"><strong>TEXT NOT NULL</strong></span>,
    <span style="color: #003300;"><strong>PRIMARY KEY</strong></span> ("Column0"),
    "Column1" <span style="color: #003300;"><strong>INTEGER NOT NULL UNIQUE</strong></span>,
    <span style="color: #003300;"><strong>FOREIGN KEY</strong></span> ("Column1") <span style="color: #003300;"><strong>REFERENCES</strong></span> "Foo" ("ROWID"))</pre>
<p>That yields the <em>incredibly</em> helpful error message:</p>
<blockquote><p>near &#8220;&#8221;Column1&#8243;&#8221;: syntax error</p></blockquote>
<p>Cool story bro.</p>
<p>What it&#8217;s trying to say is that it wasn&#8217;t expecting any more column definitions after that PRIMARY KEY table constraint.  If you simply declare Column1 before the PRIMARY KEY, it works just fine.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/sqlite-table-constraints-must-be-specified-after-all-columns/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3747</post-id>	</item>
	</channel>
</rss>
