axiom-realm-migration-ref

0
0
Source

Use when migrating from Realm to SwiftData - comprehensive migration guide covering pattern equivalents, threading model conversion, schema migration strategies, CloudKit sync transition, and real-world scenarios

Install

mkdir -p .claude/skills/axiom-realm-migration-ref && curl -L -o skill.zip "https://mcp.directory/api/skills/download/7224" && unzip -o skill.zip -d .claude/skills/axiom-realm-migration-ref && rm skill.zip

Installs to .claude/skills/axiom-realm-migration-ref

About this skill

Realm to SwiftData Migration — Reference Guide

Purpose: Complete migration path from Realm to SwiftData Swift Version: Swift 5.9+ (Swift 6 with strict concurrency recommended) iOS Version: iOS 17+ (iOS 26+ recommended) Context: Realm Device Sync sunset Sept 30, 2025. This guide is essential for Realm users migrating before deadline.


Critical Timeline

Realm Device Sync DEPRECATION DEADLINE = September 30, 2025

If your app uses Realm Sync:

  • ⚠️ You MUST migrate by September 30, 2025
  • ✅ SwiftData is the recommended replacement
  • ⏰ Time remaining: Depends on current date, but migrations take 2-8 weeks for production apps

This guide provides everything needed for successful migration.


Migration Strategy Overview

Phase 1 (Week 1-2): Preparation & Planning
├─ Audit current Realm usage
├─ Understand model relationships
├─ Plan data migration path
└─ Set up test environment

Phase 2 (Week 2-3): Development
├─ Create SwiftData models from Realm schemas
├─ Implement data migration logic
├─ Convert threading model to async/await
└─ Test with real data

Phase 3 (Week 3-4): Migration
├─ Migrate existing app users' data
├─ Run in parallel (Realm + SwiftData)
├─ Verify CloudKit sync works
└─ Monitor for issues

Phase 4 (Week 4+): Production
├─ Deploy update with parallel persistence
├─ Gradual cutover from Realm to SwiftData
├─ Deprecate Realm code
└─ Monitor CloudKit sync health

Part 1: Pattern Equivalents

Model Definition Conversion

Realm → SwiftData: Basic Model

// REALM
class RealmTrack: Object {
    @Persisted(primaryKey: true) var id: String
    @Persisted var title: String
    @Persisted var artist: String
    @Persisted var duration: TimeInterval
    @Persisted var genre: String?
}

// SWIFTDATA
@Model
final class Track {
    @Attribute(.unique) var id: String
    var title: String
    var artist: String
    var duration: TimeInterval
    var genre: String?

    init(id: String, title: String, artist: String, duration: TimeInterval, genre: String? = nil) {
        self.id = id
        self.title = title
        self.artist = artist
        self.duration = duration
        self.genre = genre
    }
}

Key differences:

  • Realm: @Persisted(primaryKey: true) → SwiftData: @Attribute(.unique)
  • Realm: Implicit init → SwiftData: Explicit init required
  • Realm: Object base class → SwiftData: @Model macro on final class

Realm → SwiftData: Relationships

// REALM: One-to-Many
class RealmAlbum: Object {
    @Persisted(primaryKey: true) var id: String
    @Persisted var title: String
    @Persisted var tracks: RealmSwiftCollection<RealmTrack>
}

// SWIFTDATA: One-to-Many
@Model
final class Album {
    @Attribute(.unique) var id: String
    var title: String

    @Relationship(deleteRule: .cascade, inverse: \Track.album)
    var tracks: [Track] = []
}

@Model
final class Track {
    @Attribute(.unique) var id: String
    var title: String
    var album: Album?  // Inverse automatically maintained
}

Key differences:

  • Realm: Explicit RealmSwiftCollection type → SwiftData: Native [Track] array
  • Realm: Manual relationship management → SwiftData: Inverse relationships automatic
  • Realm: No delete rules → SwiftData: deleteRule: .cascade / .nullify / .deny

Realm → SwiftData: Indexes

// REALM
class RealmTrack: Object {
    @Persisted(primaryKey: true) var id: String
    @Persisted(indexed: true) var genre: String
    @Persisted(indexed: true) var releaseDate: Date
}

// SWIFTDATA
@Model
final class Track {
    @Attribute(.unique) var id: String
    @Attribute(.indexed) var genre: String = ""
    @Attribute(.indexed) var releaseDate: Date = Date()
}

Part 2: Threading Model Conversion

Realm Threading → Swift Concurrency

Realm: Manual Thread Handling

class RealmDataManager {
    func fetchTracksOnBackground() {
        DispatchQueue.global().async {
            let realm = try! Realm()  // Must get Realm on each thread
            let tracks = realm.objects(RealmTrack.self)

            DispatchQueue.main.async {
                self.updateUI(tracks: Array(tracks))
            }
        }
    }

    func saveTrackOnBackground(_ track: RealmTrack) {
        DispatchQueue.global().async {
            let realm = try! Realm()
            try! realm.write {
                realm.add(track)
            }
        }
    }
}

Problems:

  • Manual DispatchQueue threading error-prone
  • Easy to access objects on wrong thread
  • No compile-time guarantees

SwiftData: Actor-Based Concurrency

actor SwiftDataManager {
    let modelContainer: ModelContainer

    func fetchTracks() async -> [Track] {
        let context = ModelContext(modelContainer)
        let descriptor = FetchDescriptor<Track>()
        return (try? context.fetch(descriptor)) ?? []
    }

    func saveTrack(_ track: Track) async {
        let context = ModelContext(modelContainer)
        context.insert(track)
        try? context.save()
    }
}

// Usage (automatic thread handling)
@MainActor
class ViewController: UIViewController {
    @State private var tracks: [Track] = []
    private let manager: SwiftDataManager

    func loadTracks() async {
        tracks = await manager.fetchTracks()
    }
}

Advantages:

  • No manual DispatchQueue
  • Compile-time thread safety
  • Automatic actor isolation
  • Swift 6 strict concurrency compatible

Common Threading Patterns

Realm PatternSwiftData Pattern
DispatchQueue.global().asyncasync/await in background actor
realm.write { }context.insert() + context.save()
Manual thread-local Realm instancesShared ModelContainer + background ModelContext
Thread.isMainThread checks@MainActor annotations

Part 3: Schema Migration Strategies

Simple Schema Migration (Direct Conversion)

For apps with simple schemas (< 5 tables, < 10 fields), direct migration is straightforward:

actor SchemaImporter {
    let realmPath: String
    let modelContainer: ModelContainer

    func migrateFromRealm() async throws {
        // 1. Open Realm database
        let realmConfig = Realm.Configuration(fileURL: URL(fileURLWithPath: realmPath))
        let realm = try await Realm(configuration: realmConfig)

        // 2. Create SwiftData context
        let context = ModelContext(modelContainer)

        // 3. Migrate each model type
        try migrateAllTracks(from: realm, to: context)
        try migrateAllAlbums(from: realm, to: context)
        try migrateAllPlaylists(from: realm, to: context)

        // 4. Save all at once
        try context.save()

        print("Migration complete!")
    }

    private func migrateAllTracks(from realm: Realm, to context: ModelContext) throws {
        let realmTracks = realm.objects(RealmTrack.self)

        for realmTrack in realmTracks {
            let sdTrack = Track(
                id: realmTrack.id,
                title: realmTrack.title,
                artist: realmTrack.artist,
                duration: realmTrack.duration,
                genre: realmTrack.genre
            )
            context.insert(sdTrack)
        }
    }

    private func migrateAllAlbums(from realm: Realm, to context: ModelContext) throws {
        let realmAlbums = realm.objects(RealmAlbum.self)

        for realmAlbum in realmAlbums {
            let sdAlbum = Album(
                id: realmAlbum.id,
                title: realmAlbum.title
            )
            context.insert(sdAlbum)

            // Connect relationships after creating all records
            for realmTrack in realmAlbum.tracks {
                if let sdTrack = findTrack(id: realmTrack.id, in: context) {
                    sdAlbum.tracks.append(sdTrack)
                }
            }
        }
    }

    private func findTrack(id: String, in context: ModelContext) -> Track? {
        let descriptor = FetchDescriptor<Track>(
            predicate: #Predicate { $0.id == id }
        )
        return try? context.fetch(descriptor).first
    }
}

Complex Schema Migration (Transformation Layer)

For apps with complex schemas, many computed properties, or data transformations:

// Step 1: Define transformation layer
struct TrackDTO {
    let realmTrack: RealmTrack

    var id: String { realmTrack.id }
    var title: String { realmTrack.title }
    var cleanTitle: String { realmTrack.title.trimmingCharacters(in: .whitespaces) }
    var durationFormatted: String {
        let minutes = Int(realmTrack.duration) / 60
        let seconds = Int(realmTrack.duration) % 60
        return String(format: "%d:%02d", minutes, seconds)
    }
}

// Step 2: Migrate through transformation layer
actor ComplexMigrator {
    let modelContainer: ModelContainer

    func migrateWithTransformation(from realm: Realm) throws {
        let context = ModelContext(modelContainer)

        let realmTracks = realm.objects(RealmTrack.self)
        for realmTrack in realmTracks {
            let dto = TrackDTO(realmTrack: realmTrack)

            // Transform data during migration
            let sdTrack = Track(
                id: dto.id,
                title: dto.cleanTitle,  // Cleaned version
                artist: realmTrack.artist,
                duration: realmTrack.duration
            )
            context.insert(sdTrack)
        }

        try context.save()
    }
}

Part 4: CloudKit Sync Transition

Realm Sync → SwiftData CloudKit

Realm Sync (now deprecated) provided automatic sync. SwiftData uses CloudKit directly:

// REALM SYNC: Automatic but deprecated
let config = Realm.Configuration(
    syncConfiguration: SyncConfiguration(user: app.currentUser!)
)

// SWIFTDATA: CloudKit (recommended replacement)
let schema = Schema([Track.self, Album.self])
let config = ModelConfiguration(
    schema: schema,
    cloudKitDatabase: .privat

---

*Content truncated.*

axiom-swiftdata

CharlesWiltgen

Use when working with SwiftData - @Model definitions, @Query in SwiftUI, @Relationship macros, ModelContext patterns, CloudKit integration, iOS 26+ features, and Swift 6 concurrency with @MainActor — Apple's native persistence framework

11

axiom-swiftui-nav-diag

CharlesWiltgen

Use when debugging navigation not responding, unexpected pops, deep links showing wrong screen, state lost on tab switch or background, crashes in navigationDestination, or any SwiftUI navigation failure - systematic diagnostics with production crisis defense

21

axiom-ios-build

CharlesWiltgen

Use when ANY iOS build fails, test crashes, Xcode misbehaves, or environment issue occurs before debugging code. Covers build failures, compilation errors, dependency conflicts, simulator problems, environment-first diagnostics.

151

axiom-ios-vision

CharlesWiltgen

Use when implementing ANY computer vision feature - image analysis, object detection, pose detection, person segmentation, subject lifting, hand/body pose tracking.

21

axiom-haptics

CharlesWiltgen

Use when implementing haptic feedback, Core Haptics patterns, audio-haptic synchronization, or debugging haptic issues - covers UIFeedbackGenerator, CHHapticEngine, AHAP patterns, and Apple's Causality-Harmony-Utility design principles from WWDC 2021

20

axiom-in-app-purchases

CharlesWiltgen

Use when implementing in-app purchases, StoreKit 2, subscriptions, or transaction handling - testing-first workflow with .storekit configuration, StoreManager architecture, transaction verification, subscription management, and restore purchases for consumables, non-consumables, and auto-renewable subscriptions

10

You might also like

flutter-development

aj-geddes

Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.

9521,094

drawio-diagrams-enhanced

jgtolentino

Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.

846846

ui-ux-pro-max

nextlevelbuilder

"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."

571700

godot

bfollington

This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.

548492

nano-banana-pro

garg-aayush

Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.

673466

fastapi-templates

wshobson

Create production-ready FastAPI projects with async patterns, dependency injection, and comprehensive error handling. Use when building new FastAPI applications or setting up backend API projects.

514280

Stay ahead of the MCP ecosystem

Get weekly updates on new skills and servers.