Agent skills for iOS, iPadOS, Swift, SwiftUI, and modern Apple framework development.
80
100%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Advisory
Suggest reviewing before use
Guidance for using SwiftData alongside an existing Core Data store and for
planning a transition from Core Data to SwiftData. For apps that are staying on
Core Data without SwiftData, use the sibling core-data skill instead.
When a request says "Core Data" but the actual work is shared-store
coexistence, gradual screen migration to @Model, or mapping a .xcdatamodeld
to SwiftData types, route to this SwiftData skill. Answer only the high-level
boundary first; do not turn the response into a full SwiftData migration plan
unless the user asks for implementation detail.
Apple's current coexistence sample is documented as iOS 27 / Xcode 27 beta. Use it as source-grounded migration guidance, not as an unconditional iOS 26 platform guarantee. For shipping work on earlier SDKs, verify the exact deployment target and test against copies of real stores.
Docs: Adopting SwiftData for a Core Data app
Both stacks must point to the same SQLite file and agree on the schema. The
Core Data .xcdatamodeld and SwiftData @Model classes must describe the
same entities and properties.
import SwiftData
import CoreData
// 1. Determine the store URL that Core Data already uses
let storeURL = NSPersistentContainer.defaultDirectoryURL()
.appendingPathComponent("MyAppModel.sqlite")
// 2. Point SwiftData at the same store
let config = ModelConfiguration(
"MyAppModel",
url: storeURL
)
let container = try ModelContainer(
for: Trip.self,
configurations: config
)Key rules for coexistence:
@Model class name must match the Core Data entity name.@Attribute(originalName:) when the SwiftData property name differs
from the persisted Core Data property name.// Core Data entity: CDTrip (entity name "Trip" in .xcdatamodeld)
// Attributes: name (String), destination (String), startDate (Date),
// isFavorite (Boolean), imageData (Binary Data)
// Matching SwiftData model
@Model
class Trip {
var name: String
var destination: String
var startDate: Date
var isFavorite: Bool = false
@Attribute(.externalStorage) var imageData: Data?
init(name: String, destination: String, startDate: Date) {
self.name = name
self.destination = destination
self.startDate = startDate
}
}// Phase 1: Core Data stack still handles writes;
// SwiftData reads the same store for new UI
@main
struct MyApp: App {
let existingCoreDataStoreURL = NSPersistentContainer.defaultDirectoryURL()
.appendingPathComponent("MyAppModel.sqlite")
var body: some Scene {
WindowGroup {
ContentView()
}
// SwiftData reads from the same store
.modelContainer(for: Trip.self, configurations:
ModelConfiguration(url: existingCoreDataStoreURL)
)
}
}
// Phase 2: New features use SwiftData for both reads and writes
// Phase 3: Migrate remaining Core Data code to SwiftData
// Phase 4: Remove Core Data stack and .xcdatamodeld.xcdatamodeld and
@Model cause crashes.@Model ClassesCreate a @Model class for each Core Data entity. Property names and types
must align with the .xcdatamodeld definition.
// Core Data entity "Article"
// Attributes: id (UUID), title (String), body (String),
// createdAt (Date), isDraft (Boolean)
// Relationships: author (to-one → Author), tags (to-many → Tag)
@Model
class Article {
@Attribute(.unique) var id: UUID
var title: String
var body: String
var createdAt: Date
var isDraft: Bool = true
@Relationship(deleteRule: .nullify, inverse: \Author.articles)
var author: Author?
@Relationship(deleteRule: .nullify, inverse: \Tag.articles)
var tags: [Tag] = []
init(id: UUID = UUID(), title: String, body: String, createdAt: Date = .now) {
self.id = id
self.title = title
self.body = body
self.createdAt = createdAt
}
}
@Model
class Author {
@Attribute(.unique) var id: UUID
var name: String
var articles: [Article] = []
init(id: UUID = UUID(), name: String) {
self.id = id
self.name = name
}
}
@Model
class Tag {
@Attribute(.unique) var id: UUID
var name: String
var articles: [Article] = []
init(id: UUID = UUID(), name: String) {
self.id = id
self.name = name
}
}| Core Data Type | SwiftData Type |
|---|---|
| String | String |
| Boolean | Bool |
| Integer 16/32/64 | Int |
| Float / Double | Float / Double |
| Date | Date |
| Binary Data | Data |
| UUID | UUID |
| URI | URL |
| Decimal | Decimal |
| Transformable | Compatible Codable value type; Schema.CompositeAttribute is iOS 17+, while @Attribute(.codable) is iOS 27 beta |
| To-one relationship | Optional reference to @Model |
| To-many relationship | Array of @Model |
If the Core Data store has existing data, SwiftData must be able to open it.
Use VersionedSchema and SchemaMigrationPlan for non-trivial changes.
// If the SwiftData model exactly matches the Core Data schema,
// no migration is needed -- SwiftData opens the store directly.
// For schema differences, define versioned schemas:
enum SchemaV1: VersionedSchema {
static var versionIdentifier = Schema.Version(1, 0, 0)
static var models: [any PersistentModel.Type] { [Article.self, Author.self] }
@Model class Article {
var id: UUID
var title: String
var body: String
var createdAt: Date
init(id: UUID, title: String, body: String, createdAt: Date) {
self.id = id; self.title = title
self.body = body; self.createdAt = createdAt
}
}
@Model class Author {
var id: UUID
var name: String
init(id: UUID, name: String) { self.id = id; self.name = name }
}
}
enum SchemaV2: VersionedSchema {
static var versionIdentifier = Schema.Version(2, 0, 0)
static var models: [any PersistentModel.Type] { [Article.self, Author.self, Tag.self] }
@Model class Article {
var id: UUID
var title: String
var body: String
var createdAt: Date
var isDraft: Bool = true // New property
init(id: UUID, title: String, body: String, createdAt: Date) {
self.id = id; self.title = title
self.body = body; self.createdAt = createdAt
}
}
@Model class Author {
var id: UUID
var name: String
init(id: UUID, name: String) { self.id = id; self.name = name }
}
@Model class Tag {
var id: UUID
var name: String
init(id: UUID, name: String) { self.id = id; self.name = name }
}
}
enum ArticleMigrationPlan: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] { [SchemaV1.self, SchemaV2.self] }
static var stages: [MigrationStage] {
[MigrationStage.lightweight(fromVersion: SchemaV1.self, toVersion: SchemaV2.self)]
}
}Always test migration with real production data copies before shipping.
import XCTest
import SwiftData
final class MigrationTests: XCTestCase {
func testCoreDataToSwiftDataMigration() throws {
// 1. Copy a known Core Data store into the test bundle
let sourceURL = Bundle(for: type(of: self))
.url(forResource: "TestStore", withExtension: "sqlite")!
let tempDir = FileManager.default.temporaryDirectory
.appendingPathComponent(UUID().uuidString)
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
let destURL = tempDir.appendingPathComponent("TestStore.sqlite")
try FileManager.default.copyItem(at: sourceURL, to: destURL)
// Copy WAL and SHM if they exist
for ext in ["-wal", "-shm"] {
let src = sourceURL.deletingLastPathComponent()
.appendingPathComponent("TestStore.sqlite\(ext)")
if FileManager.default.fileExists(atPath: src.path) {
try FileManager.default.copyItem(
at: src,
to: tempDir.appendingPathComponent("TestStore.sqlite\(ext)")
)
}
}
// 2. Open with SwiftData
let config = ModelConfiguration(url: destURL)
let container = try ModelContainer(
for: SchemaV2.Article.self,
migrationPlan: ArticleMigrationPlan.self,
configurations: config
)
// 3. Verify data survived migration
let context = ModelContext(container)
let articles = try context.fetch(FetchDescriptor<SchemaV2.Article>())
XCTAssertFalse(articles.isEmpty, "Migration should preserve existing articles")
// 4. Verify new properties have defaults
for article in articles {
XCTAssertTrue(article.isDraft, "New isDraft property should default to true")
}
// Cleanup
try FileManager.default.removeItem(at: tempDir)
}
}@Model class with identical property names and typesVersionedSchema and SchemaMigrationPlan defined for non-trivial schema changesModelConfiguration points to the existing Core Data SQLite fileautomaticallyMergesChangesFromParent enabled on Core Data's viewContext.xcdatamodeld removed only after full migration is verified.tessl-plugin
skills
accessorysetupkit
references
activitykit
references
adattributionkit
references
alarmkit
references
app-clips
app-intents
app-store-optimization
app-store-review
apple-on-device-ai
appmigrationkit
references
audioaccessorykit
references
authentication
references
avkit
references
background-processing
references
browserenginekit
references
callkit
references
carplay
references
cloudkit
references
contacts-framework
references
core-bluetooth
references
core-data
core-motion
references
core-nfc
references
coreml
references
cryptokit
references
cryptotokenkit
references
debugging-instruments
device-integrity
references
dockkit
references
energykit
references
eventkit
references
financekit
references
focus-engine
gamekit
references
healthkit
references
homekit
references
ios-accessibility
ios-ettrace-performance
ios-localization
ios-memgraph-analysis
ios-networking
ios-simulator
references
mapkit
metrickit
references
musickit
references
natural-language
references
paperkit
references
passkit
references
pdfkit
references
pencilkit
references
permissionkit
references
photokit
push-notifications
realitykit
references
relevancekit
references
scenekit
references
sensorkit
references
speech-recognition
references
spritekit
references
storekit
swift-api-design-guidelines
swift-architecture
references
swift-charts
references
swift-codable
references
swift-concurrency
swift-formatstyle
references
swift-language
swift-security
references
swift-testing
swiftdata
swiftlint
swiftui-animation
swiftui-gestures
references
swiftui-layout-components
swiftui-liquid-glass
references
swiftui-patterns
swiftui-performance
swiftui-uikit-interop
swiftui-webkit
tabletopkit
references
tipkit
references
vision-framework
weatherkit
references
widgetkit
references