From Core Data to SwiftData: A Backend Developer's Perspective
January 25, 2026 · 5 min read
Swift, iOS, SwiftData, Core Data, Mobile Development
I'm primarily a C# and Azure developer. When I started building my nutrition tracking iOS app as a side project, the persistence layer was my biggest struggle. Coming from Entity Framework and SQL Server, Core Data felt alien. Last weekend, I migrated everything to SwiftData, and it finally feels like home.
My Core Data Struggles
Let me be real - I never fully understood Core Data. I got it working, but I always felt like I was cargo-culting code from Stack Overflow. The terminology alone was confusing: managed object contexts, persistent store coordinators, fetch requests with string-based predicates. None of it mapped to anything I knew from the .NET world.
In C#, I'd write:
var meals = await _context.Meals
.Where(m => m.UserId == userId && !m.IsDeleted)
.OrderByDescending(m => m.ConsumedAt)
.ToListAsync();
In Core Data, I was writing:
let request: NSFetchRequest<MealEntity> = MealEntity.fetchRequest()
request.predicate = NSPredicate(
format: "userId == %@ AND isMarkedDeleted == NO",
userId as CVarArg
)
request.sortDescriptors = [NSSortDescriptor(key: "consumedAt", ascending: false)]
return try viewContext.fetch(request)
That string-based predicate gave me anxiety. One typo and it crashes at runtime. No IntelliSense, no compiler help. It felt like writing SQL in strings - something I'd never do in C#.
Why SwiftData Clicked
When I saw SwiftData's #Predicate macro, something clicked. Look at this:
let predicate = #Predicate<Meal> { meal in
meal.userId == userId && meal.isMarkedDeleted == false
}
That's a lambda! That's basically the same mental model as LINQ. The compiler checks it. I get autocomplete. If I typo a property name, it won't even build.
The @Model macro reminded me of Entity Framework's entity classes:
@Model
final class Meal {
var id: UUID
var userId: UUID
var name: String
var consumedAt: Date
@Relationship(deleteRule: .cascade)
var ingredients: [MealIngredient]?
}
Compare that to EF Core:
public class Meal
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
public string Name { get; set; }
public DateTime ConsumedAt { get; set; }
public List<MealIngredient> Ingredients { get; set; }
}
Almost the same thing! The mental overhead dropped significantly.
The Migration Weekend
I had 21 entities in Core Data. Meals, ingredients, glucose readings, recipes, health tracking - the works. I was dreading this migration for months.
Here's the thing: it took a weekend, but most of that time was me being overly cautious. Once I understood the pattern, each entity took maybe 15 minutes to convert.
The hardest part wasn't SwiftData - it was letting go of Core Data patterns I'd memorized but never understood. I kept looking for the equivalent of NSManagedObjectContext. Turns out, SwiftData's ModelContext is simpler and does what you'd expect.
What Surprised Me
No schema file. Core Data has this .xcdatamodeld file that's basically XML you edit through a GUI. SwiftData just uses your Swift code as the source of truth. Coming from EF Core's code-first approach, this made way more sense.
Relationships are just arrays. In Core Data, to-many relationships are NSSet and you need generated accessor methods. In SwiftData, it's just a Swift array. Append, remove, iterate - normal stuff.
The context is injected. SwiftUI automatically provides the model context through the environment. It's like dependency injection, which I'm very used to from .NET.
Things I Had to Work Around
Not everything maps perfectly:
No case-insensitive contains in predicates. I had to fetch and filter in memory for search. Annoying, but manageable.
Arrays of strings aren't native. I store tags as JSON strings with computed properties. Works fine, just not as elegant as I'd like.
Everything's on MainActor by default. SwiftData really wants you on the main thread. Coming from async/await in C#, I had to adjust my thinking about concurrency.
The Result
My data layer went from "code I'm afraid to touch" to "code I actually understand." That's huge for a side project where I might not look at this code for weeks.
The patterns finally feel familiar:
- Models are just classes with decorators
- Queries use lambdas with type safety
- The context is injected where needed
- Relationships are native collections
If you're a backend developer dipping into iOS, SwiftData is the persistence layer you've been waiting for. It won't feel exactly like Entity Framework, but it's close enough that your existing mental models transfer.
My Advice
-
Learn SwiftData first if you're new to iOS. Don't bother with Core Data unless you're maintaining legacy code. SwiftData is the modern path.
-
Think of
@Modellike an EF entity. The decorator approach is familiar if you've used data annotations. -
#Predicateis basically LINQ. Write it like a Where clause and you'll be fine. -
Trust the automatic saves. SwiftData handles persistence. You don't need to call save after every change.
Now I have 9 more repositories to migrate, but I'm actually looking forward to it. That's not something I ever said about Core Data.