How to Add the Realm Database to an iOS CRUD App using Swift

Are you looking for a fast, easy-to-use database for your iOS app? One that‘s cross-platform, works on both mobile and web, and has a simple, intuitive API? Then look no further than Realm!

In this tutorial, we‘ll walk through the process of adding Realm to an iOS project, defining a data model, and implementing all the CRUD (Create, Read, Update, Delete) operations required for a basic ToDo app. By the end, you‘ll have hands-on experience with this powerful mobile database and be ready to use Realm in your own apps. Let‘s get started!

What is Realm?

Realm is an open-source database specifically designed for mobile apps. It‘s a popular alternative (or complement) to Core Data and SQLite on iOS. Realm‘s key advantages are:

  • Easy to set up: Add Realm to your project with just a few lines of code
  • Cross-platform: Use Realm in native iOS/Android apps as well React Native, Flutter, and web apps
  • Fast: Realm is optimized for mobile devices and significantly outperforms SQLite
  • Supports multiple language: Write your iOS apps in Swift or Objective-C

Realm stores your data in a single file, making it easy to manage and back up. It supports all the basic data types like strings, numbers, lists, and dictionaries.

Setting Up Realm in an Xcode Project

There are a few different ways to install Realm in an Xcode project – via CocoaPods, Carthage, or Swift Package Manager. For this tutorial we‘ll use CocoaPods, as it‘s the most straightforward method.

Installing Realm via CocoaPods

Here are the step-by-step instructions:

  1. Open terminal and navigate to your Xcode project directory
  2. Run pod init to create a Podfile for your project
  3. Open the Podfile in a text editor
  4. Add the following lines to your Podfile:
target ‘YourAppName‘ do
  use_frameworks!

  # Pods for YourAppName
  pod ‘RealmSwift‘  
end
  1. Save the Podfile and run pod install in Terminal
  2. Close your Xcode project and open the newly created .xcworkspace file

You‘re ready to start using Realm! Just add import RealmSwift to any Swift file where you want to access the database.

Defining a Realm Data Model

To store data in Realm, we first need to define our model classes. These describe the structure of the data we‘ll be storing in the database.

For our sample ToDo app, we‘ll keep things simple – each ToDoItem will have just two properties:

import RealmSwift

class ToDoItem: Object {
    @objc dynamic var itemId: UUID = UUID()
    @objc dynamic var text: String = ""
}

This Realm object class is very similar to a regular Swift class with a couple key differences:

  • It inherits from Object – the base class for all Realm model objects
  • Properties are declared as @objc dynamic var so they work with Realm‘s Objective-C runtime
  • The class name matches the name of the collection where objects will be stored in the database

We‘ve given each ToDoItem a unique itemId (we could also use an auto-incrementing integer) and a text property to store the actual to-do description.

Performing CRUD Operations with Realm

Now that we have our data model set up, we‘re ready to start Creating, Reading, Updating, and Deleting objects in the database.

Create

To add a new ToDoItem to the database:

let newItem = ToDoItem()
newItem.text = "Buy groceries"

let realm = try! Realm()  
try! realm.write {
    realm.add(newItem)
}

We first create an instance of the ToDoItem class and set its properties. Then we get a reference to the default Realm database file and add the new object inside a write block.

All writes to a Realm must be done within a write block – this ensures the data integrity of the database. If any errors occur, Realm will automatically roll back the transaction.

Read

To retrieve all the ToDoItems from the database:

let realm = try! Realm()
let allItems = realm.objects(ToDoItem.self)

Here we‘re querying the default Realm for all objects of the ToDoItem type. The objects(_:) method returns a live, auto-updating collection – any time an object is added, modified, or deleted in the database, the collection will update automatically.

We can also filter the results based on certain criteria:

// Get only items that contain the word "milk"
let milkItems = realm.objects(ToDoItem.self).filter("text CONTAINS ‘milk‘")

// Get only items added in the last day
let recentItems = realm.objects(ToDoItem.self).filter("addedAt > %@", Date(timeIntervalSinceNow: -86400))

Realm has a powerful query language that supports all sorts of filtering, sorting, and aggregation.

Update

To modify an existing object in the database:

let realm = try! Realm()

if let itemToUpdate = realm.object(ofType: ToDoItem.self, forPrimaryKey: someId) {
    try! realm.write {
        itemToUpdate.text = "Updated text"
    }
}

Like with creating objects, all updates must happen inside a write block. Here we‘re using the primary key to find a specific ToDoItem, then updating its text property.

Realm objects work very similarly to regular Swift objects. You can read and write their properties and Realm will track the changes and update the database automatically when the write block completes.

Delete

To remove an object from the database:

let realm = try! Realm()

if let itemToDelete = realm.object(ofType: ToDoItem.self, forPrimaryKey: someId) {
    try! realm.write {
        realm.delete(itemToDelete)
    }
}

Again we use the primary key to find the object we want to delete, then pass it to realm.delete(_:) inside a write block.

We can also delete all objects of a certain type, or that match some filter criteria:

let realm = try! Realm()

try! realm.write {
    // Delete all ToDoItems
    realm.delete(realm.objects(ToDoItem.self))

    // Delete all completed ToDoItems
    realm.delete(realm.objects(ToDoItem.self).filter("isCompleted = true"))
}  

Use this mass-delete capability wisely – there‘s no way to recover the deleted data!

Integrating Realm with Your App‘s UI

So far we‘ve seen how to perform the basic CRUD operations on a Realm database. But to build a useful app, we need to integrate this data with our UI.

Realm integrates seamlessly with UIKit and SwiftUI, allowing you to bind live Realm collections to table views, collection views, and SwiftUI Lists.

For example, to display all the to-do items in a table view:

import RealmSwift

class ToDoListViewController: UITableViewController {
    var items: Results<ToDoItem>?

    override func viewDidLoad() {
        super.viewDidLoad()

        let realm = try! Realm()
        items = realm.objects(ToDoItem.self).sorted(byKeyPath: "addedAt")
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return items?.count ?? 0
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") ?? UITableViewCell(style: .default, reuseIdentifier: "Cell")

        if let item = items?[indexPath.row] {
            cell.textLabel?.text = item.text
        }

        return cell
    }  
}

In the viewDidLoad() we get a reference to the default Realm and query for all ToDoItems, sorted by the date they were added.

The items property is a live Results collection, so any time an item is added or removed from the database, the table view will update automatically! This makes it incredibly easy to keep your UI in sync with your data.

Some other examples of binding Realm data to UI:

  • Show the number of overdue to-do items with a Realm collection notification
  • Respond to changes in the average rating for a list of restaurants
  • Update a graph of your daily step count when new HealthKit samples are added
  • Validate that a username is unique by checking against existing users in the database

Advanced Realm Techniques

We‘ve covered the basics of working with Realm in an iOS app, but there‘s a lot more you can do to build a full-featured, production-ready app.

Migrations

As you update your app over time, you may need to change your Realm data model by adding, removing, or modifying object properties. To preserve your users‘ existing data, you‘ll need to write a migration.

Migrations in Realm are fairly straightforward – you specify the current and previous schema versions and provide a block of code to update the database:

let config = Realm.Configuration(
    schemaVersion: 2,
    migrationBlock: { migration, oldSchemaVersion in
        if oldSchemaVersion < 2 {
            // Add a new "category" property to ToDoItem
            migration.enumerateObjects(ofType: ToDoItem.className()) { oldObject, newObject in
                newObject?["category"] = "Default"
            }
        }
    })

Realm.Configuration.defaultConfiguration = config

Here we‘re telling Realm that the schema is now at version 2, and providing a block of code to run if the database is at a version less than 2. Inside the block, we enumerate all the ToDoItem objects and add a new category property with a default value.

Realm will automatically detect and run the migration the next time we try to access the database. Just be sure to increment the schemaVersion each time you make a change to your model classes.

Syncing Data Across Devices

Realm offers a cloud service for syncing your app‘s data across multiple devices. With just a few lines of code you can enable real-time sync:

let user = app.login(credentials: Credentials.emailPassword(email: "[email protected]", password: "password"))

let configuration = user.configuration(partitionValue: "some partition value")
Realm.asyncOpen(configuration: configuration) { realm, error in 
    if let realm = realm {
        // Realm successfully opened, with data synced from the server
    } else if let error = error {
        // Handle error
    }
}

You provide a partition value to determine which subset of data should be synced to each device. For a to-do list app, you might use the user ID so each user only syncs their own tasks.

Realm will automatically handle the networking, conflict resolution, and merging of local & remote changes. You interact with the synced realm the same way as a local-only realm database.

Reactive Extensions

Realm has official reactive extensions for Combine and RxSwift, which allow you to observe changes to your Realm data and react accordingly.

For example, to be notified whenever a new to-do item is added:

let realm = try! Realm()
let items = realm.objects(ToDoItem.self)

let cancellable = items.collectionPublisher
    .sink { changes in
        switch changes {
        case .initial(let items):
            // Will print "Results<ToDoItem> contains 5 items"
            print("Results<ToDoItem> contains \(items.count) items")
        case .update(let items, let deletions, let insertions, let updates):
            // Will print "Inserted 2 items at indices [5, 7]"
            print("Inserted \(insertions.count) items at indices \(insertions)")
        case .error(let error):
            // Handle error
            print("An error occurred: \(error)")
        }
    }

By observing changes with the Combine or RxSwift frameworks, you can keep your UI and other app components up-to-date with the latest data, without having to manually fetch or check for changes.

Best Practices for Working with Realm

To get the most out of Realm in your iOS app, keep these tips and best practices in mind:

  • Design your Realm objects to match your app‘s needs – don‘t just mirror your server API structure
  • Use Realm objects in your view controllers and SwiftUI views, but convert to plain Swift objects when passing data to other layers of your app
  • Watch out for retain cycles when capturing Realm objects in closures or local variable
  • Be mindful of performance and memory usage, especially on older devices
  • Always handle errors when opening a Realm or writing to the database
  • If you have a very large amount of data, consider storing it in multiple Realm files to avoid memory limitations
  • Don‘t store large binary blobs like images in your Realm – store just the file path and cache the images separately

By following these guidelines and fully leveraging Realm‘s features like live objects, query filtering, data sync, and reactive extensions, you can build incredibly powerful and responsive iOS apps.

Conclusion

In this tutorial we‘ve seen how to add Realm – a fast, easy-to-use database – to an iOS app built with Swift. We covered:

  • Setting up a new Realm database with CocoaPods
  • Defining model classes to structure your data
  • Creating, reading, updating, and deleting records
  • Integrating live Realm results with SwiftUI and UIKit
  • Enabling cross-device data sync
  • Observing database changes reactively

Realm is a great choice for mobile apps of all shapes and sizes. It‘s fast, cross-platform, and the intuitive, object-oriented API makes it easy to work with your data.

I encourage you to try out Realm in your own iOS apps. Check out the official Realm Swift documentation for more details on all the capabilities we‘ve covered.

If you have any questions or feedback, feel free to reach out in the comments below. Happy coding!

Similar Posts