---
title: Relationships
slug: docs/ios/parse-swift-sdk/data-objects/relationships
description: In this guide you will learn how to establish relations between objects.
image: https://www.back4app.com/images/logo-social.png
docTags: 
createdAt: 2024-03-11T12:11:12.637Z
---

# Relationships

## Introduction

Relations are a fundamental feature for organizing data objects stored on a database. **ParseSwift** does provide the necessary tools and methods to establish relations between classes in your Back4App Database. Depending on the use case, we can identify the following type of relations

- **1:1:** A relation that only connects two data objects.
- **1\:N:&#x20;**&#x41; relation between one data object and<font color="#2166ae">`N`</font>data objects
- **N\:N:&#x20;**&#x41; relation beween<font color="#2166ae">`N`</font>data objects to<font color="#2166ae">`N`</font>data objects.

As we will see below, implementing **1:1** relations are relatively straightforward. For **1\:N** and **N\:N** relations, the implementation involves the <font color="#2166ae">`ParseRelation`</font> object provided by **ParseSwift SDK**. There are additional alternatives for implementing **1\:N** and **N\:N** relations. However, due to efficiency and performance, we recommend following our approach

This tutorial uses a basic app created in Xcode 12 and **iOS 14**.

:::hint{type="success"}
At any time, you can access the complete Project via our GitHub repositories.

- [iOS Example Repository](https://github.com/templates-back4app/ios-crud-to-do-list)
:::

## Goal

- To understand how relations are implemented in a Back4App Database.

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- Xcode.
- An app created at Back4App.
  - Follow the [New Parse App tutorial](https://www.back4app.com/docs/get-started/new-parse-app) to learn how to create a Parse app at Back4App.
  - **Note:&#x20;**&#x46;ollow the [Install Parse SDK (Swift) Tutorial](https://www.back4app.com/docs/ios/parse-swift-sdk) to create an Xcode Project connected to Back4App.
:::

## Understanding our Books App

The project template is a Book App where the user enters a book details to save it on a Back4App Database. On the app’s homescreen you will find the form to do so

::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/CATQz6OXhv8MKsTYp2MQs_image.png" size="50" width="1242" height="2208" position="center" showCaption="false"}

Using the <font color="#2166ae">`+`</font> button located on the top-right side of the navigation bar, we can add as many **Publishers**, **Genres** and **Authors** as needed. Once the user enters a book, they can use the <font color="#2166ae">`Add book`</font> button to save the book on their Back4App Database. Additionally, the <font color="#2166ae">`List books`</font> button will allow us to show all the books the user added and also to see their relationship with the publishers and authors.

## Quick reference of commands we are going to use

We make use of the objects <font color="#2166ae">`Author`</font>, <font color="#2166ae">`Publisher`</font>, <font color="#2166ae">`ISBN`</font> and <font color="#2166ae">`Book`</font>:

:::CodeblockTabs
Genre

```swift
1   import Foundation
2   import ParseSwift
3
4   struct Genre: ParseObject {
5       ...
6    
7       var name: String?
8    
9       ...
10  }
```

Author

```swift
1   import Foundation
2   import ParseSwift
3
4   struct Author: ParseObject {
5       ...
6    
7       var name: String?
8    
9       ...
10  }
```

Publisher

```swift
1   import Foundation
2   import ParseSwift
3
4   struct Publisher: ParseObject {
5       ...
6    
7       var name: String?
8    
9       ...
10  }
```

ISBN

```swift
1   import Foundation
2   import ParseSwift
3
4   struct ISBN: ParseObject {
5       ...
6    
7       var name: String?
8    
9       ...
10  }
```

Book

```swift
1   import Foundation
2   import ParseSwift
3
4   struct Book: ParseObject {
5       ...
6
7       var title: String?
8       var publishingYear: Int?
9       var genre: Genre?
10      var isbn: ISBN?
11    
12      ...
13  }
```
:::

Before storing instances of these objects in a Back4App Database, all their properties must conform to the <font color="#2166ae">`Codable`</font> and <font color="#2166ae">`Hashable`</font> protocols.

We make use of the following methods for managing these objects on the Back4App Database:

:::CodeblockTabs
Create

```swift
   //When creating a new instance of Author we use
1   var newAuthor: Author = Author(name: "John Doe")
2
3   // Saves newAuthor on your Back4App Database synchronously and returns the new saved Item. It throws and error if something went wrong.
4   let savedAuthor = try? newAuthor.save()
5
6   // Saves newAuthor on your Back4App Database asynchronously, and passes a Result<Author, ParseError> object to the completion block to handle the save process.
7   newAuthor.save { result in
8       // Handle the result to check wether the save process was successfull or not
9   }
```

Read

```swift
   //For reading any of the objects introduced above, we construct the corresponding queries for each of them. For Author we have
1   let authorQuery = Author.query() // A query to fetch all Author items on your Back4App Database.
2
3   // Fetches the items synchronously or throws an error if found.
4   let fetchedAuthors = try? query.find()
5
6   // Fetches the items asynchronously and calls a completion block passing a result object containing the result of the operation.
7   query.find { result in
8       // Handle the result
9   }
```

Add relations

```swift
//In 1:1 relations, it is sufficient to have the child object as a property of the parent object. Back4App automatically saves the child object when the parent object is saved. For the remaining relations, we use the add(_:objects:) method via the relation property available in the parent ParseObject. Adding a relation called authors on a Book object would look like this
1   let someBook: Book
2   let authors: [Author]
3
4   // Adds the relation between someBook and authors under the name 'authors'
5   let bookToAuthorsRelation = try? someBook.relation?.add("authors", objects: authors)
6
7   // Saves the relation synchronously
8   let updatedSomeBook = try? bookToAuthorsRelation.save()
9
10  // Saves the relation asynchronously
11  bookToAuthorsRelation.save { result in
12      // Handle the result
13  }
```

Query relations

```swift
//For 1:1 relations, it is enough to append the include() method in the query. For instance, to query all the books together with their ISBN relation, we use
1   var query = Book.query().include("isbn")
2
3   // Fetches all books synchronously
4   let books = try? query.find()
5
6   // Fetches all books asynchronously
7   query.find { result in
8       // Handle the result
9   }
```

Remaining relations

```swift
//For the remaining relations, we create a query by using the static method queryRelation(_,parent:) provided by the ParseObject protocol. Querying the authors related to a book can be implemented in the following way
1   let someBook: Book
2   let authors: [Author]
3   ...
4
5   // We create a relation (identified by the name 'authors') betwee someBook and a set of authors
6   let bookToAuthorsRelation = 
7   guard let bookToAuthorsRelation = try someBook.relation?.add("authors", objects: authors) // Book -> Author 
8   else {
9       fatalError("Failed to add relation")
10  }
11
12  let savedRelation = try bookToAuthorsRelation.save() // Saves the relation synchronously
13
14  bookToAuthorsRelation.save { result in // Saves the relation asynchronously
15      // Handle the result
16  }
```
:::

## 1 - Download the Books App Template

The <font color="#2166ae">`XCode`</font> project has the following structure

![](https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/xkHn5bbpziA7whDKS1IDY_image.png)

:::hint{type="success"}
At any time, you can access the complete Project via our GitHub repositories.

- [iOS Example Repository](https://github.com/templates-back4app/ios-crud-to-do-list)
:::

## 2 - Additional CRUD flow


Before going further, it is necessary to implement some CRUD functions for us to be able to save the <font color="#2166ae">`Author`</font>, <font color="#2166ae">`Publisher`</font> and <font color="#2166ae">`Genre`</font> objects. In the<font color="#2166ae">`MainController+ParseSwift.swift`</font> file, under an extension for the <font color="#2166ae">`MainController`</font> class, we implemented the following methods

```swift
1   // MainController+ParseSwift.swift file
2   extension MainController {
3       /// Collects the data to save an instance of Book on your Back4App database.
4       func saveBook() {
5           view.endEditing(true)
6        
7           // 1. First retrieve all the information for the Book (title, isbn, etc)
8           guard let bookTitle = bookTitleTextField.text else {
9               return presentAlert(title: "Error", message: "Invalid book title")
10          }
11        
12          guard let isbnValue = isbnTextField.text else {
13              return presentAlert(title: "Error", message: "Invalid ISBN value.")
14          }
15        
16          let query = ISBN.query("value" == isbnValue)
17        
18          guard (try? query.first()) == nil else {
19              return presentAlert(title: "Error", message: "The entered ISBN already exists.")
20          }
21          
22          guard let genreObjectId = genreOptionsView.selectedOptionIds.first,
23                let genre = genres.first(where: { $0.objectId == genreObjectId})
24          else {
25              return presentAlert(title: "Error", message: "Invalid genre.")
26          }
27        
28          guard let publishingYearString = publishingYearTextField.text, let publishingYear = Int(publishingYearString) else {
29              return presentAlert(title: "Error", message: "Invalid publishing year.")
30          }
31          
32          let authors: [Author] = self.authorOptionsView.selectedOptionIds.compactMap { [weak self] objectId in
33             self?.authors.first(where: { objectId == $0.objectId })
34          }
35        
36          let publishers: [Publisher] = self.publisherOptionsView.selectedOptionIds.compactMap { [weak self] objectId in
37             self?.publishers.first(where: { objectId == $0.objectId })
38          }
39        
40          // Since we are making multiple requests to Back4App, it is better to use synchronous methods and dispatch them on the background queue
41          DispatchQueue.global(qos: .background).async {
42              do {
43                  let isbn = ISBN(value: isbnValue) // 2. Instantiate a new ISBN object
44                
45                  let savedBook = try Book( // 3. Instantiate a new Book object with the corresponding input fields
46                      title: bookTitle,
47                     publishingYear: publishingYear,
48                      genre: genre,
49                      isbn: isbn
50                  ).save() // 4. Save the new Book object
51                
52                  ... // Here we will implement the relations
53                  
54                  DispatchQueue.main.async {
55                      self.presentAlert(title: "Success", message: "Book saved successfully.")
56                  }
57              } catch {
58                  DispatchQueue.main.async {
59                     self.presentAlert(title: "Error", message: "Failed to save book: \((error as! ParseError).message)")
60                  }
61              }
62          }
63      }
64    
65      /// Retrieves all the data saved under the Genre class in your Back4App Database
66      func fetchGenres() {
67          let query = Genre.query()
68          
69          query.find { [weak self] result in
70              switch result {
71              case .success(let genres):
72                  self?.genres = genres // When setting self?.genres, it triggers the corresponding UI update
73              case .failure(let error):
74                  self?.presentAlert(title: "Error", message: error.message)
75              }
76          }
77      }
78    
79      /// Presents a simple alert where the user can enter the name of a genre to save it on your Back4App Database
80      func handleAddGenre() {
81          // Displays a form with a single input and executes the completion block when the user presses the submit button
82          presentForm(
83              title: "Add genre",
84              description: "Enter a description for the genre",
85              placeholder: nil
86          ) { [weak self] name in
87              guard let name = name else { return }
88              let genre = Genre(name: name)
89              
90              let query = Genre.query("name" == name)
91            
92              guard ((try? query.first()) == nil) else {
93                  self?.presentAlert(title: "Error", message: "This genre already exists.")
94                  return
95              }
96              
97              genre.save { [weak self] result in
98                  switch result {
99                  case .success(let addedGenre):
100                     self?.presentAlert(title: "Success", message: "Genre added!")
101                     self?.genres.append(addedGenre)
102                 case .failure(let error):
103                     self?.presentAlert(title: "Error", message: "Failed to save genre: \(error.message)")
104                 }
105             }
106         }
107     }
108     
109     /// Retrieves all the data saved under the Publisher class in your Back4App Database
110     func fetchPublishers() {
111         let query = Publisher.query()
112         
113         query.find { [weak self] result in
114             switch result {
115             case .success(let publishers):
116                 self?.publishers = publishers
117             case .failure(let error):
118                 self?.presentAlert(title: "Error", message: error.message)
119             }
120         }
121     }
122    
123     /// Presents a simple alert where the user can enter the name of a publisher to save it on your Back4App Database
124     func handleAddPublisher() {
125         // Displays a form with a single input and executes the completion block when the user presses the submit button
126         presentForm(
127             title: "Add publisher",
128             description: "Enter the name of the publisher",
129             placeholder: nil
130         ) { [weak self] name in
131             guard let name = name else { return }
132             
133             let query = Publisher.query("name" == name)
134             
135             guard ((try? query.first()) == nil) else {
136                 self?.presentAlert(title: "Error", message: "This publisher already exists.")
137                 return
138             }
139             
140             let publisher = Publisher(name: name)
141             
142             publisher.save { [weak self] result in
143                 switch result {
144                 case .success(let addedPublisher):
145                     self?.presentAlert(title: "Success", message: "Publisher added!")
146                     self?.publishers.append(addedPublisher)
147                 case .failure(let error):
148                     self?.presentAlert(title: "Error", message: "Failed to save publisher: \(error.message)")
149                 }
150             }
151         }
152     }
153    
154     /// Retrieves all the data saved under the Genre class in your Back4App Database
155     func fetchAuthors() {
156         let query = Author.query()
157        
158         query.find { [weak self] result in
159             switch result {
160             case .success(let authors):
161                 self?.authors = authors
162             case .failure(let error):
163                 self?.presentAlert(title: "Error", message: error.message)
164             }
165         }
166     }
167    
168     /// Presents a simple alert where the user can enter the name of an author to save it on your Back4App Database
169     func handleAddAuthor() {
170         // Displays a form with a single input and executes the completion block when the user presses the submit button
171         presentForm(
172             title: "Add author",
173             description: "Enter the name of the author",
174             placeholder: nil
175         ) { [weak self] name in
176             guard let name = name else { return }
177            
178             let query = Author.query("name" == name)
179            
180             guard ((try? query.first()) == nil) else {
181                 self?.presentAlert(title: "Error", message: "This author already exists.")
182                 return
183             }
184             
185             let author = Author(name: name)
186             
187             author.save { [weak self] result in
188                 switch result {
189                 case .success(let addedAuthor):
190                     self?.presentAlert(title: "Success", message: "Author added!")
191                     self?.authors.append(addedAuthor)
192                 case .failure(let error):
193                     self?.presentAlert(title: "Error", message: "Failed to save author: \(error.message)")
194                 }
195             }
196         }
197     }
198 }sw
```

:::hint{type="success"}
For more details about this step, you can go to the [basic operations guide](https://www.back4app.com/docs/ios/parse-swift-sdk/data-objects/swift-crud-database-operations).
:::

## 3 - Setup the relations

Before starting to create relations, take a look at the [quick reference](https://www.back4app.com/docs/ios/parse-swift-sdk/data-objects/relationships#quick-reference) section to have an idea about the objects we want to relate to each other. In the figure below we show how these objects are related

![](https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/HMgGaGgW7i8acqRI5eFuN_image.png)

As can be seen, the relations are created by putting the <font color="#2166ae">`Book`</font> object in the middle. The arrows show how each object is related to a <font color="#2166ae">`Book`</font> object.

## 4 - Implementing relations

**1:1 case**

Adding **1:1** relations can easlity be achieved by adding a property in the <font color="#2166ae">`Book`</font> object, i.e.,

```swift
1   struct Book: ParseObject {
2       ...
3
4       var isbn: ISBN? // Esablishes a 1:1 relation between Book and ISBN
5    
6       ...
7   }
```

In this case, <font color="#2166ae">`Book`</font> and <font color="#2166ae">`ISBN`</font> share a **1:1** relation where <font color="#2166ae">`Book`</font> is identified as the parent and <font color="#2166ae">`ISBN`</font> as the child. Internally, when Back4App saves an instance of <font color="#2166ae">`Book`</font>, it saves the <font color="#2166ae">`ISBN`</font> object (under the <font color="#2166ae">`ISBN`</font> class name) first. After this process is complete, Back4App continues with the object <font color="#2166ae">`Book`</font>. The new <font color="#2166ae">`Book`</font> object is saved in a way that its <font color="#2166ae">`isbn`</font> property is represented by a <font color="#2166ae">`Pointer<ISBN>`</font> object. A <font color="#2166ae">`Pointer<>`</font> object allows us to store a unique instance of the <font color="#2166ae">`ISBN`</font> object related to its corresponding parent.

**1\:N case**

For **1\:N** relations, the most efficient way to implement them is via a <font color="#2166ae">`ParseRelation<Book>`</font> object. **ParseSwift** provides a set of methods to add these types of relationships for any object conforming to the <font color="#2166ae">`ParseObject`</font> protocol. For instance, if we want to create a <font color="#2166ae">`1:N`</font> relation between <font color="#2166ae">`Book`</font> and <font color="#2166ae">`Author`</font> we can use

```swift
1   let someBook: Book
2   let authors: [Author]
3   ...
4
5   // We create a relation (identified by the name 'authors') between someBook and a set of authors
6   let bookToAuthorsRelation = 
7   guard let bookToAuthorsRelation = try someBook.relation?.add("authors", objects: authors) // Book -> Author 
8   else {
9       fatalError("Failed to add relation")
10  }
11
12  let savedRelation = try bookToAuthorsRelation.save() // Saves the relation synchronously
13
14  bookToAuthorsRelation.save { result in // Saves the relation asynchronously
15      // Handle the result
16  }
```

It is straightforward to adapt this snippet for the other relations we have for <font color="#2166ae">`Book`</font>.

**- Putting all together**

Once we established the basic idea to implement relations, we now complete the <font color="#2166ae">`saveBook()`</font> method. We enumerate the key points to have in mind during this process

```swift
1   extension MainController {
2       /// Collects the data to save an instance of Book on your Back4App database.
3       func saveBook() {
4           ...        
5           // 1. First retrieve all the information for the Book (bookTitle, isbnValue, etc)
6           ...
7
8           // Since we are making multiple requests to Back4App, it is better to use synchronous methods and dispatch them on the background queue
9           DispatchQueue.global(qos: .background).async {
10              do {
11                  let isbn = ISBN(value: isbnValue) // 2. Instantiate a new ISBN object
12                
13                  let savedBook = try Book( // 3. Instantiate a new Book object with the corresponding input fields
14                      title: bookTitle,
15                      publishingYear: publishingYear,
16                      genre: genre,
17                      isbn: isbn
18                  ).save() // 4. Save the new Book object
19                
20                  // 5. Add the corresponding relations for new Book object
21                  guard let bookToAuthorsRelation = try savedBook.relation?.add("authors", objects: authors), // Book -> Author
22                        let bootkToPublishersRelation = try savedBook.relation?.add("publishers", objects: publishers), // Book -> Publisher
23                        let genreRelation = try genre.relation?.add("books", objects: [savedBook]) // Genre -> Book
24                  else {
25                      return DispatchQueue.main.async {
26                          self.presentAlert(title: "Error", message: "Failed to add relations")
27                      }
28                  }
29                                 
30                  // 6. Save the relations
31                  _ = try bookToAuthorsRelation.save()
32                  _ = try bootkToPublishersRelation.save()
33                  _ = try genreRelation.save()
34                 
35                  DispatchQueue.main.async {
36                      self.presentAlert(title: "Success", message: "Book saved successfully.")
37                  }
38              } catch {
39                  DispatchQueue.main.async {
40                      self.presentAlert(title: "Error", message: "Failed to save book: \((error as! ParseError).message)")
41                  }
42              }
43          }
44      }
45
46      ...
47  }
```

## 5 - Querying relations

For **1:1** relations, given the parent <font color="#2166ae">`Book`</font> and its child <font color="#2166ae">`ISBN`</font>, we query the corresponding child by including it in the <font color="#2166ae">`Book`</font> query

```swift
1   let query = Book.query().include("isbn")
2
3   let books = try query.find() // Retrieves synchronously all the books together with its isbn
4
5   query.find { result in // Retrieves asynchronously all the books together with its isbn
6       // Handle the result
7   }
8
```

With this, all the books from the **query** will also have the **isbn** property set properly with the related **ISBN** object.

On the other hand, in order to retrieve objects with a **1\:N** relation, the **ParseObject** protocol provides the static method **queryRelation(\_,parent:)**. By providing the name of the relation (as the first parameter) and the parent, this method allows us to create the required query. For instance, to retrieve all the **Author**’s related to a specific **Book** we can use the following snippet

```swift
1   let book: Book // Book from which we are trying to retrieve its related authors
2
3   do {
4       let authorsQuery = try Author.queryRelations("authors", parent: book) // 'authors' is the name of the relation it was saved with
5        
6       authorsQuery.find { [weak self] result in
7           switch result {
8           case .success(let authors):
9               self?.authors = authors
10                    
11              DispatchQueue.main.async {
12                  // Update the UI
13              }
14              case .failure(let error):
15                  DispatchQueue.main.async {
16                      self?.presentAlert(title: "Error", message: "Failed to retrieve authors: \(error.message)")
17                  }
18              }
19          }
20      }
21  } catch {
22      if let error = error as? ParseError {
23          presentAlert(title: "Error", message: "Failed to retrieve authors: \(error.message)")
24      } else {
25          presentAlert(title: "Error", message: "Failed to retrieve authors: \(error.localizedDescription)")
26      }
27  }
```

Similarly, we can query other related objects such as **Publisher**.

In the **BookDetailsController.swift** file we implement these queries to display the relation a book has with authors and publishers

```swift
1  // BookDetailsController.swift file
2  class BookDetailsController: UITableViewController {
3      ...
4    
5      /// Retrieves the book's details, i.e., its relation with authors and publishers
6      private func fetchDetails() {
7          do {
8              // Constructs the relations you want to query
9              let publishersQuery = try Publisher.queryRelations("publishers", parent: book)
10              let authorsQuery = try Author.queryRelations("authors", parent: book)
11            
12              // Obtains the publishers related to book and display them on the tableView, it presents an error if happened.
13              publishersQuery.find { [weak self] result in
14                  switch result {
15                  case .success(let publishers):
16                      self?.publishers = publishers
17                    
18                      // Update the UI
19                      DispatchQueue.main.async {
20                          self?.tableView.reloadSections(IndexSet([Section.publisher.rawValue]), with: .none)
21                      }
22                  case .failure(let error):
23                      DispatchQueue.main.async {
24                          self?.presentAlert(title: "Error", message: "Failed to retrieve publishers: \(error.message)")
25                      }
26                  }
27              }
28            
29              // Obtains the authors related to book and display them on the tableView, it presents an error if happened.
30              authorsQuery.find { [weak self] result in
31                  switch result {
32                  case .success(let authors):
33                      self?.authors = authors
34                    
35                      // Update the UI
36                      DispatchQueue.main.async {
37                          self?.tableView.reloadSections(IndexSet([Section.author.rawValue]), with: .none)
38                      }
39                  case .failure(let error):
40                      DispatchQueue.main.async {
41                          self?.presentAlert(title: "Error", message: "Failed to retrieve authors: \(error.message)")
42                      }
43                  }
44              }
45          } catch { // If there was an error during the creation of the queries, this block should catch it
46              if let error = error as? ParseError {
47                  presentAlert(title: "Error", message: "Failed to retrieve authors: \(error.message)")
48              } else {
49                  presentAlert(title: "Error", message: "Failed to retrieve authors: \(error.localizedDescription)")
50              }
51          }
52      }
53    
54      ...
55  }
```

## 6 - Run the app!

:::hint{type="info"}
Before pressing the run button on <font color="#2166ae">`XCode`</font>, do not forget to configure your <font color="#2166ae">`Back4App` </font>application in the <font color="#2166ae">`AppDelegate`</font> class!
:::

You have to add a couple of **Genre**’s, **Publisher**’s and **Author**’s before adding a new book. Then, you can start entering a book’s information to save it on your Back4App Database. Once you have saved one Book, open your [Back4App dashboard](https://parse-dashboard.back4app.com/apps) and go to your application linked to the **XCode** project. In the Database section, you will find the class <font color="#2166ae">`Book`</font> where all the books created by the <font color="#2166ae">`iOS`</font> App are stored.

Additionally, you can see that Back4App automatically created the class <font color="#2166ae">`ISBN`</font> in order to relate it with its corresponding <font color="#2166ae">`Book`</font> object. If you go back to the <font color="#2166ae">`Book`</font> class, you can identify the data types for each type of relation. In the case of <font color="#2166ae">`ISBN`</font> and <font color="#2166ae">`Genre`</font>, the data type is a <font color="#2166ae">`Pointer<>`</font>. On the other hand, for relations like <font color="#2166ae">`Author`</font> and <font color="#2166ae">`Publisher`</font>, the data type is <font color="#2166ae">`Relation<>`</font>. This is a key difference to have in mind when constructing relations.
