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:
-
UITableViewto display the list of items. - Core Data framework to store and retrieve the item data.
-
NSFetchedResultsControllerto 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
.xcdatamodeldfile and defining your entity with the required attributes (name,artist,year). - Design the table view controller scene in Interface Builder by adding a
UITableViewControllersubclass. - 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@objcto make it accessible from Objective-C code. - The
nameArrayandidArrayare cleared usingremoveAll(keepingCapacity:)method, ensuring a clean slate before fetching new data. - The
AppDelegateinstance is obtained usingUIApplication.shared.delegate. This allows access to the persistent container and the Core Data context. - A
NSFetchRequestis created with the entity name "Paintings" to specify the entity from which data needs to be fetched.returnsObjectsAsFaultsis set tofalseto 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 theresultsarray. - 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
UUIDand appends it to theidArray. - Finally, after all the data is fetched and appended to the arrays, the
tableViewis 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 == .deletecondition checks if the editing style is set to.delete, indicating that the user wants to delete the cell. - The code retrieves the
AppDelegateinstance and the Core Datacontextfrom the shared application delegate. - A
fetchRequestis created with the entity name "Paintings" to specify the entity from which data needs to be fetched. - The
idStringvariable is assigned the UUID string value of theidat the corresponding row index in theidArray. - A predicate is set on the fetch request using
NSPredicateto specify the condition for the fetch request. In this case, it filters the objects based on theiridattribute matching theidString. -
returnsObjectsAsFaultsis set tofalseto 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 theresultsarray. - If there are results (
results.count > 0), the code enters the loop. - Inside the loop, the code checks if the
idattribute of the fetched object matches theidat 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
contextusingcontext.delete(result). - The corresponding elements in
nameArrayandidArrayare 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!
Top comments (0)