{"id":51217,"date":"2026-03-11T16:20:35","date_gmt":"2026-03-11T20:20:35","guid":{"rendered":"https:\/\/mjtsai.com\/blog\/?p=51217"},"modified":"2026-06-04T10:55:20","modified_gmt":"2026-06-04T14:55:20","slug":"coredataevolution","status":"publish","type":"post","link":"https:\/\/mjtsai.com\/blog\/2026\/03\/11\/coredataevolution\/","title":{"rendered":"CoreDataEvolution"},"content":{"rendered":"<p><a href=\"https:\/\/github.com\/fatbobman\/CoreDataEvolution\">Fatbobman<\/a> (<a href=\"https:\/\/mastodon.social\/@fatbobman\/116173553595030634\">Mastoson<\/a>):<\/p>\n<blockquote cite=\"https:\/\/github.com\/fatbobman\/CoreDataEvolution\">\n<p>This library is designed to simplify and enhance Core Data&rsquo;s handling of multithreading, drawing inspiration from SwiftData&rsquo;s <code>@ModelActor<\/code> feature, enabling efficient, safe, and scalable operations.<\/p>\n<p>[&#8230;]<\/p>\n<ul><li><p><strong>Custom Executors for Core Data Actors<\/strong><br\/>\nCoreDataEvolution provides custom executors that ensure all operations on managed objects are performed on the appropriate thread associated with their managed object context. It uses a <code>UnownedJob<\/code>-based serial executor path compatible with the minimum supported OS versions.<\/p>\n<\/li>\n<li>\n<p><strong>@NSModelActor Macro<\/strong><br\/>\nThe <code>@NSModelActor<\/code> macro simplifies Core Data concurrency, mirroring SwiftData&rsquo;s <code>@ModelActor<\/code> macro. It generates the necessary boilerplate code to manage a Core Data stack within an actor, ensuring safe and efficient access to managed objects.<\/p>\n<\/li>\n<li>\n<p><strong>NSMainModelActor Macro<\/strong><br\/>\n<code>NSMainModelActor<\/code> is the main-thread companion macro for classes. It binds <code>modelContext<\/code> to <code>viewContext<\/code> and provides the same convenience access APIs (<code>subscript<\/code>, <code>withContext<\/code>) through <code>NSMainModelActor<\/code> protocol extensions.<\/p>\n<\/li>\n<li>\n<p><strong>Elegant Actor-based Concurrency<\/strong><br\/>\nCoreDataEvolution allows you to create actors with custom executors tied to Core Data contexts, ensuring that all operations within the actor are executed serially on the context&rsquo;s thread.<\/p><\/li><\/ul>\n<\/blockquote>\n\n<p>Previously:<\/p>\n<ul>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2025\/08\/05\/macos-tahoe-26-developer-beta-5\/\">macOS Tahoe 26 Developer Beta 5<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2024\/04\/23\/elegant-concurrency-operations-in-core-data\/\">Elegant Concurrency Operations in Core Data<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2023\/10\/02\/model-for-coredata\/\">@Model for CoreData<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2015\/12\/02\/generating-core-data-swift\/\">Generating Core Data Swift<\/a><\/li>\n<\/ul>\n\n<p id=\"coredataevolution-update-2026-03-19\">Update (<a href=\"#coredataevolution-update-2026-03-19\">2026-03-19<\/a>): <a href=\"https:\/\/fatbobman.com\/en\/posts\/cde-an-attempt-to-make-core-data-feel-more-like-modern-swift\/\">Fatbobman<\/a>:<\/p>\n<blockquote cite=\"https:\/\/fatbobman.com\/en\/posts\/cde-an-attempt-to-make-core-data-feel-more-like-modern-swift\/\">\n<p>More precisely, it&rsquo;s my own answer to these disconnects: If I still value Core Data&rsquo;s object graph model, its migration system, and its mature runtime capabilities, can I make it continue to exist in modern Swift projects in a more natural way?<\/p>\n<p>[&#8230;]<\/p>\n<p>But in the era of cloud sync, Core Data projects often encounter two other very real needs: how to handle continuously changing transaction histories, and how to monitor iCloud \/ CloudKit&rsquo;s operational status.<\/p>\n<p>To that end, I&rsquo;ve also built two complementary tools: <a href=\"https:\/\/github.com\/fatbobman\/PersistentHistoryTrackingKit?utm_source=Fatbobman%20Blog&amp;utm_medium=web\">PersistentHistoryTrackingKit<\/a> and <a href=\"https:\/\/github.com\/fatbobman\/iCloudSyncStatusKit?utm_source=Fatbobman%20Blog&amp;utm_medium=web\">iCloudSyncStatusKit<\/a>.<\/p>\n<\/blockquote>\n\n<p id=\"coredataevolution-update-2026-06-04\">Update (<a href=\"#coredataevolution-update-2026-06-04\">2026-06-04<\/a>): <a href=\"https:\/\/fatbobman.com\/en\/posts\/core-data-observation-freer-mental-model\/\">Fatbobman<\/a>:<\/p>\n<blockquote cite=\"https:\/\/fatbobman.com\/en\/posts\/core-data-observation-freer-mental-model\/\">\n<p>The point here is not just syntactic simplicity. More importantly, property reads along the relationship chain can still participate in Observation tracking. The framework knows what the view has read, and therefore what it depends on. Developers do not need to manually split out a series of intermediate views just to preserve reactivity.<\/p>\n<p>With Core Data, however, things become much more cumbersome. If you want to use data along a relationship chain in a single view while still preserving real-time responsiveness to changes in that data, you typically need to split each <code>NSManagedObject<\/code> along the chain into its own <code>struct View<\/code> and hold it with <code>@ObservedObject<\/code>. Otherwise, changes in some intermediate objects may not be accurately propagated to the final view.<\/p>\n<p>[&#8230;]<\/p>\n<p>For this reason, I decided to fill this capability gap in CDE: to let <code>NSManagedObject<\/code> provide property-level observation capabilities similar to SwiftData&rsquo;s <code>PersistentModel<\/code>. Most importantly, views can respond directly to changes in the data they read without using <code>@ObservedObject<\/code>.<\/p>\n<\/blockquote>","protected":false},"excerpt":{"rendered":"<p>Fatbobman (Mastoson): This library is designed to simplify and enhance Core Data&rsquo;s handling of multithreading, drawing inspiration from SwiftData&rsquo;s @ModelActor feature, enabling efficient, safe, and scalable operations. [&#8230;] Custom Executors for Core Data Actors CoreDataEvolution provides custom executors that ensure all operations on managed objects are performed on the appropriate thread associated with their managed [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"apple_news_api_created_at":"2026-03-11T20:20:39Z","apple_news_api_id":"5c0d9899-5dd8-4591-a523-65360d9a4516","apple_news_api_modified_at":"2026-06-04T14:55:24Z","apple_news_api_revision":"AAAAAAAAAAAAAAAAAAAAAQ==","apple_news_api_share_url":"https:\/\/apple.news\/AXA2YmV3YRZGlI2U2DZpFFg","apple_news_coverimage":0,"apple_news_coverimage_caption":"","apple_news_is_hidden":false,"apple_news_is_paid":false,"apple_news_is_preview":false,"apple_news_is_sponsored":false,"apple_news_maturity_rating":"","apple_news_metadata":"\"\"","apple_news_pullquote":"","apple_news_pullquote_position":"","apple_news_slug":"","apple_news_sections":"\"\"","apple_news_suppress_video_url":false,"apple_news_use_image_component":false,"footnotes":""},"categories":[4],"tags":[109,2741,30,2742,941,74,71,2200,901],"class_list":["post-51217","post","type-post","status-publish","format-standard","hentry","category-programming-category","tag-coredata","tag-ios-26","tag-mac","tag-macos-tahoe-26","tag-macros","tag-opensource","tag-programming","tag-swift-concurrency","tag-swift-programming-language"],"apple_news_notices":[],"_links":{"self":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/51217","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/comments?post=51217"}],"version-history":[{"count":3,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/51217\/revisions"}],"predecessor-version":[{"id":52090,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/51217\/revisions\/52090"}],"wp:attachment":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/media?parent=51217"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/categories?post=51217"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/tags?post=51217"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}