# Migrating a SwiftUI iOS App from Core Data to Realm

Porting an app that's using Core Data to Realm is very simple. If you
have an app that already uses Core Data, and have been considering the
move to Realm, this step-by-step guide is for you! The way that your
code interacts with Core Data and Realm is very different depending on
whether your app is based on SwiftUI or UIKit—this guide assumes SwiftUI
(a UIKit version will come soon.)

You're far from the first developer to port your app from Core Data to
Realm, and we've been told many times that it can be done in a matter of
hours. Both databases handle your data as objects, so migration is
usually very straightforward: Simply take your existing Core Data code
and refactor it to use the Realm
SDK.

After migrating, you should be thrilled with the ease of use, speed, and
stability that Realm can bring to your apps. Add in MongoDB Realm
Sync and you can share the same
data between iOS, Android, desktop, and web apps.

>
>
>This article was updated in July 2021 to replace `objc` and `dynamic`
>with the `@Persisted` annotation that was introduced in Realm-Cocoa
>10.10.0.
>
>

## Prerequisites

This guide assumes that your app is written in Swift and built on
SwiftUI rather than UIKit.

## Steps to Migrate Your Code

### 1. Add the Realm Swift SDK to Your Project

To use Realm, you need to include Realm's Swift SDK
(Realm-Cocoa) in your Xcode
project. The simplest method is to use the Swift Package Manager.

In Xcode, select "File/Swift Packages/Add Package Dependency...". The
package URL is :

You can keep the default options and then select both the "Realm" and
"RealmSwift" packages.

### 2a. The Brutalist Approach—Remove the Core Data Framework

First things first. If your app is currently using Core Data, you'll
need to work out which parts of your codebase include Core Data code.
These will need to be refactored. Fortunately, there's a handy way to do
this. While you could manually perform searches on the codebase looking
for the relevant code, a much easier solution is to simply delete the
Core Data import statements at the top of your source files:

``` swift
import CoreData
```

Once this is done, every line of code implementing Core Data will throw
a compiler error, and then it's simply a matter of addressing each
compiler error, one at a time.

### 2b. The Incremental Approach—Leave the Core Data Framework Until Port Complete

Not everyone (including me) likes the idea of not being able to build a
project until every part of the port has been completed. If that's you,
I'd suggest this approach:

-   Leave the old code there for now.
-   Add a new model, adding `Realm` to the end of each class.
-   Work through your views to move them over to your new model.
-   Check and fix build breaks.
-   Remove the `Realm` from your model names using the Xcode refactoring
    feature.
-   Check and fix build breaks.
-   Find any files that still `import CoreData` and either remove that
    line or the entire file if it's now obsolete.
-   Check and fix build breaks.
-   Migrate existing user data from Core Data to Realm if needed.
-   Remove the original model code.

### 3. Remove Core Data Setup Code

In Core Data, changes to model objects are made against a managed object
context object. Managed object context objects are created against a
persistent store coordinator object, which themselves are created
against a managed object model object.

Suffice to say, before you can even begin to think about writing or
reading data with Core Data, you usually need to have code somewhere in
your app to set up these dependency objects and to expose Core Data's
functionality to your app's own logic. There will be a sizable chunk of
"setup" Core Data code lurking somewhere.

When you're switching to Realm, all of that code can go.

In Realm, all of the setting up is done on your behalf when you access a
Realm object for the first time, and while there are options to
configure it—such as where to place your Realm data file on disk—it's
all completely optional.

### 4. Migrate Your Model Files

Your Realm schema will be defined in code by defining your Realm Object
classes. There is no need for `.xcdatamodel` files when working with
Realm and so you can remove those Core Data files from your project.

In Core Data, the bread-and-butter class that causes subclassed model
objects to be persisted is `NSManagedObject`. The classes for these
kinds of objects are pretty much standard:

``` swift
import CoreData

@objc(ReminderList)
public class ReminderList: NSManagedObject {
   @NSManaged public var title: String
   @NSManaged public var reminders: Array
}

@objc(Reminder)
public class Reminder: NSManagedObject {
   @NSManaged var title: String
   @NSManaged var isCompleted: Bool
   @NSManaged var notes: String?
   @NSManaged var dueDate: Date?
   @NSManaged var priority: Int16
   @NSManaged var list: ReminderList
}
```

Converting these managed object subclasses to Realm is really simple:

``` swift
import RealmSwift

class ReminderList: Object, ObjectKeyIdentifiable {
   @Persisted var title: String
   @Persisted var reminders: List
}

class Reminder: EmbeddedObject, ObjectKeyIdentifiable {
   @Persisted var title: String
   @Persisted var isCompleted: Bool
   @Persisted var notes: String?
   @Persisted var dueDate: Date?
   @Persisted var priority: Int16
}
```

Note that top-level objects inherit from `Object`, but objects that only
exist within higher-level objects inherit from `EmbeddedObject`.

### 5. Migrate Your Write Operations

Creating a new object in Core Data and then later modifying it is
relatively trivial, only taking a few lines of code.

Adding an object to Core Data must be done using a
`NSManagedObjectContext`. This context is available inside a SwiftUI
view through the environment:

``` swift
@Environment(\.managedObjectContext) var viewContext: NSManagedObjectContext
```

That context can then be used to save the object to Core Data:

``` swift
let reminder = Reminder(context: viewContext)
reminder.title = title
reminder.notes = notes
reminder.dueDate = date
reminder.priority = priority

do {
   try viewContext.save()
} catch {
   let nserror = error as NSError
   fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
```

Realm requires that writes are made within a transaction, but the Realm
Swift SDK hides most of that complexity when you develop with SwiftUI.
The current Realm is made available through the SwiftUI environment and
the view can access objects in it using the `@ObserveredResults`
property wrapper:

``` swift
@ObservedResults(Reminder.self) var reminders
```

A new object can then be stored in the Realm:

``` swift
let reminder = Reminder()
reminder.title = title
reminder.notes = notes
reminder.dueDate = date
reminder.priority = priority
$reminders.append(reminder)
```

The Realm Swift SDK also hides the transactional complexity behind
making updates to objects already stored in Realm. The
`@ObservedRealmObject` property wrapper is used in the same way as
`@ObservedObject`—but for Realm managed objects:

``` swift
@ObservedRealmObject var reminder: Reminder
TextField("Notes", text: $reminder.notes)
```

To benefit from the transparent transaction functionality, make sure
that you use the `@ObservedRealmObject` property wrapper as you pass
Realm objects down the view hierarchy.

If you find that you need to directly update an attribute within a Realm
object within a view, then you can use this syntax to avoid having to
explicitly work with Realm transactions (where `reminder` is an
`@ObservedRealmObject`):

``` swift
$reminder.isCompleted.wrappedValue.toggle()
```

### 6. Migrate Your Queries

In its most basic implementation, Core Data uses the concept of fetch
requests in order to retrieve data from disk. A fetch can filter and
sort the objects:

``` swift
var reminders = FetchRequest(
   entity: Reminder.entity(),
   sortDescriptors: NSSortDescriptor(key: "title", ascending: true),
   predicate: NSPredicate(format: "%K == %@", "list.title", title)).wrappedValue
```

The equivalent code for such a query using Realm is very similar, but it
uses the `@ObservedResults` property wrapper rather than `FetchRequest`:

``` swift
@ObservedResults(
   Reminder.self,
   filter: NSPredicate(format: "%K == %@", "list.title", title),
   sortDescriptor: SortDescriptor(keyPath: "title", ascending: true)) var reminders
```

### 7. Migrate Your Users' Production Data

Once all of your code has been migrated to Realm, there's one more
outstanding issue: How do you migrate any production data that users may
already have on their devices out of Core Data and into Realm?

This can be a very complex issue. Depending on your app's functionality,
as well as your users' circumstances, how you go about handling this can
end up being very different each time.

We've seen two major approaches:

-   Once you've migrated your code to Realm, you can re-link the Core
    Data framework back into your app, use raw NSManagedObject objects
    to fetch your users' data from Core Data, and then manually pass it
    over to Realm. You can leave this migration code in your app
    permanently, or simply remove it after a sufficient period of time
    has passed.
-   If the user's data is replaceable—for example, if it is simply
    cached information that could be regenerated by other user data on
    disk—then it may be easier to simply blow all of the Core Data save
    files away, and start from scratch when the user next opens the app.
    This needs to be done with very careful consideration, or else it
    could end up being a bad user experience for a lot of people.

## SwiftUI Previews

As with Core Data, your SwiftUI previews can add some data to Realm so
that it's rendered in the preview. However, with Realm it's a lot easier
as you don't need to mess with contexts and view contexts:

``` swift
func bootstrapReminder() {
   do {
      let realm = try Realm()
      try realm.write {
         realm.deleteAll()
         let reminder = Reminder()
         reminder.title = "Do something"
         reminder.notes = "Anything will do"
         reminder.dueDate = Date()
         reminder.priority = 1
         realm.add(list)
      }
   } catch {
      print("Failed to bootstrap the default realm")
   }
}

struct ReminderListView_Previews: PreviewProvider {
   static var previews: some View {
      bootstrapReminder()
      return ReminderListView()
   }
}
```

## Syncing Realm Data

Now that your application data is stored in Realm, you have the option
to sync that data to other devices (including Android) using MongoDB
Realm Sync. That same data is
then stored in Atlas where it can be queried by web applications via
GraphQL or Realm's web
SDK.

This enhanced functionality is beyond the scope of this guide, but you
can see how it can be added by reading the
Building a Mobile Chat App Using Realm – Integrating Realm into Your App series.

## Conclusion

Thanks to their similarities in exposing data through model objects,
converting an app from using Core Data to Realm is very quick and
simple.

In this guide, we've focussed on the code that needs to be changed to
work with Realm, but you'll be pleasantly surprised at just how much
Core Data boilerplate code you're able to simply delete!

If you've been having trouble getting Core Data working in your app, or
you're looking for a way to sync data between platforms, we strongly
recommend giving Realm a try, to see if it works for you. And if it
does, please be sure to let us know!

If you've any questions or comments, then please let us know on our
community
forum.

>
>
>If you have questions, please head to our developer community
>website where the MongoDB engineers and
>the MongoDB community will help you build your next big idea with
>MongoDB.
>
>