Realm Tutorial: Getting Started
In this tutorial, you’ll learn how to use the Realm cross-platform mobile database solution by building an app that keeps track of wild animals. By Felipe Laso-Marsetti.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Realm Tutorial: Getting Started
30 mins
Adding Specimens
Still in AddNewEntryController.swift, add one more property to the class:
var specimen: Specimen!
This property stores the new specimen object.
Next, add this helper method to the class:
func addNewSpecimen() {
let realm = try! Realm() // 1
try! realm.write { // 2
let newSpecimen = Specimen() // 3
newSpecimen.name = nameTextField.text! // 4
newSpecimen.category = selectedCategory
newSpecimen.specimenDescription = descriptionTextField.text
newSpecimen.latitude = selectedAnnotation.coordinate.latitude
newSpecimen.longitude = selectedAnnotation.coordinate.longitude
realm.add(newSpecimen) // 5
specimen = newSpecimen // 6
}
}
Here’s what the code above does:
- First, get a
Realm
instance, like before. - Start the write transaction to add your new
Specimen
. - Create a new
Specimen
instance. - Assign the
Specimen
values. The values come from the input text fields in the user interface, the selected categories and the coordinates from the map annotation. - Add the new
Specimen
to the realm. - Assign the new
Specimen
to yourspecimen
property.
You’ll need some sort of validator to make sure all of the fields are populated in your Specimen
. validateFields()
in AddNewEntryViewController
exists to check for a specimen name and description. Since you’ve added the ability to assign a category to a specimen, you’ll check for that field too.
Find the line in validateFields()
that looks like this:
if nameTextField.text!.isEmpty || descriptionTextField.text!.isEmpty {
Change that line to this:
if
nameTextField.text!.isEmpty ||
descriptionTextField.text!.isEmpty ||
selectedCategory == nil {
This verifies that all of the fields are populated and that you’ve selected a category.
Next, add the following method to the class:
override func shouldPerformSegue(
withIdentifier identifier: String,
sender: Any?
) -> Bool {
if validateFields() {
addNewSpecimen()
return true
} else {
return false
}
}
In the code above, you call the method to validate the fields. If everything is filled in, you add the new specimen and return `true`; otherwise, you return `false`.
Build and run. Tap the + button to create a new specimen. Fill in the name and description, select a category, and then tap Confirm to add your Specimen
to the database.
The view controller dismisses, but nothing appears to happen. What’s the deal?
You posted the record to your realm, but you haven’t populated the map with your new specimen.
Retrieving Records
You added a specimen to the database that you want to show on the map.
Start by taking another look at the updated database in the Realm Browser:
You’ll see a single specimen with its fields populated, along with the latitude and longitude from the MKAnnotation
. You’ll also see the link to your specimen’s category; this means your one-to-many Category
relationship is working as expected.
Click the Category
in your Specimen
record to view the Category
record itself.
Next, you’ll populate the map in the app.
Open SpecimenAnnotation.swift and add a property to the class:
var specimen: Specimen?
This holds the Specimen
for the annotation.
Next, replace the initializer with the following:
init(
coordinate: CLLocationCoordinate2D,
title: String,
subtitle: String,
specimen: Specimen? = nil
) {
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
self.specimen = specimen
}
The change here is to add an option to pass in a Specimen
. The specimen has a default value of nil
meaning you can omit that argument if you like. The rest of the app can still call the initializer with the first three arguments if there’s no specimen.
Open MapViewController.swift and add a new property to the class:
var specimens = try! Realm().objects(Specimen.self)
Since you want to store a collection of specimens in this property, you ask a Realm
instance for all objects of type Specimen
.
Now, add the following method to the class:
func populateMap() {
mapView.removeAnnotations(mapView.annotations) // 1
specimens = try! Realm().objects(Specimen.self) // 2
// Create annotations for each one
for specimen in specimens { // 3
let coord = CLLocationCoordinate2D(
latitude: specimen.latitude,
longitude: specimen.longitude);
let specimenAnnotation = SpecimenAnnotation(
coordinate: coord,
title: specimen.name,
subtitle: specimen.category.name,
specimen: specimen)
mapView.addAnnotation(specimenAnnotation) // 4
}
}
Reviewing each numbered comment, you:
- Clear out all the existing annotations on the map to start fresh.
- Refresh your
specimens
property. - Loop through
specimens
and create aSpecimenAnnotation
with the coordinates of the specimen, as well as itsname
andcategory
. - Add each
specimenAnnotation
to theMKMapView
.
You need to call this method from somewhere. Find viewDidLoad()
and add this line to the end of its implementation:
populateMap()
That ensures that the map shows the specimens when the view controller loads.
Now you’ll change your annotation to include the specimen name and category. Find unwindFromAddNewEntry(segue:)
and replace the method with the following implementation:
@IBAction func unwindFromAddNewEntry(segue: UIStoryboardSegue) {
let addNewEntryController = segue.source as! AddNewEntryViewController
let addedSpecimen = addNewEntryController.specimen!
let addedSpecimenCoordinate = CLLocationCoordinate2D(
latitude: addedSpecimen.latitude,
longitude: addedSpecimen.longitude)
if let lastAnnotation = lastAnnotation {
mapView.removeAnnotation(lastAnnotation)
} else {
for annotation in mapView.annotations {
if let currentAnnotation = annotation as? SpecimenAnnotation {
if currentAnnotation.coordinate.latitude == addedSpecimenCoordinate.latitude &&
currentAnnotation.coordinate.longitude == addedSpecimenCoordinate.longitude {
mapView.removeAnnotation(currentAnnotation)
break
}
}
}
}
let annotation = SpecimenAnnotation(
coordinate: addedSpecimenCoordinate,
title: addedSpecimen.name,
subtitle: addedSpecimen.category.name,
specimen: addedSpecimen)
mapView.addAnnotation(annotation)
lastAnnotation = nil;
}
The system calls this method once you’ve returned from AddNewEntryController
and there’s a new specimen to add to the map. When you add a new specimen to the map, it gets the generic annotation icon. With your category, you want to change that icon to the category-specific icon.
Here, you remove the last annotation added to the map and replace it with one that shows the specimen’s name and category.
Build and run. Create some new specimens of different categories and see how the map updates:
A Different View
You might have noticed the Log button in the top-left of the map view. In addition to the map, the app also has a text-based table view listing of all annotations called the Log View. Next, you’ll populate this table view with some data.
Open LogViewController.swift and import RealmSwift
:
import RealmSwift
Then, replace the specimens
property with the following:
var specimens = try! Realm().objects(Specimen.self)
.sorted(byKeyPath: "name", ascending: true)
In the code above, you replace the placeholder array with Results
that holds Specimen
s as you did in MapViewController
. They’ll be sorted by name
.
Next, add the following to tableView(_:cellForRowAt:)
before return cell
:
let specimen = specimens[indexPath.row]
cell.titleLabel.text = specimen.name
cell.subtitleLabel.text = specimen.category.name
switch specimen.category.name {
case "Uncategorized":
cell.iconImageView.image = UIImage(named: "IconUncategorized")
case "Reptiles":
cell.iconImageView.image = UIImage(named: "IconReptile")
case "Flora":
cell.iconImageView.image = UIImage(named: "IconFlora")
case "Birds":
cell.iconImageView.image = UIImage(named: "IconBird")
case "Arachnid":
cell.iconImageView.image = UIImage(named: "IconArachnid")
case "Mammals":
cell.iconImageView.image = UIImage(named: "IconMammal")
default:
cell.iconImageView.image = UIImage(named: "IconUncategorized")
}
This method populates the cell with the specimen’s name and category.
Build and run your app. Tap Log and you’ll see all of your entered specimens in the table view like so: