Fish Classification iOS App with SashiDo and Teachable Machine
Tags: swift, machinelearning, ios, tutorial
Published: 2020-12-03 18:59:55

I don’t consider myself *A Fisher*, but I do enjoy fishing occasionally. I thought it would be fun to create a classification and storage app. Using [Xcode](https://apps.apple.com/us/app/xcode/id497799835?mt=12) to create this app, [Teachable Machine](https://teachablemachine.withgoogle.com/) to identify the type of fish, and [SashiDo](https://www.sashido.io/en/) to manage all the data and backend code, I finally have a finished product that I would like to share with you all. All these tools (Xcode, Teachable Machine, and SashiDo) enable you to create amazing apps, and I wanted to provide you the basic knowledge to get started. Before advancing, I recommend you have knowledge of [swift](https://swift.org/) as I will mainly be going over how to use SashiDo and how to integrate a Teachable Machine model. Using my app as an example, I want to show you the steps to creating your own and some important things I learned from creating mine.

First, for a quick overview of my app, once logged in, you can either take a picture, upload a picture, browse your catches, or browse others' catches. Below is a quick video displaying the components of my app:

{% youtube yEn0XrUNqk4 %}

## Table of Contents
* [Teachable Machine](#using-teachable-machine)
* [Classification](#classifying-the-image)
* [Outline](#outline)
* [SashiDo and Parse](#using-sashido-and-parse)
* [Registration](#registration)
   * [Signup](#signup)
   * [Login](#login)
   * [Logout](#logout)
* [SessionManager](#session-manager)
   * [Setting up sessionManager](#setting-up-sessionmanager)
   * [requestGetPages](#requestgetpages)
   * [requestCreateNewPage](#requestcreatenewpage)
* [Closing Remarks](#closing-remarks)

## Using Teachable Machine

Although it was not my actual first step, creating and importing the model really should be the first step. Unfortunately, Teachable Machine does not offer an exportation method using swift, so I had to find some other way to access the model. There are many different solutions, but I chose to export the model using Tensorflow Lite (which is made for Android development).
 
Here are steps to creating and downloading the model:
&nbsp;&nbsp;&nbsp;&nbsp; 1\. Create and train your model with [Teachable Machine](https://teachablemachine.withgoogle.com/).
&nbsp;&nbsp;&nbsp;&nbsp; 2\. Click **export model**.
&nbsp;&nbsp;&nbsp;&nbsp; 3\. Click on **Tensorflow Lite** at the top (right-most option).
&nbsp;&nbsp;&nbsp;&nbsp; 4\. Download model (should be Floating point for Model conversion type).

This will download two files onto your computer (model_unquant.tflite and labels.txt). Save them for later as we will be inserting them to our Xcode project.

To actually get started with Xcode, generally you would create your own project and begin working from scratch, but in this case, because you must use the .tflite model in Xcode, it is best to download and use a template. 

These are the following steps to set up the template:
&nbsp;&nbsp;&nbsp;&nbsp; 1\. Download [CatVsDogSample](https://github.com/lmoroney/dlaicourse).
&nbsp;&nbsp;&nbsp;&nbsp; 2\. In your downloaded file go to **TensorFlow Deployment/Course 2 - TensorFlow Lite/Week 3/ Examples/iOS Apps/**.
&nbsp;&nbsp;&nbsp;&nbsp; 3\. You should see 3 files (cats_vs_dogs, image_classification, and object_detection); move the **cats_vs_dogs** folder into a separate place outside of all the other folders.
&nbsp;&nbsp;&nbsp;&nbsp; 4\. Delete everything else (you only want the cats_vs_dogs folder).
&nbsp;&nbsp;&nbsp;&nbsp; 5\. In terminal type **sudo gem install cocoapods**.
&nbsp;&nbsp;&nbsp;&nbsp; 6\. Inside the cats_vs_dogs folder, right click on **CatVsDogClassifierSample.xcworkspace** and open with Xcode (you must open .xcworkspace, NOT .xcodeproj).
&nbsp;&nbsp;&nbsp;&nbsp; 7\. Open the bottom-most folder Pods and open the **Podfile**.
&nbsp;&nbsp;&nbsp;&nbsp; 8\. Add **pod ‘Parse’** so that the contents are as such:
![podfile](https://dev-to-uploads.s3.amazonaws.com/i/g8ddqxqqv039gc09abyt.png)
&nbsp;&nbsp;&nbsp;&nbsp; 9\. Save and exit Xcode.
&nbsp;&nbsp;&nbsp;&nbsp; 10\. In terminal navigate to the downloaded CatVsDogSample:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - Use **cd folderName**.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - You should see 6 other folders/files like this:
![terminal](https://dev-to-uploads.s3.amazonaws.com/i/qaonqr1d77j3auicloz9.png)

&nbsp;&nbsp;&nbsp;&nbsp; 11\. Type **pod init**.
&nbsp;&nbsp;&nbsp;&nbsp; 12\. Type **pod install**.

Now that you have downloaded everything, here are the steps to importing your model:
&nbsp;&nbsp;&nbsp;&nbsp; 1\. Open up the **CatVsDogClassifierSample.xcworkspace** again.
&nbsp;&nbsp;&nbsp;&nbsp; 2\. Under **StoryBoards/Main.storyboard**, delete the pre-existing ViewController.
&nbsp;&nbsp;&nbsp;&nbsp; 3\. Under ViewController, delete the **ViewController.swift** file.
&nbsp;&nbsp;&nbsp;&nbsp; 4\. Right click on the outermost CatVsDogClassifierSample.xcworkspace and click **Add Files to “CatVsDogClassifierSample”...**
&nbsp;&nbsp;&nbsp;&nbsp; 5\. At the top select your **model_unquant.tflite** and click **Add**:
![import model](https://dev-to-uploads.s3.amazonaws.com/i/h5apfzvr6hqxtxvmbt5b.png)
&nbsp;&nbsp;&nbsp;&nbsp; 6\. Open **ModelDataHandler.swift**.
&nbsp;&nbsp;&nbsp;&nbsp; 7\. Towards the top, look for this function:
![model reference](https://dev-to-uploads.s3.amazonaws.com/i/no5n9arpq55i22ji5pdg.png)
&nbsp;&nbsp;&nbsp;&nbsp; 8\. Replace **“converted_model”** with **“model_unquant”**.
&nbsp;&nbsp;&nbsp;&nbsp; 9\. Scroll down a bit until you see this line of code:
![model labels](https://dev-to-uploads.s3.amazonaws.com/i/48hyi7emtrmvpo229p67.png)
&nbsp;&nbsp;&nbsp;&nbsp; 10\. Replace **“Cat”, “Dog”** with your own labels.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - To do this you must open the labels.txt file and IN THE SAME ORDER insert them into the array.

With the model imported, I'll show you how to use it for classification.

## Classifying the image
There are several different ways you can get the image you want to classify (picture, upload, UIImage View, etc.), so in this example, we are just assuming that you have stored the image in a variable called image. This is the code to run the model:
```swift
let pixelBuffer = image.pixelBuffer()!
let inferenceResults = modelDataHandler?.runModel(onFrame:pixelBuffer)
guard let inference = inferenceResults?.first else {
   return
}
```
Now the classification label should be stored in inference.label, and you can choose what you want to do with this (display it on the controller, save it to the database, pass it to the next view controller, etc.).

## Outline
One of the most important things to do when creating an iOS app is designing an outline for all the controllers you will need. This will look different for every app, but for mine, the outline looked like this:
![outline sketch](https://dev-to-uploads.s3.amazonaws.com/i/6dnj1buvk5syfalrvw62.png)
With an outline in mind, it is now time to actually create these controllers.
For my specific app, I had 3 different types of controllers (viewController, TableViewController, and CollectionViewController), all highlighted in the specific colors (black, orange, blue) respectively. 

Here are the steps to creating the controllers and their files:
&nbsp;&nbsp;&nbsp;&nbsp; 1\. Add necessary controllers (View Controller, Table View Controller, and Collection View Controller, etc.).
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - Make sure you choose the controller option.
&nbsp;&nbsp;&nbsp;&nbsp; 2\. Add navigation controller:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - Set navigation controller as the initial view controller.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - Set signup/login page (or whatever you want the first page of your app to be) as the navigation controller’s root view controller.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - Add this code inside the settings class (or page/file after the signup and login pages).
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - Make sure **'settings'** is changed to whatever you called your controller.
```swift
//keep with navigation controller
var objVC: UIViewController? = storyboard!.instantiateViewController(withIdentifier: "settings")
var aObjNavi = UINavigationController(rootViewController: objVC!)
```
&nbsp;&nbsp;&nbsp;&nbsp; 3\. Add other elements (buttons, image views, labels, etc.).
&nbsp;&nbsp;&nbsp;&nbsp; 4\. Create connections/paths through storyboard.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - Hold control, click on button, drag to controller that you want to create path between.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - Not all paths will be made through storyboard as some will be made through code.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - The following were paths I did not connect through the storyboard: login and signup settings, upload and pic input info page, and table view collectionview description page.
&nbsp;&nbsp;&nbsp;&nbsp; 5\. Add constraints to elements.

Here what my storyboard ended up looking like:
![storyboard outline](https://dev-to-uploads.s3.amazonaws.com/i/k7tfj1qfdfj97bue5w0u.png)
&nbsp;&nbsp;&nbsp;&nbsp; 6\. Create files for all of the controllers:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a. Right click in the ViewController folder and press **New File...**
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b. Choose swift file and click **Next**.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c. Name the file and click **Create**.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; d. Write the necessary code:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i. At the top of each file, **import UIKit** and **import Parse** (although you won’t need both of these for all controllers, it doesn’t hurt to have it).
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ii. Create a class with a name matching the name of the file and a type matching the type of the controller.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iii. Inside the class override the **viewDidLoad** function and inside write **super.viewDidLoad()**.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iv. Eg. The code for **settings.swift** meant to connect with a viewController should look like this:
```swift
import Foundation
import UIKit
import Parse

class settings: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}
```
&nbsp;&nbsp;&nbsp;&nbsp; 7\. Connect each file to its corresponding controller:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a. Click on the top of a view controller.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b. Click on the identity inspector on the top right.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c. Under Custom Class and next to Class, click on the blue arrow.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; d. Choose the corresponding file name.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e. Copy the name of the file (in this example settings) and paste it into **StoryboardID** as we may need to reference it later.

![file to controller connection](https://dev-to-uploads.s3.amazonaws.com/i/d5woc45et65lrw0va3uv.png)


## Using SashiDo and Parse
Next we have to link our app to SashiDo:
&nbsp;&nbsp;&nbsp;&nbsp; 1\. Go to your SashiDo dashboard.
&nbsp;&nbsp;&nbsp;&nbsp; 2\. Click **Create New App** and follow the instructions.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - Once your app has been created you should be shown a page saying **Connect Applications With SashiDo**.
&nbsp;&nbsp;&nbsp;&nbsp; 3\. Making sure swift is selected, copy the 6 lines at the bottom:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - If you are not shown this page or accidentally move past it, you can find it again by clicking **Getting Started** at the top of the menu on the left.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - If you had any trouble with this section, refer to the getting [started guide](https://blog.sashido.io/sashidos-getting-started-guide/).

![SashiDo](https://dev-to-uploads.s3.amazonaws.com/i/z8118re061vb2m5xe1u2.png)
&nbsp;&nbsp;&nbsp;&nbsp; 4\. Next go back to Xcode and open **AppDelegate.swift**.
&nbsp;&nbsp;&nbsp;&nbsp; 5\. Inside func application, paste the code you copied.
![connect SashiDo to application](https://dev-to-uploads.s3.amazonaws.com/i/4zld5lm1215rkge0auw7.png)
In order to communicate with SashiDo, we are going to be using Parse documentation. I will be going over some of the functions and calls I used, but it is also beneficial to refer to the [Parse iOS documentation](https://docs.parseplatform.org/ios/guide/). Additionally, in order to gain a more in depth understanding, I found this [video playlist](https://www.youtube.com/playlist?list=PLMRqhzcHGw1ZFjFyHGJTTPuvcLbwVCuG4) on using parse for iOS development very useful.

## Registration
One thing that is consistent and important in most apps are the login and signup pages. In this section, I will lead you through creating your own registration pages with Sashido and Parse.

### Signup
&nbsp;&nbsp;&nbsp;&nbsp; 1\. Connect text inputs (as outlet) and button (as action) to file.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - If you do not know how to do this, refer to [this](https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/ConnectTheUIToCode.html) thorough guide.
&nbsp;&nbsp;&nbsp;&nbsp; 2\. Inside the button action function (I called it signupToggled) create a user.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - [Parse](https://docs.parseplatform.org/ios/guide/#signing-up) enables you to do this through a few simple lines of code.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - See screenshot below for code.
&nbsp;&nbsp;&nbsp;&nbsp; 3\. Save the user and use the navigation controller to go to the next page.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - See code below, but if you don’t understand how to push the next page, visit this [link](https://stackoverflow.com/questions/25326647/present-view-controller-in-storyboard-with-a-navigation-controller-swift) #18.
&nbsp;&nbsp;&nbsp;&nbsp; 4\. Override the function **touchesBegan** to dismiss the keyboard when you tap somewhere else.

```swift
import Foundation
import UIKit
import Parse

class signup: UIViewController {
    @IBOutlet weak var emailTextField: UITextField!
    @IBOutlet weak var usernameTextField: UITextField!
    @IBOutlet weak var passwordTextField: UITextField!
    @IBOutlet weak var buttonStyle: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    //get rid of keyboard when touch screen
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        usernameTextField.resignFirstResponder()
        passwordTextField.resignFirstResponder()
        emailTextField.resignFirstResponder()
    }

    @IBAction func signupToggled(_ sender: Any) {
        self.showSpinner()
        //check username and password field are not empty / nil
        let user = PFUser()
        user.username = usernameTextField.text!
        user.password = passwordTextField.text!
        user.email = emailTextField.text!
        user.signUpInBackground { (result, error) in
            if error == nil && result == true {
                //successfully signed up in
                let cUser = currentUser(username: user["username"] as! String, id: user.objectId!)
                sessionManager.shared.user = cUser
                //next screen
                let secondViewController = self.storyboard?.instantiateViewController(withIdentifier: "settings") as! settings
                self.navigationController?.pushViewController(secondViewController, animated: true)
                
            } else {
                //error display error message
            }
        }
    }
    
}
```
Note: The code about **cUser** is just to store a local variable that keeps track of the current user. I stored it in [sessionManager](#session-manager), which is a file that I can access globally containing all of my database query functions.

### Login
&nbsp;&nbsp;&nbsp;&nbsp; 1\. This is the same as the signup page, but change the code from signing up to [logging](https://docs.parseplatform.org/ios/guide/#logging-in) in as shown below:

```swift
import Foundation
import UIKit
import Parse

class login: UIViewController {
    @IBOutlet weak var usernameTextField: UITextField!
    @IBOutlet weak var passwordTextField: UITextField!
    @IBOutlet weak var buttonStyle: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    //get rid of keyboard when touch screen
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        usernameTextField.resignFirstResponder()
        passwordTextField.resignFirstResponder()
    }
    
    @IBAction func loginToggled(_ sender: Any) {
        PFUser.logInWithUsername(inBackground:usernameTextField.text!, password:passwordTextField.text!) {
          (user, error) -> Void in
          if user != nil {
            var cUser = currentUser(username: user?["username"] as! String, id: user?.objectId as! String)
            sessionManager.shared.user = cUser
            // Do stuff after successful login.
            let secondViewController = self.storyboard?.instantiateViewController(withIdentifier: "settings") as! settings
            self.navigationController?.pushViewController(secondViewController, animated: true)
          } else {
            // The login failed
          }
        }
    }
}
```

### Logout
To logout in Parse, all you do is add this line of code:
``` swift
PFUser.logOut()
```

## Session Manager
When creating the app, I had two major issues surrounding database querying or saving that I solved using a file called **sessionManager.swift**. The first was organization and the second was timing. Basically, sessionManager is a file (and class) that I created where I can write and store all my database query and saving functions while still being able to access them in other files. By doing this, I was able to easily keep track of my functions, solving my first problem. My second problem was a little more complicated. When I tried retrieving data from SashiDo without sessionManager, there was a timing issue and the page would sometimes load before the data got back. Because of this, I decided to store information locally when the user signed in. This enabled me to access information on the catches of the user very quickly and easily. sessionManager was important in that process because all the functions were in one place and accessible throughout the app - I could also create arrays that stored the information there, too. If the user uploaded a new catch (or page as I called them), I would save the information to the database but also append it to the necessary arrays. This is generally a good practice, so I want to show you how to set up sessionManager, some examples of using it, and some examples of communicating with SashiDo.

### Setting up sessionManager
&nbsp;&nbsp;&nbsp;&nbsp; 1\. Create a new file and call it **sessionManager**.
&nbsp;&nbsp;&nbsp;&nbsp; 2\. **import Parse** at the top.
&nbsp;&nbsp;&nbsp;&nbsp; 3\. Create a class of type **NSObject**.
&nbsp;&nbsp;&nbsp;&nbsp; 4\. Write **static let shared = sessionManager()**.
&nbsp;&nbsp;&nbsp;&nbsp; 5\. Then override the **init** function.

``` swift
import Foundation
import Parse
class sessionManager: NSObject {
    static let shared = sessionManager()
    override init() {
       super.init()
   }
}
```

Now you can write whatever code you want inside this class. The two examples I am going to show you are retrieving information from the database and saving information in the database.

### requestGetPages
Before getting started, it might be beneficial to take a look at Parse’s [basic querying](https://docs.parseplatform.org/ios/guide/#basic-queries) and [image querying](https://docs.parseplatform.org/ios/guide/#images). 

This is a function I wrote that gets all the pages (or catches) of a user with the matching ID:
&nbsp;&nbsp;&nbsp;&nbsp; 1\. Define function (I called it requestGetPages).
&nbsp;&nbsp;&nbsp;&nbsp; 2\. Clear the array that local information is stored in.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - You clear it at the beginning because this should only be called once for each user, so if it is called again it means they have logged out then logged back in.
&nbsp;&nbsp;&nbsp;&nbsp; 3\. Create query and define its [constraints](https://docs.parseplatform.org/ios/guide/#query-constraints).
&nbsp;&nbsp;&nbsp;&nbsp; 4\. Find objects in the database, check for errors, and loop through returned objects.
&nbsp;&nbsp;&nbsp;&nbsp; 5\. Save or use the information somehow.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - I created an object of class page and appended it to the array.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - The class page is one that I created similar to sessionManager and is of type NSObject as well.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - To learn more about creating classes in swift look at [this site](https://www.tutorialspoint.com/swift/swift_classes.htm).

``` swift
func requestGetPages (completion:@escaping (_ success:Bool) -> ()) {
    AllPages.removeAll()
    let query = PFQuery(className: "page")
    query.whereKey("userID", equalTo:user.id)
    query.findObjectsInBackground { (objects, error) in
        // no errors
        if error == nil {
            // if there are objects in the array
            if let returnedObjects = objects {
                // loop through all objects in array
                for object in returnedObjects {
                    let file = object["Image"] as! PFFileObject
                    file.getDataInBackground { (data, error) in
                        if error == nil {
                            if let imageData = data {
                               let image = UIImage(data: imageData) //can then display on screen
                               let fishData = page(fishType: object["fishType"]! as! String, Image: image!, desc: object["description"]! as! String, location: object["location"]! as! String, catchDate: object["catchDate"]! as! String, weight: object["weight"]! as! String, dimensions: object["dimensions"]! as! String)
                                    self.AllPages.append(fishData)
                            }
                        }
                    }
                }
            }
            completion(true)
        }
        else {
            //return false completion if fails
            completion(false)
        }
    }
}
```
Note: The functions I used for this are completion functions, meaning they will not move on until the data query has been completed.

&nbsp;&nbsp;&nbsp;&nbsp; 6\. In my **settings.swift** file (or whenever you want to call this function), INSIDE **viewDidLoad** you would put the following code:

``` swift
sessionManager.shared.requestGetPages { (success) in
   if success {
      // query has completed - do what you want
   }
}
```

### requestCreateNewPage
Before getting started it might be beneficial to take a look at saving [data](https://docs.parseplatform.org/ios/guide/#saving-objects) and [images](https://docs.parseplatform.org/ios/guide/#images) with Parse. 

This is a function I wrote that saves a new page:
&nbsp;&nbsp;&nbsp;&nbsp; 1\. Define function (requestCreateNewPage).
&nbsp;&nbsp;&nbsp;&nbsp; 2\. Create a new object and add all necessary ‘columns’.
&nbsp;&nbsp;&nbsp;&nbsp; 3\. Resize image
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - This step is not necessary, but I found I was trying to save too much information to the database and this was a solution to that.
&nbsp;&nbsp;&nbsp;&nbsp; 4\. Save the information in the database and do whatever else you would like.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - I appended the new page to the AllPages array.

``` swift
func requestCreateNewPage (page:page, completion:@escaping (_ success:Bool) -> ()) {
    let pg = PFObject(className:"page")
    pg["fishType"] = page.fishType
    pg["description"] = page.desc
    pg["location"] = page.location
    pg["catchDate"] = page.catchDate
    pg["userID"] = user.id
    pg["weight"] = page.weight
    pg["dimensions"] = page.dimensions
        
    // reducing image size
    let image = page.Image
    let actualHeight:CGFloat = image.size.height
    let actualWidth:CGFloat = image.size.width
    let imgRatio:CGFloat = actualWidth/actualHeight
    let maxWidth:CGFloat = 1024.0
    let resizedHeight:CGFloat = maxWidth/imgRatio
    let compressionQuality:CGFloat = 0.5
    let rect:CGRect = CGRect(x: 0, y: 0, width: maxWidth, height: resizedHeight)
    UIGraphicsBeginImageContext(rect.size)
    image.draw(in: rect)
    let img: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
    let imageData:Data = img.jpegData(compressionQuality: compressionQuality)!
    UIGraphicsEndImageContext()
    let imageFinal = UIImage(data: imageData)!
        
    // preping to save image
    let imgData = imageFinal.pngData()
    let imageFile = PFFileObject(name:"image.png", data:imgData!)
    pg["Image"] = imageFile
        
    pg.saveInBackground { (succeeded, error)  in
        if (succeeded) {
            // The object has been saved.
            self.AllPages.append(page)
            self.UpdateFishCat { (success) in }
            completion(true)
        } else {
            // There was a problem
            completion(false)
        }
    }
}
```

&nbsp;&nbsp;&nbsp;&nbsp; 5\. In my info input page, I have an action of the submit button:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a. First, I created a new page element using the template of a class page.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b. Then I called my function and passed in the page I created.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c. Finally, if it is successful I move onto the next controller.

``` swift
@IBAction func submitToggled(_ sender: Any) {
    //create new page
    var newPage = page(fishType: inference, Image: image, desc: descriptionField.text, location: locationField.text!, catchDate: dateField.text!, weight:weightField.text!, dimensions: dimensionsField.text!)
    sessionManager.shared.requestCreateNewPage(page: newPage) { (success) in
        if success {
            //go to diff viewController
            let customViewController = self.storyboard?.instantiateViewController(withIdentifier: "CatchTypeTable") as! CatchTypeTable
                self.navigationController?.pushViewController(customViewController, animated: true)
       }
       else {
            //error
       }
    }
}
```

## Closing Remarks
When I started this project, I had never before interacted with SashiDo and Parse. Like most new things, it was challenging at first, but after sifting through lots of resources and trying to implement the concepts myself, it began to get easier and my understanding grew. I really enjoyed creating this project, and I hope you can learn from the knowledge and skills I gained in doing so. There are a lot of great tips and good practices within this tutorial, and I would love to see posts about other projects with Teachable Machine and SashiDo! Overall, SashiDo is a great backend platform that was really useful in creating this app, and I really recommend it for anyone who is interested in app development. Additionally, Teachable Machine makes machine learning and classification super easy and is great for cool projects like this one! 

A big thanks to SashiDo and to all you readers! I hope this was helpful and can’t wait to see where your journey takes you!

## Useful links:
My code: https://github.com/CarolineBaillie/FishClassification
SashiDo: https://www.sashido.io/en/
iOS Tensorflow Lite Template: https://github.com/lmoroney/dlaicourse
Install Cocoapods: https://www.youtube.com/watch?v=7Sp7ojClZPU
SashiDo Getting Started Guide: https://blog.sashido.io/sashidos-getting-started-guide/
iOS Parse Documentation: https://docs.parseplatform.org/ios/guide/
iOS Parse Video Playlist: https://www.youtube.com/playlist?list=PLMRqhzcHGw1ZFjFyHGJTTPuvcLbwVCuG4
Connections between elements and file: https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/ConnectTheUIToCode.html
Navigation programmatically go to next controller (answer 18): https://stackoverflow.com/questions/25326647/present-view-controller-in-storyboard-with-a-navigation-controller-swift
Signing up with Parse: https://docs.parseplatform.org/ios/guide/#signing-up
Logging in with Parse: https://docs.parseplatform.org/ios/guide/#logging-in
Pares Query constraints: https://docs.parseplatform.org/ios/guide/#query-constraints
Teachable Machine Node:
https://github.com/SashiDo/teachablemachine-node
The Awesome Teachable Machine List:
https://github.com/SashiDo/awesome-teachable-machine