Storing data on Parse is built around the Parse.Object class. Each Parse.Object contains key-value pairs of JSON-compatible data. This data is schemaless, which means that you don’t need to specify ahead of time what keys exist on each Parse.Object. You can simply set whatever key-value pairs you want, and our backend will store it.
You can also specify the datatypes according to your application needs and persist types such as number, boolean,string, DateTime, list, GeoPointers, and Object, encoding them to JSON before saving. Parse also supports store and query relational data by using the types Pointers and Relations.
In this guide, you will learn how to perform basic data operations through a CRUD example app (ToDo list App), which will show you how to create, read, update and delete data from your Parse server database using the ParseSwift SDK.
This tutorial uses a basic app created in Xcode 12 and iOS 14.
At any time, you can access the complete Project via our GitHub repositories.
To better understand the ParseSwift SDK you will perform CRUD operations on a To-do List App. The application database will have a simple task class with a title and a description (both strings). You can update each task’s title and/or description.
Quick reference of commands we are going to use
Once an object conforms the ParseSwift protocol, it automatically implements a set of methods that will allow you to manage the object and update any changes on your Back4App Database. Given the object ToDoListItem
Swift
1 struct ToDoListItem: ParseObject {2...34/// Title for the todo item5 var title: String?67/// Description for the todo item8 var description: String?9}
these methods are listed below.
Create
Read
Update
Delete
//Once created an instance of ToDoListItem object and set its custom properties, you can save it on your Back4App Database by calling any of the following methods1 var newItem: ToDoIListtem
2// newItem's properties34// Saves newItem on your Back4App Database synchronously and returns the new saved Item. It throws and error if something went wrong.5 let savedItem =try? newItem.save()67// Saves newItem on your Back4App Database asynchronously, and passes a Result<ToDoListItem, ParseError> object to the completion block to handle the save proccess.8 newItem.save { result in9// Handle the result to check the save was successfull or not10}11
1 - Create To-do List App Template
At any time, you can access the complete Project via our GitHub repositories.
Go to Xcode, and find the SceneDelegate.swift file. In order to add a navigation bar on top of the app, we setup a UINavigationController as the root view controller in the following way
Swift
1 class SceneDelegate: UIResponder, UIWindowSceneDelegate {23 var window: UIWindow?45 func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions){6 guard let scene =(scene as? UIWindowScene)else{return}78 window =.init(windowScene: scene)9 window?.rootViewController =UINavigationController(rootViewController:ToDoListController())10 window?.makeKeyAndVisible()1112// Additional logic13}1415...16}
The root view controller class (ToDoListController) for the navigation controller is a subclass of UITableViewController, this makes easy to layout a list of items.
2 - Setup the CRUD object
Objects you want to save on your Back4App Database have to conform the ParseObject protocol. On our To-do Liat app this object is ToDoListItem. Therefore, you first need to create this object:
Swift
1 import Foundation
2 import ParseSwift
34 struct ToDoListItem: ParseObject {5// Required properties from ParseObject protocol6 var objectId: String?7 var createdAt: Date?8 var updatedAt: Date?9 var ACL: ParseACL?1011/// Title for the todo item12 var title: String?1314/// Description for the todo item15 var description: String?16}
This object defines a class in your Back4App Database. Any new instance of this object is then stored in your database under the ToDoListItem class.
3 - Setup ToDoListController
In ToDoListController we should implement all the necessary configuration for the navigationBar, and tableView properties
Swift
1 class ToDoListController: UITableViewController {2 var items:[ToDoListItem]=[]34 override func viewDidLoad(){5 super.viewDidLoad()67setupTableView()8setupNavigationBar()9}1011 private func setupNavigationBar(){12 navigationItem.title ="To-do list".uppercased()13 navigationItem.rightBarButtonItem =UIBarButtonItem(barButtonSystemItem:.add, target: self, action: #selector(handleNewItem))14}1516 private func setupTableView(){17 tableView.register(ToDoListItemCell.self, forCellReuseIdentifier: ToDoListItemCell.identifier)18}1920 override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int)-> Int {21 items.count
22}2324 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)-> UITableViewCell {25 let cell = tableView.dequeueReusableCell(withIdentifier: ToDoListItemCell.identifier,for: indexPath) as! ToDoListItemCell
26 cell.item = items[indexPath.row]27return cell
28}2930/// This method is called when the user wants to add a new item to the to-do list31 @objc private func handleNewItem(){32...33}3435...36}
To conclude this step, we implement the custom table view cell ToDoListItemCell
Swift
1// Content of ToDoListItemCell.swift file2 class ToDoListItemCell: UITableViewCell {3 class var identifier: String {"\(NSStringFromClass(Self.self)).identifier"}// Cell's identifier45/// When set, it updates the title and detail texts of the cell6 var item: ToDoListItem?{7 didSet {8 textLabel?.text = item?.title
9 detailTextLabel?.text = item?.description
10}11}1213 override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?){14 super.init(style:.subtitle, reuseIdentifier: reuseIdentifier)1516 accessoryType =.detailButton // This accessory button will be used to present edit options for the item17}1819 required init?(coder: NSCoder){20 super.init(coder: coder)2122 accessoryType =.detailButton // This accessory button will be used to present edit options for the item23}24}
4 - CRUD flow
We implement all CRUD logic in the ToDoListController class. Go to ToDoListController.swift and add the following methods to the ToDoListController class
Swift
1// MARK: - CRUD Flow2 extension ToDoListController {3/// Creates a ToDoListItem and stores it on your Back4App Database4/// - Parameters:5/// - title: The title for the to-do task6/// - description: An optional description for the to-to task7 func createObject(title: String, description: String?){89}1011/// Retrieves all the ToDoListItem objects from your Back4App Database12 func readObjects(){1314}1516/// Updates a ToDoListItem object on your Back4App Database17/// - Parameters:18/// - objectId: The object id of the ToDoListItem to update19/// - newTitle: New title for the to-to task20/// - newDescription: New description for the to-do task21 func updateObject(objectId: String, newTitle: String, newDescription: String?){2223}2425/// Deletes a ToDoListItem on your Back4App Database26/// - Parameter item: The item to be deleted on your Back4App Database27 func deleteObject(item: ToDoListItem){2829}30}
- Create Object
Now we start implementing the createObject(title:description:) method. Create an instance of ToDoListItem using the init(title:description:) initializer. In order to save this new item on your Back4App Database, the ParseSwift protocol provides a save() method. This method can be called synchronously or asynchronously, choose one of them according to your use case. An asynchrononous implementation should look like this
Swift
1 func createObject(title: String, description: String?){2 let item =ToDoListItem(title: title, description: description)34 item.save {[weak self] result in5 guard let self = self else{return}6 switch result {7 case .success(let savedItem):8 self.items.append(savedItem)9 DispatchQueue.main.async {10 self.tableView.insertRows(at:[IndexPath(row: self.items.count -1, section:0)], with:.right)11}12 case .failure(let error):13 DispatchQueue.main.async {14 self.showAlert(title:"Error", message:"Failed to save item: \(error.message)")15}16}17}18}
Now we can complete the action for the add button located at the right side of the navigation bar. Go toToDoListControllerand add the following
Swift
1 class ToDoListController: UITableViewController {2 enum ItemDescription: Int { case title =0, description =1}34...56/// This method is called when the user wants to add a new item to the to-do list7 @objc private func handleNewItem(){8showEditController(item: nil)9}1011/// Presents an alert where the user enters a to-do task for either create a new one (item parameter is nil) or edit an existing one12 private func showEditController(item: ToDoListItem?){13 let controllerTitle: String = item == nil ?"New item":"Update item"1415 let editItemAlertController =UIAlertController(title: controllerTitle, message: nil, preferredStyle:.alert)1617 editItemAlertController.addTextField { textField in18 textField.tag = ItemDescription.title.rawValue
19 textField.placeholder ="Title"20 textField.text = item?.title
21}2223 editItemAlertController.addTextField { textField in24 textField.tag = ItemDescription.description.rawValue
25 textField.placeholder ="Description"26 textField.text = item?.description
27}2829 let mainActionTitle: String = item == nil ?"Add":"Update"3031 let mainAction: UIAlertAction =UIAlertAction(title: mainActionTitle, style:.default){[weak self] _ in32 guard let title = editItemAlertController.textFields?.first(where:{ $0.tag == ItemDescription.title.rawValue })?.text else{33return editItemAlertController.dismiss(animated:true, completion: nil)34}3536 let description = editItemAlertController.textFields?.first(where:{ $0.tag == ItemDescription.description.rawValue })?.text
3738 editItemAlertController.dismiss(animated:true){39if let objectId = item?.objectId {// if the item passed as parameter is not nil, the alert will update it40 self?.updateObject(objectId: objectId, newTitle: title, newDescription: description)41}else{42 self?.createObject(title: title, description: description)43}44}45}4647 let cancelAction =UIAlertAction(title:"Cancel", style:.cancel, handler: nil)4849 editItemAlertController.addAction(mainAction)50 editItemAlertController.addAction(cancelAction)5152present(editItemAlertController, animated:true, completion: nil)53}54}
- Read Object
We move to the readObjects() method. Retreiveing ToDoListItem items from your Back4App Database is performed via a Query<ToDoListItem> object. This query is instanciated in the following way
Swift
1 func readObjects(){2 let query = ToDoListItem.query()3...4}
In this tutorial we use a query which will retreive all the items of type ToDoListItem from your Back4App Database. In case you want to retreive a set of specific items, you can provide QueryConstraint elements to ToDoListItem.query(QueryConstraint...). For instance, to fetch all items where title == "Some title", the query takes the form
Swift
1 let query = ToDoListItem.query("title"=="Some title")
Once you have the query ready, we proceed to retreive the items by callingquery.find(). Again, this can be done synchronously or asynchronously. In our To-do List app we implement it asynchronously
Swift
1 func readObjects(){2 let query = ToDoListItem.query()34 query.find {[weak self] result in5 guard let self = self else{return}6 switch result {7 case .success(let items):8 self.items = items
9 DispatchQueue.main.async {10 self.tableView.reloadSections([0], with:.top)11}12 case .failure(let error):13 DispatchQueue.main.async {14 self.showAlert(title:"Error", message:"Failed to save item: \(error.message)")15}16}17}18}
With readObjects() completed, we can now fetch all the tasks stored in your Back4App Database and show them right after the app enters to foreground. Go back to ToDoListController and override the viewDidAppear() method
Given the objectId of a ToDoListItem object, it is straightforward to perform an update. We simply instanciate a ToDoListItem object using the init(objectId:) initializer. Next, we update the properties we need and call the save() method (of ToDoListItem) to save changes
Swift
1 func updateObject(objectId: String, newTitle: String, newDescription: String?){2 var item =ToDoListItem(objectId: objectId)3 item.title = newTitle
4 item.description = newDescription
56 item.save {[weak self] result in7 switch result {8 case .success:9if let row = self?.items.firstIndex(where:{ $0.objectId == item.objectId }){10 self?.items[row]= item
11 DispatchQueue.main.async {12 self?.tableView.reloadRows(at:[IndexPath(row: row, section:0)], with:.fade)13}14}15 case .failure(let error):16 DispatchQueue.main.async {17 self?.showAlert(title:"Error", message:"Failed to save item: \(error.message)")18}19}20}21}
- Delete object
Deleting objects on your Back4App Database is very similar to creating objects. We begin by creating an instance of ToDoListItem with the objectId of the item we want to delete. Next, we simply call (synchronously or ascynchronously) the delete() method of the object. If the deletion was successfull we update the UI, otherwise we report the error
Swift
1 func deleteObject(item: ToDoListItem){2 item.delete {[weak self] result in3 switch result {4 case .success:5if let row = self?.items.firstIndex(where:{ $0.objectId == item.objectId }){6 self?.items.remove(at: row)7 DispatchQueue.main.async {8 self?.tableView.deleteRows(at:[IndexPath(row: row, section:0)], with:.left)9}10}11 case .failure(let error):12 DispatchQueue.main.async {13 self?.showAlert(title:"Error", message:"Failed to save item: \(error.message)")14}15}16}17}
With deleteObject(item:) and updateObject(objectId:newTitle:newDescription) completed, we proceed to add the corresponding actions to call these operations. Go back to ToDoListController and add
Swift
1// MARK: UITableViewDataSource delegate2 extension ToDoListController {3// When the user taps on the accessory button of a cell, we present the edit options for the to-do list task4 override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath){5 guard !items.isEmpty else{return}67showEditOptions(item: items[indexPath.row])8}910/// Presents a sheet where the user can select an action for the to-do list item11 private func showEditOptions(item: ToDoListItem){12 let alertController =UIAlertController(title: title, message: nil, preferredStyle:.actionSheet)1314 let editAction =UIAlertAction(title:"Edit", style:.default){[weak self] _ in15 self?.showEditController(item: item)16}1718 let deleteAction =UIAlertAction(title:"Delete", style:.destructive){[weak self] _ in19 alertController.dismiss(animated:true){20 self?.deleteObject(item: item)21}22}2324 let cancelAction =UIAlertAction(title:"Cancel", style:.cancel){ _ in25 alertController.dismiss(animated:true, completion: nil)26}2728 alertController.addAction(editAction)29 alertController.addAction(deleteAction)30 alertController.addAction(cancelAction)3132present(alertController, animated:true, completion: nil)33}34}
As we pointed out earlier, the accessory button in each ToDoListItemCell triggers an edit sheet via the tableView(_:accessoryButtonTappedForRowWith:) delegate method.
It’s done!
At this point, you have learned how to do the basic CRUD operations with Parse on iOS.