Table View Controller with Core Data and Navigation Controller in Swift
Fatih Yavuz
Posted on June 13, 2023
This document provides an introduction to creating a table view controller in Swift using Core Data and a navigation controller. The combination of these technologies allows you to create a list-based interface with persistent data storage and navigation capabilities. This setup is commonly used in iOS apps to display and manage a collection of items.
Prerequisites
Before proceeding with this tutorial, ensure that you have the following:
- Xcode installed on your macOS machine.
- Basic knowledge of Swift programming language.
- Familiarity with Core Data concepts.
- Understanding of iOS app development using Storyboard.
Overview
In this tutorial, we will create a table view controller that displays a list of items using Core Data for data persistence. Each item will have attributes such as a name, artist, and year. The table view cells will present the item names, and tapping on a cell will navigate to a details view to view and edit the item's information.
To implement this, we'll use the following components:
-
UITableView
to display the list of items. - Core Data framework to store and retrieve the item data.
-
NSFetchedResultsController
to efficiently manage and update the table view's data. - Navigation controller to handle navigation between the table view and details view.
Steps
- Create a new Swift project in Xcode.
- Set up Core Data in your project by adding an
.xcdatamodeld
file and defining your entity with the required attributes (name
,artist
,year
). - Design the table view controller scene in Interface Builder by adding a
UITableViewController
subclass. - Implement the Core Data stack and create a managed object context in the app delegate.
- Implement the fetched results controller in the table view controller to fetch and manage the data from Core Data.
- Set up the table view data source methods in the table view controller to display the fetched data in the cells.
- Add a navigation controller to the storyboard and embed the table view controller in it.
- Create a segue from the table view cell to the details view controller for navigation.
- Implement the details view controller to display and edit the item's information.
- Add functionality to the table view controller for adding, deleting, and updating items using Core Data.
- Test your app on the iOS Simulator or a physical device to verify its functionality.
class TableViewController : UITableViewController
The document contains Swift code defining a TableViewController
as a subclass of UITableViewController
.
var nameArray = [String]()
var idArray = [UUID]()
In this particular line of code, we initialize the nameArray
and idArray
to empty arrays. This means that the arrays has no elements in it yet, but can be populated with String
and UUID
elements later in the code.
override func viewDidLoad() {
super.viewDidLoad()
//tableView config
tableView.dataSource = self
tableView.delegate = self
}
A Swift code snippet that configures a tableView
's dataSource
and delegate
in the viewDidLoad()
method.
// Table View Rows
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return nameArray.count
}
// Table View Row Texts
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = nameArray[indexPath.row]
return cell
}
// On Row selection
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Set id to tappedId to access tappedId value in prepare segue func
tappedId = idArray[indexPath.row]
self.performSegue(withIdentifier: "toDetailVC", sender: nil)
}
This is a Swift code that sets up a table view with rows and row texts. The function tableView(_:numberOfRowsInSection:)
returns the number of rows in a section, which is equal to the number of elements in the nameArray
. The function tableView(_:cellForRowAt:)
returns a table view cell that displays the name of an element in the nameArray
at the corresponding indexPath.row
. The function tableView(_:didSelectRowAt:)
is called when a cell is selected, it sets the variable tappedId
to the value at idArray[indexPath.row]
and performs a segue to the view controller with identifier "toDetailVC".
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toDetailVC" {
let destinationVC = segue.destination as! DetailViewController
destinationVC.selectedId = tappedId
tappedId = nil
}
}
This is a Swift code snippet that overrides the prepare
method in a view controller. It checks if the segue identifier is "toDetailVC" and if so, it sets the selectedId
property of the destination view controller to tappedId
and then sets tappedId
to nil
.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
nameArray.remove(at: indexPath.row)
}
}
Removes a row from a table view when the user swipes left and taps "Delete." The nameArray
is the data source for the table view, and indexPath.row
is the index of the row to be removed.
Using Core Data In Table View
@objc func getData() {
nameArray.removeAll(keepingCapacity: false)
idArray.removeAll(keepingCapacity: false)
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Paintings")
fetchRequest.returnsObjectsAsFaults = false
do {
let results = try context.fetch(fetchRequest)
if results.count > 0 {
for result in results as! [NSManagedObject] {
if let name = result.value(forKey: "name") as? String {
self.nameArray.append(name)
}
if let id = result.value(forKey: "id") as? UUID {
self.idArray.append(id)
}
self.tableView.reloadData()
}
}
} catch {
print("error")
}
}
This code snippet retrieves data from a Core Data entity named "Paintings" and populates two arrays (nameArray
and idArray
) with the fetched data. Here's a breakdown of the code:
- The
getData()
function is marked with@objc
to make it accessible from Objective-C code. - The
nameArray
andidArray
are cleared usingremoveAll(keepingCapacity:)
method, ensuring a clean slate before fetching new data. - The
AppDelegate
instance is obtained usingUIApplication.shared.delegate
. This allows access to the persistent container and the Core Data context. - A
NSFetchRequest
is created with the entity name "Paintings" to specify the entity from which data needs to be fetched.returnsObjectsAsFaults
is set tofalse
to ensure that the fetched objects are fully realized (not just faulted) and contain the actual attribute values. - The fetch request is executed using
context.fetch(fetchRequest)
, and the results are stored in theresults
array. - If there are results (
results.count > 0
), the code iterates through each fetched object. - Inside the loop, the code retrieves the "name" attribute value using
value(forKey:)
and casts it to aString
. If the cast is successful, the name is appended to thenameArray
. - Similarly, the code retrieves the "id" attribute value as a
UUID
and appends it to theidArray
. - Finally, after all the data is fetched and appended to the arrays, the
tableView
is reloaded usingtableView.reloadData()
to reflect the updated data in the UI. - In case an error occurs during the fetch operation, the catch block is executed and "error" is printed to the console.
This code assumes that you have a Core Data entity named "Paintings" with attributes "name" (String) and "id" (UUID). The nameArray
and idArray
are assumed to be instance variables of the class containing this code.
Note: You should call getData()
in the viewDidLoad
method to ensure data is loaded when the app opens.
Note: It's important to handle any errors that may occur during Core Data fetch operations. Printing "error" to the console is a minimal error handling approach for demonstration purposes. In a production environment, you should consider handling errors more gracefully, such as displaying an alert to the user or logging detailed error information.
override func viewWillAppear(_ animated: Bool) {
NotificationCenter.default.addObserver(self, selector: #selector(getData), name: NSNotification.Name(rawValue: "newData"), object: nil)
}
This code snippet adds an observer to the default NotificationCenter
in the viewWillAppear
method of a view controller. The observer listens for a specific notification named "newData" and calls the getData()
method when the notification is received.
Update cell deletion method from table view using Core Data
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Paintings")
let idString = idArray[indexPath.row].uuidString
fetchRequest.predicate = NSPredicate(format: "id = %@", idString)
fetchRequest.returnsObjectsAsFaults = false
do {
let results = try context.fetch(fetchRequest)
if results.count > 0 {
for result in results as! [NSManagedObject] {
if let id = result.value(forKey: "id") as? UUID {
if id == idArray[indexPath.row] {
context.delete(result)
nameArray.remove(at: indexPath.row)
idArray.remove(at: indexPath.row)
self.tableView.reloadData()
do {
try context.save()
} catch {
print("error")
}
break
}
}
}
}
} catch {
print("error")
}
}
}
This code snippet is an implementation of the tableView(_:commit:forRowAt:)
method, which is a delegate method for handling editing actions in a UITableView
. Specifically, this implementation handles the delete action for a table view cell. Here's an explanation of the code:
- The method is called when the user performs a commit action, such as swiping on a table view cell and tapping the delete button.
- The
if editingStyle == .delete
condition checks if the editing style is set to.delete
, indicating that the user wants to delete the cell. - The code retrieves the
AppDelegate
instance and the Core Datacontext
from the shared application delegate. - A
fetchRequest
is created with the entity name "Paintings" to specify the entity from which data needs to be fetched. - The
idString
variable is assigned the UUID string value of theid
at the corresponding row index in theidArray
. - A predicate is set on the fetch request using
NSPredicate
to specify the condition for the fetch request. In this case, it filters the objects based on theirid
attribute matching theidString
. -
returnsObjectsAsFaults
is set tofalse
to ensure the fetched objects are fully realized (not faulted) and contain the actual attribute values. - The fetch request is executed using
context.fetch(fetchRequest)
, and the results are stored in theresults
array. - If there are results (
results.count > 0
), the code enters the loop. - Inside the loop, the code checks if the
id
attribute of the fetched object matches theid
at the corresponding row index in theidArray
. - If there is a match, it means the correct object has been found for deletion. The code proceeds to delete the object from the Core Data
context
usingcontext.delete(result)
. - The corresponding elements in
nameArray
andidArray
are also removed at the same index to keep the data in sync. - The table view is reloaded using
tableView.reloadData()
to reflect the updated data in the UI. - The changes are saved to the Core Data context using
context.save()
. - If any error occurs during the deletion or saving process, the catch block is executed, and "error" is printed to the console.
This code assumes you have a Core Data entity named "Paintings" with an attribute named "id" of type UUID. The nameArray
and idArray
are assumed to be instance variables of the class containing this code.
It's important to handle any errors that may occur during Core Data operations more gracefully in a production environment, such as displaying an alert to the user or logging detailed error information.
Navigation Bar Add Button
navigationController?
.navigationBar.topItem?
.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.add,
target: self,
action: #selector(addTapped))
This code snippet sets the rightBarButtonItem
of the navigation bar in a view controller's navigationBar
to a system-defined "Add" button. When the "Add" button is tapped, it triggers the addTapped
method on the current view controller.
Note: You should call this line of code in the viewDidLoad
method to ensure button is rendered when the app opens.
@objc func addTapped(){
performSegue(withIdentifier: "toDetailsVC", sender: nil)
}
This code snippet is an implementation of the addTapped
method, which is called when the "Add" button is tapped in the navigation bar. The method performs a segue to navigate to another view controller with the identifier "toDetailsVC".
Conclusion
By following this document, you have learned how to create a table view controller with Core Data and a navigation controller in Swift. This setup enables you to build a list-based interface with persistent data storage and navigation capabilities. You can now extend the functionality of your app by adding additional features, such as search, sorting, and filtering based on the stored data.
Remember to consult the official Apple documentation and other online resources for further details and best practices while working on your iOS app development projects. Happy coding!
Posted on June 13, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 29, 2024