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.

4.4 (23) · 1 Review

Download materials
Save for later
Share
You are currently viewing page 4 of 4 of this article. Click here to view the first page.

Fetches With Predicates

You want your app to rock, so you’ll need a handy search feature. Your starter project contains an instance of UISearchController; You’ll add a few modifications specific to your app to make it work with Realm.

In LogViewController.swift, replace the searchResults property with the following:

var searchResults = try! Realm().objects(Specimen.self)

Add this method to the class:

func filterResultsWithSearchString(searchString: String) {
  let predicate = NSPredicate(format: "name BEGINSWITH [c]%@", searchString) // 1
  let scopeIndex = searchController.searchBar.selectedScopeButtonIndex // 2
  let realm = try! Realm()
    
  switch scopeIndex {
  case 0:
    searchResults = realm.objects(Specimen.self)
      .filter(predicate).sorted(byKeyPath: "name", ascending: true) // 3
  case 1:
    searchResults = realm.objects(Specimen.self).filter(predicate)
      .sorted(byKeyPath: "created", ascending: true) // 4
  default:
    searchResults = realm.objects(Specimen.self).filter(predicate) // 5
  }
}

Here’s what the above function does:

  1. First, you create a predicate which searches for names that start with searchString. The [c] that follows BEGINSWITH indicates a case insensitive search.
  2. You then grab a reference to the currently selected scope index from the search bar.
  3. If the first segmented button is selected, sort the results by name ascending.
  4. If the second button is selected, sort the results by created date ascending.
  5. If none of the buttons are selected, don’t sort the results, take them in the order they’re returned from the database.

Now, you’ll actually perform the filtering when the user interacts with the search field. In updateSearchResults(for:), add the following two lines at the beginning of the method:

let searchString = searchController.searchBar.text!
filterResultsWithSearchString(searchString: searchString)

Since the search results table view calls the same data source methods, you need to make a small change to tableView(_:cellForRowAt:) to handle both the main log table view and the search results. In that method, find the line that assigns to specimen:

let specimen = specimens[indexPath.row]

Delete it and replace it with the following:

let specimen = searchController.isActive ?
  searchResults[indexPath.row] : specimens[indexPath.row]

This code checks whether the searchController is active. If so, it retrieves the specimen from searchResults. If not, it retrieves the specimen from specimens instead.

Next, you’ll add a function to sort the returned results when the user taps a button in the scope bar.

Add the following implementation to scopeChanged(sender:):

let scopeBar = sender as! UISegmentedControl
let realm = try! Realm()
  
switch scopeBar.selectedSegmentIndex {
case 1:
  specimens = realm.objects(Specimen.self)
    .sorted(byKeyPath: "created", ascending: true)
default:
  specimens = realm.objects(Specimen.self)
    .sorted(byKeyPath: "name", ascending: true)
}
  
tableView.reloadData()

Here, you check which scope button is pressed (A-Z, or Date Added) and sort. By default, the list will sort by name.

Build and run your app. Try a few different searches and see what you get for results.

Specimen search result

Updating Records

You’ve covered adding records, but what about when you want to update them?

If you tap in a cell in LogViewController, you’ll segue to AddNewEntryViewController, but with the fields empty. The first step to letting the user edit the fields is to show the existing data.

Open AddNewEntryViewController.swift and add the following helper method to the class:

func fillTextFields() {
  nameTextField.text = specimen.name
  categoryTextField.text = specimen.category.name
  descriptionTextField.text = specimen.specimenDescription

  selectedCategory = specimen.category
}

This method fills in the user interface with the specimen data. Remember, AddNewEntryViewController has, up to this point, only been used for new specimens, so those fields have always started empty.

Next, add the following lines to the end of viewDidLoad():

if let specimen = specimen {
  title = "Edit \(specimen.name)"
      
  fillTextFields()
} else {
  title = "Add New Specimen"
}

This code sets the navigation title to indicate whether the user is adding or updating a specimen. If it’s an existing specimen, you also call your helper method to populate the fields.

You need a method to update the specimen record with the user’s changes. Add the following method:

func updateSpecimen() {
  let realm = try! Realm()
    
  try! realm.write {
    specimen.name = nameTextField.text!
    specimen.category = selectedCategory
    specimen.specimenDescription = descriptionTextField.text
  }
}

As usual, the method begins with getting a Realm instance and then the rest is wrapped inside a write() transaction. Inside the transaction you update the data fields.

Six lines of code to update the Specimen record is all it takes! :]

Next, you’ll call the above method when the user taps Confirm. Find shouldPerformSegue(withIdentifier:sender:) and replace the call to addNewSpecimen() with the following:

if specimen != nil {
  updateSpecimen()
} else {
  addNewSpecimen()
}

This calls your method to update the data when appropriate.

Open LogViewController.swift and add the following implementation for prepare(for:sender:):

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  if (segue.identifier == "Edit") {
    let controller = segue.destination as! AddNewEntryViewController
    var selectedSpecimen: Specimen!
    let indexPath = tableView.indexPathForSelectedRow
      
    if searchController.isActive {
      let searchResultsController =
        searchController.searchResultsController as! UITableViewController
      let indexPathSearch = searchResultsController.tableView.indexPathForSelectedRow
        
      selectedSpecimen = searchResults[indexPathSearch!.row]
    } else {
      selectedSpecimen = specimens[indexPath!.row]
    }
      
    controller.specimen = selectedSpecimen
  }
}

You’ll pass the selected specimen to the AddNewEntryController instance. The complication with the if/else is because getting the selected specimen is different depending on whether or not the user is looking at search results.

Build and run your app. Open the Log view and tap on a Specimen. You’ll see the details with all of the fields populated and ready for editing.

Edit Specimen

Where to Go From Here?

You can download the final project using the Download materials button at the top or bottom of this tutorial.

In this tutorial, you learned how to create, update and fetch records from a Realm database. You also learned how to use predicates and sort the results by their properties.

Many features of Realm were not covered in this tutorial, but you can learn about those topics and much more in the official documentation or with our book, Realm: Building Modern Swift Apps with Realm Database.

Thank you for taking the time to read this tutorial. I hope you enjoyed it :)

If you have any comments or questions on this tutorial, please join the discussion below!