Realm Database Guide - Building a Note app in Swift for iOS
Tags: ios, swift, realm, notes
Published: 2019-01-04 03:49:31

***If you prefer videos over written articles, this article is a written version of a video I produced. The content is identical.***

{% youtube XVm_6977jcE %}

This video is the first in a series that I hope will serve to teach you how to use iOS frameworks and tools such as Siri Shortcuts, CloudKit, and more. If you have a specific framework or feature you'd like to see this series cover, feel free to write me at jordan[dot]osterberg[at]shadowsystems[dot]tech, or on Twitter [@josterbe1](https://twitter.com/josterbe1).

#### Table of Contents
1. [Introduction](#introduction)
2. [Installing Dependencies](#installing-dependencies)
3. [The Model](#the-model)
4. [Implementing Realm](#implementing-realm)
5. [Conclusion](#conclusion)

Without further ado, let's take a look at the application we're going to build in this series...

### Introduction

![The application's list of notes](https://thepracticaldev.s3.amazonaws.com/i/4nzrs0ttu43ski89l51g.png)
![Editing a note individually](https://thepracticaldev.s3.amazonaws.com/i/5n4lyx3ec29mz1hgvir4.png)

We have our list of notes, and when we tap into one, we can read and begin to edit, or delete it. Pretty simple. You'll notice if you download the [starter project](https://github.com/JordanOsterberg) that notes don't persist when you go in and out of the NoteDetailController when tapping on the "Apple Park Visit" note. That is, the content of the note doesn't save when you edit it.

That's what we're going to build out in this article, using the [Realm Database](https://realm.io). I've used Realm for just about two years, and I find its simplicity to outweigh the cost of using a 3rd party library. In fact, I use it in the app I spend most of my personal development time on (see https://countdowns.download) on both macOS and iOS. 

### Installing Dependencies

In order to begin using Realm, we need to install it using [CocoaPods](https://cocoapods.org). CocoaPods, if you're not aware, CocoaPods is a dependency management tool that is widely used in the iOS space. If you don't have CocoaPods installed on your Mac already, you can use the `sudo gem install cocoapods` command to get started. Now, open the project folder, as well as a Terminal window inside of that directory.

Type in `pod init`, and then `open Podfile`.

Inside of the newly created "Podfile", we need to write some text which will inform CocoaPods what libraries you'd like to install into your project.

Below `# Pods for NotesApp`, write these two lines:
```
pod 'Realm', '~> 3.12.0'
pod 'RealmSwift', '~> 3.12.0'
```

Your Podfile should look similar to this after you've written those lines:
```
platform :ios, '12.0'

target 'NotesApp' do
  use_frameworks!

  # Pods for NotesApp
  pod 'Realm', '~> 3.12.0'
  pod 'RealmSwift', '~> 3.12.0'

  target 'NotesAppTests' do
    inherit! :search_paths
  end

  target 'NotesAppUITests' do
    inherit! :search_paths
  end

end

```

Now that we've added our dependencies, let's ask CocoaPods to install them with `pod install`

This **will** take some time when you first use CocoaPods. Don't worry, CocoaPods is just downloading some initial components. This won't happen every time you `pod install`.

Your Terminal window will look like this once that command finishes executing:

![Terminal window](https://thepracticaldev.s3.amazonaws.com/i/gcrwub50q7seobhpbjhx.png)

After this, if you've opened the `NotesApp.xcodeproj` already, close out of it. When using CocoaPods, you must use the `.xcworkspace` file instead of the default `.xcodeproj` file. Open the `NotesApp.xcworkspace` file and head to `Note.swift`.

### The Model

This class contains our Note model object, which contains a few basic properties:
```swift
class Note {
    
    var identifier: String
    var content: String
    var lastEdited: Date
    
    init(
         identifier: String = UUID().uuidString,
         content: String,
         lastEdited: Date = Date()) {
        self.identifier = identifier
        self.content = content
        self.lastEdited = lastEdited
    }
    
}
```

Standard model code, nothing special going on here.

We also have an extension to our Note object in the same file, which subclasses as protocol called `Writeable`
```swift
extension Note: Writable {
    
    func write(dataSource: DataSource) {
        self.lastEdited = Date()
        
        dataSource.store(object: self)
    }
    
    func delete(dataSource: DataSource) {
        dataSource.delete(object: self)
    }
    
}
```

Inside of the `write` and `delete` functions, you'll notice we have a `DataSource` property. `DataSource` is a generic protocol to help make modifying data as abstract at the higher levels of our code as possible.

Here's the protocol definition:
```swift
protocol DataSource {
    
    func store<T>(object: T)
    func delete<T>(object: T)
    
}
```

If you're not familiar with generics, each of our functions has a `T` parameter which essentially means it can be any type of object. This isn't used very heavily in our project, but it could be evolved and used further to create multiple DataSources with different constraints around what objects they can store.

We implement our `DataSource` protocol in `NoteDataSource`. There isn't anything special here either, aside from one little tidbit I'd like to note for explanation's sake.

Whenever we `store` or `delete` objects, we use the following call to `NotificationCenter`:
```swift
NotificationCenter.default.post(name: .noteDataChanged, object: nil)

// We also have this extension of Notification.Name to make sending and receiving this notification simple.

extension Notification.Name {
    
    static let noteDataChanged = Notification.Name(rawValue: "noteDataChanged")
    
}
```

Essentially, whenever any note is stored or deleted, we inform any listeners that our data has changed, so they can update their UI accordingly.

With all of our model files and classes out of the way, let's begin implementing Realm!

### Implementing Realm

There are three steps to integrate Realm with our project:
1. [Creating the Realm Object](#creating-the-realm-object)
2. [Bridging between our Realm object and our primitive Note object](#bridging-between-our-realm-object-and-our-primitive-note-object)
3. [Begin retrieving and modifying data with Realm](#begin-retrieving-and-modifying-data-with-realm)


#### Creating the Realm Object

This step is relatively simple. Let's create a new Swift file called `RealmNote.swift`. Inside of `RealmNote.swift`, import the RealmSwift framework and create a class declaration like so:
```swift
import RealmSwift

class RealmNote: Object {

}
```

We'll subclass Realm's `Object` class which will allow us to use `RealmNote` in Realm database functions.

Now, add in the three properties that we have in our Note model:
```swift
@objc dynamic var identifier: String = ""
@objc dynamic var content: String = ""
@objc dynamic var lastEdited: Date = Date()
```

The `@objc dynamic` pieces of our variable declaration expose our properties to Objective-C, which many of the iOS layers of Realm are written in. It also allows Realm to listen for changes to our `RealmNote` objects.

To finish off our class, override the `class func` primaryKey, which returns an optional string (`String?`) with the value "identifier". This informs Realm that `RealmNote`'s primary key, a way to uniquely identify our objects, is stored in the "identifier" property.

Once you've completed these steps, your `RealmNote` will look like this:
```swift
class RealmNote: Object {
    
    @objc dynamic var identifier: String = ""
    @objc dynamic var content: String = ""
    @objc dynamic var lastEdited: Date = Date()
    
    override class func primaryKey() -> String? {
        return "identifier"
    }
    
}
```

That's all for step one.

#### Bridging between our Realm object and our primitive Note object

We have two separate model objects: `Note` and `RealmNote`. RealmNote is used internally when dealing with Realm, in order to keep our model layer decoupled from our UI. By using two separate objects, we could switch away from using Realm in the future if the need arrises.

In the `RealmNote.swift` file, create an extension of `RealmNote`:
```swift
extension RealmNote {

}
```

Now, create a `convenience init` inside of the extension which takes in a `Note` as it's only property. This will allow us to create `RealmNote` objects by using a `Note` object.

```swift
convenience init(note: Note) {
    self.init()
        
    self.identifier = note.identifier
    self.content = note.content
    self.lastEdited = note.lastEdited
}
```

Great, now, to finish `RealmNote.swift` off, create a `Note` variable inside the extension which initializes from a `RealmNote`:
```swift
var note: Note {
    return Note(realmNote: self)
}
```

Don't worry if Xcode gives you an error about Note's initializer, we're about to fix that.

Head over to `Note.swift`, where we're about to write the other half of our bridge. This code is essentially the same thing as `RealmNote`'s extension.

```swift
extension Note {
    
    convenience init(realmNote: RealmNote) {
        self.init(identifier: realmNote.identifier, content: realmNote.content, lastEdited: realmNote.lastEdited)
    }
    
    var realmNote: RealmNote {
        return RealmNote(note: self)
    }
    
}
```

Great, that's step two. We can now access a `RealmNote` from a `Note`, and a `Note` from a `RealmNote`. This also means that this code is perfectly valid:

```swift
Note(content: "Example").realmNote.note.realmNote.note.realmNote.note.realmNote
// and so on...
```

Jokes aside, let's finish our application with step three.

#### Begin retrieving and modifying data with Realm

Head into `NoteDataSource.swift`, and `import RealmSwift` before the class declaration. Before we modify, delete, and retrieve Realm objects, we need an instance of the Realm Database. Replace `NoteDataSource`'s init with this:
```swift
var realm: Realm

init() {
    // Load our data
    self.realm = try! Realm()
}
```

This will create an instance of the Realm database. In production, you'd likely not want to use the bang (!) operator when creating the instance because your application will crash if the database isn't available.

Next, let's edit the `store` function to actually store objects into our database.

Writing with Realm is simple:
```swift
try? self.realm.write {

}
```

Inside of that code block, we can update objects within the database. Write this:
```swift
self.realm.add(note.realmNote, update: true)
```

This will either create a new `RealmNote` in the database or update an existing one if one exists. That's it! We're now storing objects in the Realm database (calling note's write function is performed in `NoteDetailController.swift` if you'd like to see the function that is actually used to perform this write.)

Now, let's write our `delete` function. Because of the way we wrote our bridge (without consulting Realm whenever we create a `RealmNote` from a `Note`), we must fetch the object directly from the database using the note's identifier rather than using it's `realmNote` property.

This is simple:
```swift
if let realmNote = self.realm.object(ofType: RealmNote.self, forPrimaryKey: note.identifier) {

}
```

This asks Realm to retrieve a `RealmNote` object from the database, with the identifier, or primary key, of `note.identifier`.

This can be a bit funky. For some reason, the application crashes if we use the traditional `write` function on Realm. As a workaround, this code is perfectly valid and essentially performs the same task as `write`: 
```swift
self.realm.beginWrite()
self.realm.delete(realmNote)
try? self.realm.commitWrite()
```

We begin our write, we delete the object, and we commit our write.

With that, we've built a functioning note application!

### Conclusion

I hope you've enjoyed this tutorial on how to use the Realm Database. I've had a lot of fun making it and I can't wait to evolve this series and add more features to our app over time such as Siri Shortcuts, CloudKit, and more. Thanks for reading.