Swift Database Functions

In this swift tutorial you will download an iOS project and use it to perform database functions without the use of the NSFetchedResultsController class. If you want to learn how to use the NSFetchedResultsController class in a Core Data application, then read this post:

SWIFT CORE DATA FROM SCRATCH.

Download The Project

The first thing you have to do is download the TaskList project. When you finish unzipping the file and transferring the TaskList folder to the Interactive Swift Programming folder; take the app for spin to see how it work.

The Project Creation Steps

I created the TaskList project by doing the following:

  1. I selected the “Single View Application” template.
  2. I provided options for the projects.
  3. On the final window; I selected the “Interactive Swift Programming” folder and ticked the “Source Control” checkbox. I then clicked the “Create” button.

tasklist-create-project

By the way, by ticking the “Use Core Data” checkbox, I told the template to setup the Core Data stack in the AppDelegate.swift file for me. It consist of four variables and one function:

  • applicationDocumentsDirectory
  • managedObjectModel
  • persistentStoreCoordinator
  • managedObjectContext
  • saveContext()
Project Modifications

Once Xcode created the TaskList project for me, I modified the Main.storyboard file, deleted the ViewController.swift file from the project, added two class files (TaskListViewController and AddUpdateViewController.swift) in the project and connected them to their respective storyboard scenes.

I selected the TaskList.xcdatamodeld file, then the “Create NSManaged Subclass…” item from the “Editor” menu to create the Task.swift class file. Finally, I added the project name to the Task entity. This image below shows how I did that.

tasklist-fig09

As you can see in the image below, the Task entity has only two attributes and both of them are of type String.

tasklist-datamodel-file

You will use the Task.swift class file to create managed objects and Core Data code to insert them in the app’s database file. The image below shows the Task.swift file’s source code. You will not modify it.

tasklist-task-class

Here’s what the Project Navigator look like now.

tasklist-projectnav

The Storyboard Scenes

The Main.storyboard file contain two scenes and a Navigation Controller. As you can see in the image below, the Task List scene is embedded in the Navigation Controller. Both the Task List and The Add Task scene serves as the application’s views.

tasklist-storyboard-scenes

Database Functions

You will add code in the AddTaskViewController.swift file and the TaskListViewController.swift file to perform these database functions.

  1. Insert a managed object (task) in the database
  2. Fetch and display managed objects (tasks) in the table view cells
  3. Sort fetched managed objects in alphabetical order
  4. Update a managed object in the database
  5. Search the database for a managed object and display them in the table view cells
  6. Delete a managed object from the table view and the database
Create a New Branch

Before get started in implementing above database functions in the TaskList project. Use the Source Control menu to create a new branch from the master branch. Give it the name, create-task.

Insert a Managed Object in The Database

When you enter data in the Add Task View’s text fields and click the navigation bar’s Done button, the app should validate the required input field; which is the Task Title text field. If validation passes, the app should create and insert a managed object in the database file. Here’s the code to do that. Enter it in the AddUpdatedViewController.swift file.

Listing 1

func insertManagedObject() {
func insertManagedObject() {
        let entityDescription = NSEntityDescription.entityForName("Task", inManagedObjectContext: context)
        let task = Task(entity: entityDescription!, insertIntoManagedObjectContext: context)
        var error: NSError?

        task.taskTitle = taskTitle.text;
        task.taskDescription = taskDescription.text

        if context.save(&error) == true {
            taskTitle.text = nil
            taskDescription.text = nil
        } else {
            println(error?.localizedDescription)
        }

        self.showAlertViewMessage("Task saved")
    }
}

Here’s what the code you entered in the insertManagedObject() function does and output you’ll see on the Sim’s screen.

  • tasklist-fig01Create a task object variable in the context.
  • Set the task object’s properties with data you entered in the Add Task view’s text fields.
  • Save the managed object that’s in the context in the database file.
  • Clear out the view’s text fields.
  • Call the showAlertViewMessage() function.
Fetch and Display Managed Objects

Ok, so you entered say, about five tasks in the app’s database file. When you return to the Task List view, they should show up in the table view cells. To make that happen you have to add code in the TaskListViewController.swift class file to fetch all managed objects from the app’s database and load there properties in the table view cells.

The first step to populating the Task List’s table view cells with managed objects’ properties is to add code shown in Listing 2, in the TaskListViewController.swift.

Listing 2

func fetchManagedObjects() {
    let freq = NSFetchRequest(entityName: "Task")
    var error: NSError?
    let fetchedResults = context.executeFetchRequest(freq, error: &error)

    if let results = fetchedResults {
        for object in results {
            let row = object as Task
            dataSource.append(row)
        }
    } else {
        println("Could not fetch data from database\n\(error), \(error!.userInfo)")
    }
}

The second step is to call the fetchManagedObject() function in the viewDidLoad() function and the viewWillAppear() function.

Listing 3

override func viewDidLoad() {
  super.viewDidLoad()
  fetchManagedObjects()
}

override func viewWillAppear(animated: Bool) {
  dataSource.removeAll()
  fetchManagedObjects()
}

Here’s why you called the fetchManagedObject() function in both functions:

  • The viewDidLoad() function is fired only once. That happens when you launch the app in the sim. You called the fetchManagedObject() in that function to to pretty much load the table view’s dataSource object variable with managed objects fetched from the app’s database file.
  • The viewWillAppear() function is fired before the Task List View appear on the sim’s screen. That happens when you click the Add Task view’s navigation bar button, “< Task List”.  If you didn’t implement the viewWillAppear() function in the class file; when you return to the Task List View, new tasks you entered in the database will not show up in the table view cells. Now, the statements you entered in the viewWillAppear() function clear out the table view’s dataSource object and re-populated it by calling the fetchManagedObjects() function.

The final step is to add code shown in Listing 4, in the TaskListViewController.swift file.

Listing 4

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell

    // Configure and return the cell
    var task: Task = dataSource[indexPath.row]
    cell.textLabel!.text = task.taskTitle
    return cell
}

When you finish entering Listing 4 code in the file; run the app in the sim and you will see output shown in the image below; assuming you’ve entered only five tasks in the database file.

tasklist-fig02

Sort Fetched Managed Objects

Tasks aren’t shown in alphabetical order in the Task List’s table view cells. To fix that you have to basically sort fetched managed objects (tasks) in alphabetical order before displaying them in the Task List’s table view cells. To make that happen; you have to modify the fetchedManagedObjects() function’s code as shown in Listing 5.

Listing 5

func fetchManagedObjects() {
    let freq = NSFetchRequest(entityName: "Task")
    var error: NSError?
    let sortDescriptor = NSSortDescriptor(key: "taskTitle", ascending: true)
    freq.sortDescriptors = [sortDescriptor]
    let fetchedResults = context.executeFetchRequest(freq, error: &error)

    if let results = fetchedResults {
        for object in results {
            let row = object as Task
            dataSource.append(row)
        }
    } else {
        println("Could not fetch data from database\n\(error), \(error!.userInfo)")
    }
}

Here’s what the new code you entered in the insertManagedObject() function does and output you’ll see on the Sim’s screen.

  • Create a sort descriptor array object that sorts the Core Data managed objects’ taskTitle properties in alphabetical order (ascending: true).
  • Set the fetch request’s sortDescriptors property, so it includes the sort descriptor array object.

tasklist-fig03

Update A Managed Object in The Database

To update a managed object in the application’s database file, you’ll have to add code in the AddUpdatedViewController.swift file’s updateManagedObject() function. Listing 6 shows the code to add in the function.

Listing 6

func updateManagedObject() {
    selectedTask.taskTitle = taskTitle.text
    selectedTask.taskDescription = taskDescription.text
    var error: NSError?

    if context.save(&error) == true {
        self.navigationController!.popViewControllerAnimated(true)
    } else {
        println(error?.localizedDescription)
    }
}

Here’s what code you entered in the updateManagedObject() function does:

  • Set the selectedTask object’s properties with the view’s text fields values. By the way, the selectedTask object is a managed object that currently residing in the context. So you are basically updating the context’s managed object.
  • Call the save() function on the context, in an if statement. The save() function basically saves changes you made to the context’s managed object, in the database file.
  • The statement in the if block dismiss the Add Task View so you see the Task List View on the sim’s screen.
  • Code in the else block print the error message the save() function returns, in Xcode’s Debugger console. That will only happen, if for some reason the save() function is unable to do its job.

Don’t run the app yet; add this statement below existing code, in the viewWillAppear() function:

tableView.reloadData()

Since you dismissed the Add Task view from the navigation stack, you have to reload the Task List’s table view, so changes you made to the selectedTask object, in the database, is reflected in the table view cell.

A note on dismissing the Add Task View

Remember, you dismissed the Add Task View in the updateManagedObject() function with this statement:

self.navigationController!.popViewControllerAnimated(true)

Original I used this statement:

self.dismissViewControllerAnimated(true, completion: nil)

But that statement did not dismiss the Add Task View. I’m guessing the reason for this is, because the AddUpdateViewController is loading the Add Task View on the navigation stack as a popover view that cover the Task List View. I came to this conclusion because I’m able to click-drag the Add Task View to partially reveal the Task List View.

tasklist-fig05

I’d love to hear (read) your thoughts on this, via the post’s “Comment System”.

Test The Code

Go ahead and run the app in the sim. You will see output shown in the image below, assuming you’ve modified the table view cell I highlighted in the image.

tasklist-fig04

Search and Display Managed Objects

See the Search Bar on the Task List View. Well, you have to add code in the TaskListViewController.swift file to search the database for a managed object (task) and display results in the Task List’s table view cells. To do that, add code shown in Listing 7 in the file.

Listing 7

func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
    searchBar.setShowsCancelButton(true, animated: true)

    if !searchText.isEmpty {
        dataSource.removeAll()
        let fetchRequest = NSFetchRequest(entityName: "Task")

        fetchRequest.fetchLimit = 25
        fetchRequest.predicate = NSPredicate(format: "taskTitle contains[cd] %@", searchText)
        let sortDescriptor = NSSortDescriptor(key: "taskTitle", ascending: true)
        fetchRequest.sortDescriptors = [sortDescriptor]

        var error: NSError? = nil
        if let fetchedResults = context.executeFetchRequest(fetchRequest, error: &error) {
            dataSource = fetchedResults as [Task]
        } else {
            println("Search Request Failed\n \(error?.localizedDescription)")
        }

        tableView.reloadData()
    }
}

func searchBarCancelButtonClicked(searchBar: UISearchBar!) {
    searchBar.text = nil
    searchBar.showsCancelButton = false
    searchBar.resignFirstResponder()
    fetchManagedObjects()
    tableView.reloadData()
}

First of all, you implemented two delegate functions of the UISearchBarDelegate protocol in the file. For them to work, make sure the Search Bar’s searchBar and delegate properties are connected to the Task List View Controller scene’s object. The connection should look like this in Interface Builder:

tasklist-fig06

Now, the Search Bar’s textDidChange() function is fired when you start typing in the Search Bar’s text field. Code you entered in the function does the following:

  • Show the Cancel button on the Search Bar.
  • Empty out the table view’s dataSource object.
  • Create a fetchRequest object variable.
  • Set the number of managed objects the fetchRequest should fetch from the database.
  • Set the fetchRequest condition; in other words, you told the fetchRequest object which database field to perform the search operation on. The condition is this: "taskTitle contains[cd] %@"
  • Tell the fetch request object to sort fetched managed objects in alphabetical order.
  • Execute the fetch request, unwrap fetched results and store them in the fetchedResults object variable.
  • Cast the fetchedResults object’s type as an array of Task, before assigning it to the table view’s dataSource object.
  • If the fetchRequest failed, then print the error message it returns in Xcode’s Debugger console.
  • Refresh the Task List’s table view so the dataSource objects are shown in its cells.

As for the Search Bar’s searchBarCancelButtonClicked() function, it is fired when you click the Search Bar’s Cancel button. Code you entered in the function does the following:

  • Empty out the table view’s dataSource object.
  • Remove text from the Search Bar’s text field.
  • Hide the Search Bar’s Cancel button.
  • Dismiss the on-screen keyboard.
  • Call the fetchManagedObject() function. As you already know, that function fetch all managed objects from the application’s database file and load them in the table view’s dataSource object.
  • Refresh the Task List’s table view so the dataSource objects are shown in its cells.

Now, this image show output you’ll see on the sim’s screen when you use the Search Bar to search for a task.

tasklist-fig07

This image shows output you’ll see on the sim’s screen when you click the Search Bar’s Cancel button.

tasklist-fig08

Delete a Manage Object

The last database task you have to do is delete a managed object (task) from the Task List’s table view and the app’s database file. To implement the “delete managed object” function in the TaskList project; add code shown in Listing 8, in the TaskListViewController.swift file’s commitEditingStyle() function.

Listing 8

func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
    if editingStyle == .Delete {
        let taskToDelete = dataSource[indexPath.row]
        deleteManagedObject(taskToDelete, index: indexPath)
        tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
    }
}

The commitEditingStyle() is a data source function of the table view and its job is to delete the swiped table view cell. Now, code you entered in the function calls a function to handle the task of deleting the swiped table view cell’s managed object from the app’s database file and the table view’s dataSource object. The last statement in the commitEditingStyle() function tell the table view to animated out the deleted cell.

Here’s the code to implement the deleteManagedObject() function.

Listing 9

func deleteManagedObject(let taskToDelete: Task, let index: NSIndexPath) {
    context.deleteObject(taskToDelete)

    var savingError: NSError?
    if !context.save(&savingError) {
        println("Delete managed object operation failed\n \(savingError?.localizedDescription)")
    }

    dataSource.removeAtIndex(index.row)
}

This image show output you’ll see on the sim’s screen when you delete four of the table view cells.

tasklist-fig10

You’ve reached the end of the tutorial, Swift Database Functions. I hope you had fun learning how to implement these database functions in the tutorial’s TaskList project:

  1. Insert a managed object (task) in the database
  2. Fetch and display managed objects (tasks) in the table view cells
  3. Sort fetched managed objects in alphabetical order
  4. Update a managed object in the database
  5. Search the database for a managed object and display them in the table view cells
  6. Delete a managed object from the table view and the database

If you want to download the TaskList project as it stands now, then click here. Comments are welcomed! 🙂