<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Caroline Baillie - SashiDo.io | API Development, Deployment and Scaling made simple.]]></title><description><![CDATA[SashiDo.io is a serverless API development platform with scalable json rest and graphql apis, headless cms, built with nodejs, mongodb, parse server, kubernetes and docker.]]></description><link>https://blog.sashido.io/</link><image><url>https://blog.sashido.io/favicon.png</url><title>Caroline Baillie - SashiDo.io | API Development, Deployment and Scaling made simple.</title><link>https://blog.sashido.io/</link></image><generator>Ghost 1.20</generator><lastBuildDate>Sat, 23 May 2026 12:23:33 GMT</lastBuildDate><atom:link href="https://blog.sashido.io/author/caroline/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Xcode Collection of Useful Functions and Tips]]></title><description><![CDATA[Part 3 of a step-by-step guide on how to create your own digital scrapbook in iOS using a NodeJS backend from SashiDo. The tutorial is perfect for beginners, as well as passionate travelers and wanderers that are also into coding.]]></description><link>https://blog.sashido.io/xcode-collection-of-useful-functions-and-tips/</link><guid isPermaLink="false">607f633c14887600170b6259</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[iOS]]></category><category><![CDATA[Xcode]]></category><dc:creator><![CDATA[Caroline Baillie]]></dc:creator><pubDate>Thu, 22 Apr 2021 05:46:05 GMT</pubDate><media:content url="https://media-blog.sashido.io/content/images/2021/04/Xcode-Collection-of-Useful-Functions-and-Tips.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><h2 id="tableofcontents">Table of Contents</h2>
<ul>
<li><a href="#scrollview">Scroll View</a></li>
<li><a href="#keyboarddismiss">Keyboard Dismiss</a></li>
<li><a href="#backpropagationfunction">Backpropagation Function</a></li>
<li><a href="#loadingicon">Loading Icon</a></li>
<li><a href="#lightdarkmode">Light/Dark Mode</a></li>
</ul>
<h2 id="extras">Extras</h2>
<img src="https://media-blog.sashido.io/content/images/2021/04/Xcode-Collection-of-Useful-Functions-and-Tips.png" alt="Xcode Collection of Useful Functions and Tips"><p>When writing a tutorial for my latest iOS app, <a href="https://blog.sashido.io/snapshot-how-to-create-a-digital-scrapbook-in-ios-part-1">SnapShot</a>, there were bits of code that appeared often. I thought these steps were generally useful, so I created this collection of them to post separately.</p>
<h3 id="scrollview">Scroll View</h3>
<p>Below are my steps to incorporating a scroll view, but for more visual instruction check out <a href="https://www.youtube.com/watch?v=nfHBCQ3c4Mg">this video</a>.<br>
     1. Add a scroll view to the chosen controller<br>
     2. Drag it so it fills up the whole screen<br>
     3. Hover over the scrollView -&gt; click control -&gt; drag to View -&gt; select Leading/Top/Trailing/Bottom Space to Safe Area<br>
     4. Add a view on top of the scrollView<br>
     5. Repeat steps 2 &amp; 3 but with the view and scroll view, also clicking <code>Equal Widths/Heights</code><br>
     6. Click on the equal heights constraints and in the <code>Attributes Inspector</code>, set the priority to 250<br>
     7. Then click the controller -&gt; Size Inspector -&gt; Simulated Size -&gt; Freeform and set the height to a suitable value (for signup: 950)<br>
     8. Now add all the elements and make sure to add constraints to everything (otherwise the scrollView won’t work)<br>
     9. Finally, click scrollView -&gt; Attributes Inspector -&gt; Scroll View -&gt; Keyboard -&gt; Dismiss Interactively</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ex82wwh1r6i88umwreas.png" alt="Xcode Collection of Useful Functions and Tips"></p>
<h3 id="keyboarddismiss">Keyboard Dismiss</h3>
<p>You can override touchesBegan to dismiss the keyboard:</p>
<pre><code class="language-swift">override func touchesBegan(_ touches: Set&lt;UITouch&gt;, with event: UIEvent?) {
        // option 1
        self.view.endEditing(true)
        // option2
        usernameTextField.resignFirstResponder()
        passwordTextField.resignFirstResponder()
}
</code></pre>
<h3 id="backpropagationfunction">Backpropagation Function</h3>
<p>     1. In <strong>1viewDidLoad1</strong> of the controller, you want the function to run in:</p>
<pre><code class="language-swift">NotificationCenter.default.addObserver(self, selector: #selector(reloadContent), name: Notification.Name(&quot;reloadContent&quot;), object: nil)
</code></pre>
<p>     2. In the same file, add the function:</p>
<pre><code class="language-swift">@objc func reloadContent (notification: NSNotification){
        // stuff you want to run in func - e.g...
        self.memTitle.text = page.title
        memTitle.adjustsFontSizeToFitWidth = true
        self.memImage.image = page.Image
        self.memDesc.text = page.desc
        self.memCategory.text = page.category
        self.memLocation.text = page.location
        self.memTags.text = page.tags
        self.memDate.text = page.date
    }
</code></pre>
<p>     3. In the controller you want to call the function from, write:</p>
<pre><code class="language-swift">NotificationCenter.default.post(name: Notification.Name(&quot;reloadContent&quot;), object: nil)
</code></pre>
<h3 id="loadingicon">Loading Icon</h3>
<p>     1. In the <code>Classes</code> folder, create a new file called <strong><code>AIUtil.swift</code></strong><br>
     2. Add the following code:</p>
<pre><code class="language-swift">@objc func reloadContent (notification: NSNotification) {
        self.memTitle.text = page.title
        memTitle.adjustsFontSizeToFitWidth = true
        self.memImage.image = page.Image
        self.memDesc.text = page.desc
        self.memCategory.text = page.category
        self.memLocation.text = page.location
        self.memTags.text = page.tags
        self.memDate.text = page.date
    }
</code></pre>
<p>     3. When you want to show the spinner: <code>self.showSpinner()</code><br>
     4. When you want to remove the spinner: <code>self.removeSpinner()</code></p>
<h3 id="lightdarkmode">Light/Dark mode</h3>
<p>If your computer’s simulation shows the default color as white (lightmode) but your phone is in darkmode, the colors will be black when you run the app on your phone. This is an issue I ran into, but luckily there is an easy solution:<br>
     1. Go to <strong><code>SceneDelegate.swift</code></strong><br>
     2. In the first function (scene), add the following code:</p>
<pre><code class="language-swift">if #available(iOS 13.0, *) {
        window?.overrideUserInterfaceStyle = .light // for light mode
        window?.overrideUserInterfaceStyle = .dark // for dark mode
}
</code></pre>
<p>Happy Coding!</p>
<h2 id="resources">Resources</h2>
<p><a href="https://blog.sashido.io/snapshot-how-to-create-a-digital-scrapbook-in-ios-part-1">SnapShot: How to Create a Digital Scrapbook in iOS - Part 1</a><br>
<a href="https://blog.sashido.io/snapshot-how-to-create-a-digital-scrapbook-in-ios-part-2">SnapShot: How to Create a Digital Scrapbook in iOS - Part 2</a><br>
<a href="https://youtu.be/wTXN-c1tPHs">Video Demo</a><br>
<a href="https://github.com/CarolineBaillie/snapShot">SnapShot GitHub Repo</a><br>
<a href="https://www.sashido.io/en/">SashiDo</a><br>
<a href="https://docs.parseplatform.org/ios/guide/">Parse Documentation</a><br>
<a href="https://www.youtube.com/playlist?list=PLMRqhzcHGw1ZFjFyHGJTTPuvcLbwVCuG4">Parse Video Playlist</a></p>
</div>]]></content:encoded></item><item><title><![CDATA[SnapShot: How to Create a Digital Scrapbook in iOS - Part 2]]></title><description><![CDATA[Part 2 of a step-by-step guide on how to create your own digital scrapbook in iOS using a NodeJS backend from SashiDo. The tutorial is perfect for beginners, as well as passionate travelers and wanderers that are also into coding.]]></description><link>https://blog.sashido.io/snapshot-how-to-create-a-digital-scrapbook-in-ios-part-2/</link><guid isPermaLink="false">607f6cdf14887600170b625a</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[iOS]]></category><category><![CDATA[Xcode]]></category><dc:creator><![CDATA[Caroline Baillie]]></dc:creator><pubDate>Thu, 22 Apr 2021 05:43:32 GMT</pubDate><media:content url="https://media-blog.sashido.io/content/images/2021/04/How-to-Create-a-Digital-Scrapbook-in-iOS-part-2-1.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://media-blog.sashido.io/content/images/2021/04/How-to-Create-a-Digital-Scrapbook-in-iOS-part-2-1.png" alt="SnapShot: How to Create a Digital Scrapbook in iOS - Part 2"><p>Welcome to Part 2 of <strong>SnapShot: A Digital iOS Scrapbook</strong>! If you haven’t seen <a href="https://blog.sashido.io/snapshot-how-to-create-a-digital-scrapbook-in-ios-part-1">Part 1</a>, I recommend you check it out as it takes care of all of the setup and registration.</p>
<p>Just to refresh your mind, SnapShot is an iOS app made using Xcode and <a href="https://www.sashido.io/en/">SashiDo</a>. It is kind of like a digital scrapbook, but for more information, here is the video demo:</p>
<p><iframe width="854" height="480" src="https://www.youtube.com/embed/wTXN-c1tPHs" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe></p>
<p>Again, I recommend looking at <a href="https://blog.sashido.io/snapshot-how-to-create-a-digital-scrapbook-in-ios-part-1">SnapShot Part 1</a>, or even my first tutorial <a href="https://dev.to/carolinebaillie/fish-classification-ios-app-with-sashido-and-teachable-machine-417i"><em>Fish Classification iOS App with SashiDo and Teachable Machine</em></a>). Additionally, all my code is available on my <a href="https://github.com/CarolineBaillie/snapShot">GitHub</a>.</p>
<h2 id="tableofcontents">Table of Contents</h2>
<ul>
<li><a href="#map">Map</a>
<ul>
<li><a href="#setup">Setup</a></li>
<li><a href="#memoryclass">Memory Class</a></li>
<li><a href="#requestmemories">Request Memories</a></li>
<li><a href="#interactiveannotations">Annotations</a></li>
<li><a href="#memoryinfo-page">Memory Info Page</a></li>
<li><a href="#addannotation">Add Annotation</a></li>
<li><a href="#newmemory">New Memory</a></li>
</ul>
</li>
<li><a href="#delete">Delete</a></li>
<li><a href="#edit">Edit</a></li>
<li><a href="#storing">Storing</a></li>
<li><a href="#closingremarks">Closing Remarks</a></li>
<li><a href="#resources">Resources</a></li>
</ul>
<h2 id="map">Map</h2>
<p>Adding the map is probably the biggest step in this project. Below I have outlined all the steps I took to create and connect everything. However, if you want more visual instructions on how to create and display the map, I recommend checking out <a href="https://www.youtube.com/watch?v=p0dOywHx0VI&amp;list=RDCMUCmJi5RdDLgzvkl3Ly0DRMlQ&amp;start_radio=1&amp;t=1">this video playlist</a>!</p>
<h3 id="setup">Setup</h3>
<p>     1. Go to the <strong><code>storyboard</code></strong> map controller (I created mine on the default <strong><code>ViewController</code></strong>)<br>
     2. Add <code>MKMapView</code> controller<br>
     3. Drag the sides so the <code>MKMapView</code> takes up the whole screen<br>
     4. On the top bar, click Editor -&gt; Resolve Auto Layout Issues -&gt; Add Missing Constraints (selected views)<br>
<img src="https://media-blog.sashido.io/content/images/2021/04/mapConstraints.png" alt="SnapShot: How to Create a Digital Scrapbook in iOS - Part 2"><br>
     5. Open dual editors (<strong><code>storyboard</code></strong> and <strong><code>ViewController.swift</code></strong>)<br>
         a. Control drag from <code>MKMapView</code> into the <code>ViewController</code> class<br>
         b. Call the outlet mapView<br>
     6. Select the <code>MKMapView/mapView</code><br>
     7. Control drag to <code>ViewController</code> and click <code>delegate</code><br>
     8. Make sure you have <code>import MapKit</code> and <code>import CoreLocation</code> in <strong><code>ViewController.swift</code></strong><br>
     9. Make sure <strong><code>ViewController.swift</code></strong> conforms to <code>MKMapViewDelegate</code><br>
We also have to set up a reload function that could be called from a different controller (used when memory is <a href="#edit">edited</a>). The code is also given in the Extras <a href="https://blog.sashido.io/xcode-collection-of-useful-functions-and-tips">Backpropagation Function Section</a>, but basically there are only two sections of code for <strong><code>ViewController.swift</code></strong>:<br>
     1. In <code>viewDidLoad</code> add this:</p>
<pre><code class="language-swift">// setup for function that is called from newMemory
NotificationCenter.default.addObserver(self, selector: #selector(reload), name: Notification.Name(&quot;reload&quot;), object: nil)
</code></pre>
<p>     2. Add the following reload function inside the class:</p>
<pre><code class="language-swift">// function to reload newMemories bc when dismissed adding controller doesn't show without
@objc func reload (notification: NSNotification){
    self.mapView.removeAnnotations(self.mapView.annotations)
    for m in sessionManager.shared.memories {
        // if there is a mapView (protection)
        if mapView != nil {
            // add that Memory
            mapView.addAnnotation(m)
        }
    }
}
</code></pre>
<h3 id="memoryclass">Memory Class</h3>
<p>     1. Inside the <code>Classes</code> folder, create a new <code>Cocoa Touch Class</code> file<br>
     2. Make sure your options are filled in as follows:<br>
<img src="https://media-blog.sashido.io/content/images/2021/04/classMemory.png" alt="SnapShot: How to Create a Digital Scrapbook in iOS - Part 2"><br>
     3. Click <code>next</code> and <code>create</code><br>
     4. Inside this file, create a class with all the elements of each memory:</p>
<pre><code class="language-swift">import UIKit
import MapKit

class Memory: NSObject, MKAnnotation {
    var title: String?
    var coordinate: CLLocationCoordinate2D
    var desc: String
    var category: String?
    var tags: String?
    var location: String
    var date: String
    var Image: UIImage
    var objID: String
    
    init(title:String, Image:UIImage, coordinate:CLLocationCoordinate2D,desc:String,category:String,tags:String, location:String,date:String, objID:String){
        self.title = title
        self.Image = Image
        self.coordinate = coordinate
        self.desc = desc
        self.category = category
        self.tags = tags
        self.location = location
        self.date = date
        self.objID = objID
    }
}
</code></pre>
<p>Now you can create a variable of type Memory and add it to the annotation map. However, we want to use the information from the database to populate this annotation. Therefore we must create a function in sessionManager.</p>
<h3 id="requestmemories">Request Memories</h3>
<p>For our current task, we want a function that gets all the data from SashiDo:<br>
     1. Inside the <strong><code>sessionManager</code></strong> class, add <code>var memories : [Memory]! = []</code> to create an array of type <code>Memory</code><br>
     2. Add the following function:</p>
<pre><code class="language-swift">// get all memories
func GetAllMem(completion:@escaping (_ success:Bool) -&gt; ()){
    // clear previous data
    memories.removeAll()
    // get all rows
    let query = PFQuery(className: &quot;Memory&quot;)
    // where user is equal to current user
    query.whereKey(&quot;userID&quot;, 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 {
                    // extract the image
                    let file = object[&quot;Image&quot;] as! PFFileObject
                    file.getDataInBackground { (imageData: Data?, error: Error?) in
                        if error == nil {
                            if let imageData = imageData {
                                let image = UIImage(data: imageData)
                                // convert coord of type GeoPoint to CLLocationCoordinate2D
                                let coor = object[&quot;coordinate&quot;]! as! PFGeoPoint
                                let coord = CLLocationCoordinate2D(latitude: coor.latitude, longitude: coor.longitude)
                                // create a new memory
                                let memData = Memory(title: object[&quot;title&quot;]! as! String, Image: (image ?? UIImage(named: &quot;test&quot;))!, coordinate: coord, desc: object[&quot;desc&quot;]! as! String, category: object[&quot;category&quot;]! as! String, tags: object[&quot;tags&quot;]! as! String, location: object[&quot;location&quot;]! as! String, date: object[&quot;date&quot;]! as! String, objID: object.objectId!)
                                // add memory to the global array
                                self.memories.append(memData)
                            }
                        }
                    }
                }
            }
            completion(true)
        }
        else {
            //return false completion if fails
            completion(false)
        }
    }
}
</code></pre>
<p>         a. Essentially the function is getting all the information from the database rows that correspond with the current user (using <a href="https://docs.parseplatform.org/ios/guide/#queries">Parse Documentation</a>)<br>
         b. It then loops through all the rows and creates a variable of type <code>Memory</code><br>
         c. Finally, it adds that memory to the array<br>
     3. Now go to <strong><code>settings.swift</code></strong> and add this code to <code>viewDidLoad</code>:</p>
<pre><code class="language-swift">// get all memories from database
sessionManager.shared.GetAllMem { (success) in
        if success {
            self.removeSpinner()
        }
}
</code></pre>
<p>         a. This means that whenever a user logs in, all their information will be saved to the array <code>memories</code> in <code>sessionManager</code> (<code>sessionManager.shared.memories</code>)<br>
     4. To show these memories as annotations on the map, return to <strong><code>ViewContoller.swift</code></strong> and add this code in <code>viewDidLoad</code>:</p>
<pre><code class="language-swift">// go through all memories in global list
for m in sessionManager.shared.memories {
    // if there is a mapView (protection)
    if mapView != nil {
        // add that Memory
        mapView.addAnnotation(m)
    }
}
</code></pre>
<p>         a. This code just loops through all the memories and adds the memory to the <code>mapView</code></p>
<h3 id="interactiveannotations">Interactive Annotations</h3>
<p>Now that we have displayed all our memories, we want to be able to click on them and open a new controller that displays all the information. To do this we need to add two functions to <strong>ViewController.swift</strong>:<br>
     1. This function changes the style of the pin and displays the i button when clicked:</p>
<pre><code class="language-swift">func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -&gt; MKAnnotationView? {
    // make sure only right type of pins display
    guard annotation is Memory else {return nil}
    // create identifier for annotation views
    let identifier = &quot;Memory&quot;
    // get back an annotation if it is one with identifier, otherwise nil
    var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
    if annotationView == nil {
        annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
        annotationView?.canShowCallout = true
        //create button - i button for info
        let btn = UIButton(type: .detailDisclosure)
        annotationView?.rightCalloutAccessoryView = btn
    } else {
        // if have in dq go ahead and use
        annotationView?.annotation = annotation
    }
    return annotationView
}
</code></pre>
<p>     2. This function presents a different view controller when the i button is clicked</p>
<pre><code class="language-swift">func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
    // make sure is type Memory
    guard let memory = view.annotation as? Memory else {return}
    if control == view.rightCalloutAccessoryView {
        // go to info page for that memory
        let obj = UIStoryboard(name: &quot;Main&quot;, bundle: nil).instantiateViewController(withIdentifier: &quot;memInfoPage&quot;) as! memInfoPage
        // pass memory information
        obj.page = memory
        self.present(obj, animated: true, completion: nil)
   }
}
</code></pre>
<h3 id="memoryinfopage">Memory Info Page</h3>
<p>The previous code showed you how to make an annotation clickable and open a new controller. It also sent information to the next controller (<code>memInfoPage</code>). To display this information in <strong><code>memInfoPage.swift</code></strong> you must:<br>
     1. Create outlets for all the storyboard elements (labels, images, etc.)<br>
     2. Add <code>var page : Memory!</code> to the top of the class to collect the memory passed from <code>ViewContoller.swift</code><br>
     3. In <code>viewDidLoad</code>, pass the information from <code>page</code> into all the storyboard elements<br>
     4. Add a <a href="https://blog.sashido.io/xcode-collection-of-useful-functions-and-tips">function</a> to reload the controller (this is needed for <a href="#edit">editing the memory</a>)<br>
     5. All together it should look like this:</p>
<pre><code class="language-swift">import Foundation
import UIKit
import Parse

class memInfoPage : UIViewController {
    // var from prev page
    var page : Memory!
    // connections
    @IBOutlet weak var memTitle: UILabel!
    @IBOutlet weak var memImage: UIImageView!
    @IBOutlet weak var memDesc: UITextView!
    @IBOutlet weak var memCategory: UILabel!
    @IBOutlet weak var memLocation: UILabel!
    @IBOutlet weak var memTags: UITextView!
    @IBOutlet weak var memDate: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.memTitle.text = page.title
        memTitle.adjustsFontSizeToFitWidth = true
        self.memImage.image = page.Image
        self.memDesc.text = page.desc
        self.memCategory.text = page.category
        self.memLocation.text = page.location
        self.memTags.text = page.tags
        self.memDate.text = page.date
        NotificationCenter.default.addObserver(self, selector: #selector(reloadContent), name: Notification.Name(&quot;reloadContent&quot;), object: nil)
    }
    
    @objc func reloadContent (notification: NSNotification){
        self.memTitle.text = page.title
        memTitle.adjustsFontSizeToFitWidth = true
        self.memImage.image = page.Image
        self.memDesc.text = page.desc
        self.memCategory.text = page.category
        self.memLocation.text = page.location
        self.memTags.text = page.tags
        self.memDate.text = page.date
    }
}
</code></pre>
<p>Now when you click the i button on an annotation, a page with all the information about the memory will appear.</p>
<h3 id="addannotation">Add Annotation</h3>
<p>The next logical step is to create a way to add new memories:<br>
     1. Add a button to the <strong><code>storyboard</code></strong> map controller and create an action function (<code>addAnnotation</code>) connecting to <strong><code>ViewController.swift</code></strong><br>
     2. Insert the following code to present a camera controller (<code>picController</code>):</p>
<pre><code class="language-swift">@IBAction func addAnnotation(_ sender: Any) {
    // should pop up different controller so that can enter info
    let obj = UIStoryboard(name: &quot;Main&quot;, bundle: nil).instantiateViewController(withIdentifier: &quot;picController&quot;) as! picController
    self.present(obj, animated: true, completion: nil)
}
</code></pre>
<p>To create <strong>picController.swift</strong>:<br>
     1. If you haven’t already, create a controller with a button and <code>imageView</code><br>
     2. Create a file called <code>picController.swift</code> with the following code:</p>
<pre><code class="language-swift">import Foundation
import UIKit

class picController: UIViewController {
    
    @IBOutlet weak var img: UIImageView!

     // create camera
    let pickerController = UIImagePickerController()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // set camera
        pickerController.sourceType = UIImagePickerController.SourceType.camera
        pickerController.delegate = self
    }
    // linked to button that shows the camera
    @IBAction func onClickTakePic(_ sender: Any) {
        present(pickerController, animated: true, completion: nil)
    }
}
// image taken
extension picController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        picker.dismiss(animated: true, completion: nil)
        if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
            // set image taken to image on screen
            img.image = image
            dismiss(animated: true, completion: nil)
            // send image to create new mem
            let obj = UIStoryboard(name: &quot;Main&quot;, bundle: nil).instantiateViewController(withIdentifier: &quot;newMemory&quot;) as! newMemory
            obj.image = image
            self.present(obj, animated: true, completion: nil)
        }
    }
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        //close out (camera has been closed)
    }
}
</code></pre>
<p>     3. Connect <code>img</code> and <code>onClickTakePic</code> to the image and button on the controller<br>
     4. Allow camera access:<br>
         a. Go to the outermost <code>snapShot.xcodeproj</code><br>
         b. info -&gt; Custom iOS Target Properties -&gt; right click → add row -&gt; Privacy - Camera Usage Description<br>
<img src="https://media-blog.sashido.io/content/images/2021/04/privacyProperties.png" alt="SnapShot: How to Create a Digital Scrapbook in iOS - Part 2"><br>
From the code, you can see that once the picture is taken, it sends the information to a different controller called <code>newMemory</code>.</p>
<h3 id="newmemory">New Memory</h3>
<p><strong><code>newMemory</code></strong> is the controller where the user inputs all the different information on the memory they wish to create. The corresponding file has 4 major parts to it:<br>
     1. Basic setup<br>
     2. Gets users location<br>
     3. Gets current date<br>
     4. Creates a new memory<br>
Breaking each part down…</p>
<p>Basic setup:<br>
     1. Add <code>var image : UIImage!</code> to the top of the <strong>newMemory</strong> class (it will store the image passed from picController)<br>
     2. Create connections between each input field, image, and button<br>
     3. In <code>viewDidLoad</code>, add <code>self.memImage.image = image</code> to set the image</p>
<p>Location:<br>
     1. Allow location access:<br>
         a. Go to the outermost snapShot.xcodeproj<br>
         b. info -&gt; Custom iOS Target Properties -&gt; right click add row (x2) -&gt; Privacy - Location When In Use Usage Description &amp; Privacy - Location Always and When In Use Usage Description<br>
     2. Add <code>private var locationManager:CLLocationManager?</code> to the top of the <strong><code>newMemory</code></strong> class so we can access the user’s location<br>
     3. Add the following two functions to the class:</p>
<pre><code class="language-swift">func getUserLocation() {
    locationManager = CLLocationManager()
    locationManager?.requestAlwaysAuthorization()
    locationManager?.startUpdatingLocation()
    locationManager?.delegate = self
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    if let location = locations.last {
        sessionManager.shared.currentCoord = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
    }
}
</code></pre>
<p>     4. In <strong><code>sessionManager.swift</code></strong> add <code>var currentCoord : CLLocationCoordinate2D?</code> as a variable at the top of the class (this will store the users current coordinate)<br>
     5. Finally, back in <strong><code>newMemory.swift</code></strong>, create a variable for the location in the save button function (<code>saveToggled</code>)</p>
<pre><code class="language-swift">getUserLocation()
let coord = sessionManager.shared.currentCoord
</code></pre>
<p>Date:<br>
     1. Add the following to <strong><code>saveToggled</code></strong>:</p>
<pre><code class="language-swift">let date = Date()
let calendar = Calendar.current
let y = calendar.component(.year, from: date)
let m = calendar.component(.month, from: date)
let d = calendar.component(.day, from: date)
let dateNow = &quot;\(m)/\(d)/\(y)&quot;
</code></pre>
<p>New Memory:<br>
     1. In <strong><code>saveToggled</code></strong>, create a new variable of type Memory and save it to sessionManager</p>
<p>Code all together:</p>
<pre><code class="language-swift">import Foundation
import UIKit
import MapKit
import Parse
import CoreLocation

class newMemory : UIViewController, UITextFieldDelegate, CLLocationManagerDelegate {
    
    var image : UIImage!
    private var locationManager:CLLocationManager?
    
    @IBOutlet weak var memTitle: UITextField!
    @IBOutlet weak var memCategory: UITextField!
    @IBOutlet weak var memImage: UIImageView!
    @IBOutlet weak var memDesc: UITextView!
    @IBOutlet weak var memLocation: UITextField!
    @IBOutlet weak var memTags: UITextView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Set image from passed in value
        self.memImage.image = image
    }
    
    //dismiss keyboard when tapped
    override func touchesBegan(_ touches: Set&lt;UITouch&gt;, with event: UIEvent?) {
        self.view.endEditing(true)
    }
    
    @IBAction func saveToggled(_ sender: Any) {
        self.showSpinner()
        // find current location
        getUserLocation()
        let coord = sessionManager.shared.currentCoord
        // get current date
        let date = Date()
        let calendar = Calendar.current
        let y = calendar.component(.year, from: date)
        let m = calendar.component(.month, from: date)
        let d = calendar.component(.day, from: date)
        let dateNow = &quot;\(m)/\(d)/\(y)&quot;
        // create new memory
        let newMem = Memory(title: memTitle.text!, Image: image, coordinate: coord, desc: memDesc.text!, category: memCategory.text!, tags: memTags.text!, location: memLocation.text!, date: dateNow, objID: &quot;none&quot;)
        // save memory
        sessionManager.shared.saveMemory(memory: newMem) { (success) in
            // reload map controller
            NotificationCenter.default.post(name: Notification.Name(&quot;reload&quot;), object: nil)
            // move to next view controller
            self.dismiss(animated: true, completion: nil)
        }
    }
    // get user location stuff
    func getUserLocation() {
        locationManager = CLLocationManager()
        locationManager?.requestAlwaysAuthorization()
        locationManager?.startUpdatingLocation()
        locationManager?.delegate = self
    }
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        if let location = locations.last {
            sessionManager.shared.currentCoord = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
        }
    }
}
</code></pre>
<p>In <strong>s<code>essionManager.swift</code></strong>, the function to save a memory looked like this:<br>
     1. Set all the database rows to be the inputted information<br>
     2. Reduce the image size (otherwise might cause error)<br>
     3. Save changes to the database<br>
     4. Add the new memory to global array (<code>memories</code>)</p>
<pre><code class="language-swift">func saveMemory (memory:Memory, completion:@escaping (_ success:Bool) -&gt; ()) {
    // set values for new memory
    let mem = PFObject(className:&quot;Memory&quot;)
    mem[&quot;title&quot;] = memory.title
    mem[&quot;desc&quot;] = memory.desc
    mem[&quot;location&quot;] = memory.location
    mem[&quot;date&quot;] = memory.date
    mem[&quot;userID&quot;] = user.id
    mem[&quot;tags&quot;] = memory.tags
    mem[&quot;category&quot;] = memory.category
    mem[&quot;coordinate&quot;] = PFGeoPoint(latitude: memory.coordinate.latitude, longitude: memory.coordinate.longitude)
    // reducing image size
    let image = memory.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)!
    // prepping to save image
    let imgData = imageFinal.pngData()
    let imageFile = PFFileObject(name:&quot;image.png&quot;, data:imgData!)
    mem[&quot;Image&quot;] = imageFile
    // save all
    mem.saveInBackground { (succeeded, error)  in
        if (succeeded) {
           // The object has been saved.
            memory.objID = mem.objectId as! String
            self.memories.append(memory)
            completion(true)
        } else {
            // There was a problem
            completion(false)
        }
    }
}
</code></pre>
<h2 id="delete">Delete</h2>
<p>I wanted to allow the user to delete a memory if they no longer wanted it. Fortunately, this process was relatively simple:<br>
     1. Create an action function from the <strong><code>memInfoPage</code></strong> delete button (<code>deleteToggled</code>)<br>
     2. Create an alert asking the user to confirm that they want to delete this item<br>
     3. If canceled dismiss<br>
     4. If confirmed call the <code>sessionManager</code> delete function and return to the dismiss the controller</p>
<pre><code class="language-swift">@IBAction func deleteToggle(_ sender: Any) {
    // alert are you sure you want to delete this
    let confirmAlert = UIAlertController(title: &quot;Delete&quot;, message: &quot;Are you sure you want to delete this memory?&quot;, preferredStyle: UIAlertController.Style.alert)
    // alert confirmed
    confirmAlert.addAction(UIAlertAction(title: &quot;Confirm&quot;, style: .default, handler: { (action: UIAlertAction!) in
        // run delete function
        sessionManager.shared.deleteMemory(memory: self.page) { (success) in
            // reload map controller
            NotificationCenter.default.post(name: Notification.Name(&quot;reload&quot;), object: nil)
            self.dismiss(animated: true, completion: nil)
        }
    }))
    // alert canceled
    confirmAlert.addAction(UIAlertAction(title: &quot;Cancel&quot;, style: .cancel, handler: { (action: UIAlertAction!) in
        // do nothing
    }))
    // show the alert
    present(confirmAlert, animated: true, completion: nil)
}
</code></pre>
<p>To delete the memory in the <strong><code>sessionManager.swift</code></strong> function I had to:<br>
     1. Find that memory in the database<br>
     2. Delete it from the database<br>
     3. Delete it from the global memories array (by comparing objectIDs)</p>
<pre><code class="language-swift">func deleteMemory (memory:Memory, completion:@escaping (_ success:Bool) -&gt; ()) {
    // get the memory
    let query = PFQuery(className:&quot;Memory&quot;)
    query.getObjectInBackground(withId: memory.objID) { (object, error) in
        if error == nil {
            // Success!
            if let object = object {
                // delete this row
                object.deleteInBackground()
                // delete from array memories
                self.deleteMemArray(memory: memory)
            }
            completion(true)
        } else {
            // Fail!
            completion(false)
        }
    }
}

func deleteMemArray(memory:Memory) {
    // loop through all memories in array
    for i in 0..&lt;memories.count-1 {
        // if the objectIDs match
        if memories[i].objID == memory.objID {
            // delete the memory
            memories.remove(at: i)
        }
    }
}
</code></pre>
<p>I used two separate functions as a stylistic choice.</p>
<h2 id="edit">Edit</h2>
<p>I also wanted users to be able to edit memories instead of having to delete and recreate them. This proved to be a little bit harder, but I eventually got it to work:<br>
     1. Create an action function from the <strong><code>memInfoPage</code></strong> edit button (<code>editToggled</code>)<br>
     2. Present and pass information to the edit controller (<code>editMem</code>)</p>
<pre><code class="language-swift">@IBAction func editToggle(_ sender: Any) {
    // put all inputs into the text stuff to be resaved
    let VC = self.storyboard?.instantiateViewController(withIdentifier: &quot;editMem&quot;) as! editMem
    // pass memory information
    VC.page = page
    self.present(VC, animated: true, completion: nil)
}
</code></pre>
<p>     3. In <strong><code>editMem.swift</code></strong>, write the following code:</p>
<pre><code class="language-swift">import Foundation
import UIKit
import MapKit
import Parse
import CoreLocation

class editMem: UIViewController, UITextFieldDelegate, CLLocationManagerDelegate {
    
    var page : Memory!
    
    @IBOutlet weak var memTitle: UITextField!
    @IBOutlet weak var memCategory: UITextField!
    @IBOutlet weak var memImage: UIImageView!
    @IBOutlet weak var memDesc: UITextView!
    @IBOutlet weak var memTags: UITextView!
    @IBOutlet weak var memLocation: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // set all input fields to the previous values
        memTitle.text = page.title
        memCategory.text = page.category
        memDesc.text = page.desc
        memTags.text = page.tags
        memLocation.text = page.location
        memImage.image = page.Image
    }
    
    @IBAction func memUpdate(_ sender: Any) {
        self.showSpinner()
        // can't edit coord or image
        let coord = page.coordinate
        let image = page.Image
        // create new memory with inputted info
        let newMem = Memory(title: memTitle.text!, Image: image, coordinate: coord, desc: memDesc.text!, category: memCategory.text!, tags: memTags.text!, location: memLocation.text!, date: page.date, objID: page.objID)
        // call update function
        sessionManager.shared.updateMemory(memory: newMem) { (success) in
            self.removeSpinner()
            // go back to memInfoPage
            let VC = self.storyboard?.instantiateViewController(withIdentifier: &quot;memInfoPage&quot;) as! memInfoPage
            VC.page = newMem
            self.dismiss(animated: true, completion: nil)
            // call reload functions so update appears
            NotificationCenter.default.post(name: Notification.Name(&quot;reloadContent&quot;), object: nil)
            NotificationCenter.default.post(name: Notification.Name(&quot;reload&quot;), object: nil)
        }
    }
}
</code></pre>
<p>         a. This code first sets all the inputs equal to whatever the previous information on the memory was<br>
         b. The user can then edit the information and click the update button when finished (which runs <code>memUpdate</code>)<br>
         c. <code>memUpdate</code> creates a new memory with the updated information and runs the <code>updateMemory</code> function in <code>sessionManager</code><br>
         d. It then dismisses the controller (returning to <code>memInfopage</code>)<br>
         e. Finally, it triggers the reload functions mentioned earlier in <code>memInfoPage</code> and <code>ViewController</code> so that the updated information is displayed<br>
In <strong>sessionManager</strong>, create an update function that:<br>
     1. Finds the object in the database<br>
     2. Replaces with and saves the new information<br>
     3. Replaces the old memory with the new memory in the global memories array (by comparing objectIDs)</p>
<pre><code class="language-swift">func updateMemory (memory:Memory, completion:@escaping (_ success:Bool) -&gt; ()) {
    // find memory
    let query = PFQuery(className:&quot;Memory&quot;)
    // with the same objectID
    query.getObjectInBackground(withId: memory.objID) { (object, error) in
        if error == nil {
            // Success!
            if let object = object {
                // update all values
                object[&quot;title&quot;] = memory.title
                object[&quot;desc&quot;] = memory.desc
                object[&quot;location&quot;] = memory.location
                object[&quot;date&quot;] = memory.date
                object[&quot;tags&quot;] = memory.tags
                object[&quot;category&quot;] = memory.category
            }
            // save object
            object!.saveInBackground()
            // change in global array
            self.updateMemArray(memory: memory)
            completion(true)
        } else {
            // Fail!
            completion(false)
        }
    }
}

func updateMemArray(memory:Memory) {
    // loop through memories
    for m in memories {
        // if objectIDs the same
        if m.objID == memory.objID {
            // update that memory in the global array
            m.title = memory.title
            m.desc = memory.desc
            m.location = memory.location
            m.date = memory.date
            m.tags = memory.tags
            m.category = memory.category
        }
    }
}
</code></pre>
<p>Again, using two functions is stylistic.</p>
<h2 id="storing">Storing</h2>
<p>Most of the previous code has been edited to exclude the sorting <code>tableViewControllers</code> and <code>collectionViewControllers</code>. This is because accessing information this way presented challenges when editing the memories (it was difficult to get the updated information to show up when clicking the navigation back button). Although I managed to do it in the end, including those steps and code in this tutorial added another layer of length and complexity. However, if you are interested in adding a similar kind of sorting mechanism, check out my code on <a href="https://github.com/CarolineBaillie/snapShot">GitHub</a> to see how it was done!</p>
<h2 id="closingremarks">Closing Remarks</h2>
<p>Congratulations, you have now created your own digital scrapbook! I know I learned a lot from this project, and hope you did, too. I encourage all readers to embark on the rewarding journey of experimenting with and creating tutorials about Xcode, SashiDo, and iOS Maps. There are so many great resources all over the internet that help make your life easier when coding so there really is no downside to taking things into your own hands and having fun. I hope you all enjoyed my tutorial and again, make sure to check out (or download) my code on GitHub to get the full experience or my <a href="https://dev.to/carolinebaillie/fish-classification-ios-app-with-sashido-and-teachable-machine-417i">previous tutorial</a> that uses <a href="https://teachablemachine.withgoogle.com/">Teachable Machine</a> to classify fish types.</p>
<p>Thanks so much to SashiDo and all you readers! Happy future coding!</p>
<h2 id="resources">Resources</h2>
<p><a href="https://blog.sashido.io/snapshot-how-to-create-a-digital-scrapbook-in-ios-part-1">SnapShot: How to Create a Digital Scrapbook in iOS - Part 1</a><br>
<a href="https://blog.sashido.io/xcode-collection-of-useful-functions-and-tips">An Xcode Collection of Useful Functions and Tips</a><br>
<a href="https://youtu.be/wTXN-c1tPHs">Video Demo</a><br>
<a href="https://github.com/CarolineBaillie/snapShot">SnapShot GitHub Repo</a><br>
<a href="https://www.sashido.io/en/">SashiDo</a><br>
<a href="https://docs.parseplatform.org/ios/guide/">Parse Documentation</a><br>
<a href="https://www.youtube.com/playlist?list=PLMRqhzcHGw1ZFjFyHGJTTPuvcLbwVCuG4">Parse Video Playlist</a></p>
</div>]]></content:encoded></item><item><title><![CDATA[SnapShot: How to Create a Digital Scrapbook in iOS - Part 1]]></title><description><![CDATA[Part 1 of a step-by-step guide on how to create your own digital scrapbook in iOS using a NodeJS backend from SashiDo. The tutorial is perfect for beginners, as well as passionate travelers and wanderers that are also into coding.]]></description><link>https://blog.sashido.io/snapshot-how-to-create-a-digital-scrapbook-in-ios-part-1/</link><guid isPermaLink="false">607f37ac14887600170b6258</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[iOS]]></category><category><![CDATA[Xcode]]></category><dc:creator><![CDATA[Caroline Baillie]]></dc:creator><pubDate>Thu, 22 Apr 2021 05:41:39 GMT</pubDate><media:content url="https://media-blog.sashido.io/content/images/2021/04/How-to-Create-a-Digital-Scrapbook-in-iOS-part-1.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://media-blog.sashido.io/content/images/2021/04/How-to-Create-a-Digital-Scrapbook-in-iOS-part-1.png" alt="SnapShot: How to Create a Digital Scrapbook in iOS - Part 1"><p>When you're standing before the Great Wall of China, going out to lunch with friends, or even wandering through the middle of nowhere, don’t you want to snap a picture and easily keep track of all your adventures and experiences? Sure, you can take hundreds of photos on your phone, but there are always so many to go through. Wouldn’t it be nice to have a digital map filled with specific pictures for specific locations?</p>
<p>As a traveler and wanderer, I definitely wanted an app like this, and as a coder, I wanted to learn more about the iOS map feature. Inspired, I set out to create a sort of digital scrapbook that I call SnapShot. Today I want to share my newly gained experience through a tutorial, but first, here is a demo of the final product:</p>
<p><iframe width="854" height="480" src="https://www.youtube.com/embed/wTXN-c1tPHs" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe></p>
<p>In this tutorial, I will be going over how to set up and use the amazing backend platform SashiDo (also used in my prior tutorial: <a href="https://dev.to/carolinebaillie/fish-classification-ios-app-with-sashido-and-teachable-machine-417i">Fish Classification iOS App with SashiDo and Teachable Machine</a>) as well as going over the basic components of my app. Also, all my code is available on my <a href="https://github.com/CarolineBaillie/snapShot">GitHub</a>. Before continuing, I recommend you have a <a href="https://developer.apple.com/xcode/">Xcode</a> installed and basic knowledge of <a href="https://developer.apple.com/swift/">swift</a>.</p>
<h2 id="tableofcontents">Table of Contents</h2>
<ul>
<li><a href="#podfile">Podfile</a></li>
<li><a href="#sashido">SashiDo</a></li>
<li><a href="#outline">Outline</a></li>
<li><a href="#controllersetup">Controller Setup</a></li>
<li><a href="#sessionmanager">Session Manager</a></li>
<li><a href="#registration">Registration</a>
<ul>
<li><a href="#currentuser">Current User</a></li>
<li><a href="#signup">Signup</a></li>
<li><a href="#login">Login</a></li>
<li><a href="#logout">Logout</a></li>
</ul>
</li>
<li><a href="#part2">Part 2</a></li>
<li><a href="#resources">Resources</a></li>
</ul>
<h2 id="podfile">Podfile</h2>
<p>As mentioned before, I used SashiDo’s backend platform to store user information. Because SashiDo uses Parse open-source technology to communicate with your app, you first must <a href="https://www.youtube.com/watch?v=BSYLs0wl5ME">install the podfile</a>:<br>
     1. <a href="https://developer.apple.com/documentation/xcode/creating_an_xcode_project_for_an_app">Create</a> your Xcode project (I called mine snapShot)<br>
     2. Go to terminal and enter your project’s folder<br>
     3. Type <code>sudo install cocoapods</code><br>
     4. Then type <code>pod init</code><br>
     5. If you type <code>ls</code> you should see the following:<br>
<img src="https://media-blog.sashido.io/content/images/2021/04/terminal1.png" alt="SnapShot: How to Create a Digital Scrapbook in iOS - Part 1"><br>
     6. Type <code>open -a TextEdit Podfile</code> so that you can edit the file<br>
     7. Modify the file to look like this:<br>
<img src="https://media-blog.sashido.io/content/images/2021/04/podfile.png" alt="SnapShot: How to Create a Digital Scrapbook in iOS - Part 1"><br>
     8. Save the file and return to terminal<br>
     9. Type <code>pod update</code><br>
     10. Now if you type ls you should see all these files:<br>
<img src="https://media-blog.sashido.io/content/images/2021/04/terminal2.png" alt="SnapShot: How to Create a Digital Scrapbook in iOS - Part 1"><br>
     11. Type <code>open snapShot.xcworkspace</code> to open in Xcode<br>
<strong>From now on you must open <code>.xcworkspace</code>, NOT <code>.xcodeproj</code></strong></p>
<h2 id="sashido">SashiDo</h2>
<p>Now we will actually set up the connection with SashiDo:<br>
     1. Create an account with <a href="https://dashboard.sashido.io/register">SashiDo</a><br>
     2. Go to your dashboard<br>
     3. Click <code>Create New App</code> and follow the instructions<br>
     4. Once your app has been created, you should see a page saying <code>Connect Applications With SashiDo</code><br>
     5. Making sure swift is selected, copy the 6 lines at the bottom:<br>
         a. If you are not shown this page or accidentally move past it, you can find it again by clicking <code>Getting Started</code> at the top of the menu on the left<br>
<img src="https://media-blog.sashido.io/content/images/2021/04/sashiDo.png" alt="SnapShot: How to Create a Digital Scrapbook in iOS - Part 1"><br>
If you had any trouble with this section, refer to the <a href="https://blog.sashido.io/sashidos-getting-started-guide/">Getting Started Guide</a><br>
     6. Next, go back to Xcode and open <strong><code>AppDelegate.swift</code></strong><br>
     7. Inside <code>func application</code>, paste the code you copied<br>
     8. At the top of this file, add <code>import Parse</code><br>
     9. It should look like this (note: the line about the local datastore is not required):</p>
<p><img src="https://media-blog.sashido.io/content/images/2021/04/application.png" alt="SnapShot: How to Create a Digital Scrapbook in iOS - Part 1"></p>
<p>To communicate with SashiDo, we will use Parse documentation. I will be going over some of the functions and calls I used, but it is also beneficial to refer to the <a href="https://docs.parseplatform.org/ios/guide/">Parse iOS Documentation</a>. Additionally, to gain a more in-depth understanding, I found this <a href="https://www.youtube.com/playlist?list=PLMRqhzcHGw1ZFjFyHGJTTPuvcLbwVCuG4">video playlist</a> on using parse for iOS development very useful.</p>
<h2 id="outline">Outline</h2>
<p>Once you have the connection to SashiDo set up, it’s important to create an outline of your app. This diagram should contain all your controllers and the connections between them. It is also a good practice to color different types of controllers (in my case <code>viewController=black</code>, <code>TableViewController=blue</code>, and <code>CollectionViewController=red</code>).</p>
<p><img src="https://media-blog.sashido.io/content/images/2021/04/outline.png" alt="SnapShot: How to Create a Digital Scrapbook in iOS - Part 1"><br>
This is just the initial outline so your actual controllers might look or act differently.</p>
<h2 id="controllersetup">Controller Setup</h2>
<p>With this outline, you can now begin working on Xcode and adding the controllers:<br>
     1. Inside the project folder, create a folder called <code>viewControllers</code><br>
     2. Go to <strong><code>Main.Storyboard</code></strong><br>
     3. Add the necessary (types of) controllers<br>
     4. For each added controller create a swift file in the <code>viewControllers</code> folder with an appropriate name<br>
     5. In the file, paste and modify this template code:</p>
<pre><code class="language-swift">import Foundation
import UIKit
import MapKit
import Parse
import CoreLocation

class fileName: viewControllerType /*other types might be necessary depending on your usage of this class*/ {
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}
</code></pre>
<p>All these imports are not necessary for every controller, but it can’t hurt to have all of them.<br>
     6. Under the corresponding controller, go to <code>identity inspector</code> and under <code>class</code>, select the swift file<br>
     7. Copy the swift files name and paste it in <code>Storyboard ID</code><br>
<img src="https://media-blog.sashido.io/content/images/2021/04/namingFiles.png" alt="SnapShot: How to Create a Digital Scrapbook in iOS - Part 1"><br>
     8. Add the necessary buttons, labels, images, etc.<br>
         a. If your controller contains text inputs (e.g. <code>login</code>, <code>signup</code>, or <code>newMemory</code>), then I recommend adding a <a href="https://blog.sashido.io/xcode-collection-of-useful-functions-and-tips">scrollView</a> so the user can see all the input boxes<br>
         b. Don’t add anything to the controller for the map (<code>ViewController</code>)<br>
Now you should set up a <a href="https://developer.apple.com/documentation/uikit/uinavigationcontroller">navigationController</a> to make movements between controllers smooth:<br>
     1. Drag a navigationController onto the <strong><code>storyboard</code></strong><br>
     2. Under <code>Attributes Inspector</code>, check <code>Is Initial View Controller</code><br>
     3. Hover over the navigation controller, click <code>control</code> and drag your mouse to your first controller (e.g. <code>login/signup</code> screen)<br>
     4. Release your mouse and select <code>root view controller</code><br>
     5. In <strong><code>settings.swift</code></strong> viewDidLoad, add the following code to keep the controller with navigation:</p>
<pre><code class="language-swift">var objVC: UIViewController? = storyboard!.instantiateViewController(withIdentifier: &quot;settings&quot;)
var aObjNavi = UINavigationController(rootViewController: objVC!)
</code></pre>
<p>From here you can create segues between view controllers (<code>login/signup</code> screen -&gt; <code>login controller</code>) or wait to create these segues through code (<code>login</code> -&gt; <code>settings</code>).<br>
This is my final storyboard:</p>
<p><img src="https://media-blog.sashido.io/content/images/2021/04/storyboard.png" alt="SnapShot: How to Create a Digital Scrapbook in iOS - Part 1"></p>
<h2 id="sessionmanager">Session Manager</h2>
<p>The complexity of this app requires us to store information in a <a href="https://blog.sashido.io/sashidos-getting-started-guide/#manageyourdatabaselikeapro">database</a> (taken care of by SashiDo). However, we still need to request and send information from and to the database. To do this we will create a file called sessionManager (also used in my <a href="https://dev.to/carolinebaillie/fish-classification-ios-app-with-sashido-and-teachable-machine-417i#session-manager">previous tutorial</a>). sessionManager is a class that contains all the query functions that can then be accessed from any other file. The basic setup for this file is as follows:<br>
     1. In snapShot, create a new folder called <code>Classes</code><br>
     2. In the this folder, create a new swift file called <code>sessionManager</code><br>
     3. Add this template code to <strong><code>sessionManager.swift</code></strong>:</p>
<pre><code class="language-swift">import Foundation
import Parse
class sessionManager: NSObject {
    static let shared = sessionManager()
    override init() {
       super.init()
    }
}
</code></pre>
<p>     4. In this class we will:<br>
         a. Create query functions<br>
         b. Store global variables on the user and location<br>
         c. Store a global array with all information from the database so we only need to send one request<br>
This file will be used throughout the app so it is important to have a basic understanding of and to create this file at the beginning.</p>
<h2 id="registration">Registration</h2>
<p>In most apps, you will need the user to create and use an account. SashiDo makes this process super easy!</p>
<h3 id="currentuser">Current User</h3>
<p>In order to show individual information, I created a variable that kept track of the current user:<br>
     1. In the <code>Classes</code> folder, create a new file called <strong><code>currentUser.swift</code></strong><br>
     2. Write this code:</p>
<pre><code class="language-swift">import Foundation
import UIKit
import Parse

class currentUser : NSObject {
    var username:String
    var id:String
    
    init(username:String, id:String) {
        self.username = username
        self.id = id
    }
}
</code></pre>
<p>     3. In <strong><code>sessionManager.swift</code></strong>, add <code>var user : currentUser!</code> at the top<br>
     4. This variable will be used in other functions in <code>sessionManager</code></p>
<h3 id="signup">Signup</h3>
<p>     1. You should have all the necessary text inputs (preferably in a <a href="https://blog.sashido.io/xcode-collection-of-useful-functions-and-tips">scrollView</a>)<br>
     2. <a href="https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/ConnectTheUIToCode.html">Connect</a> these inputs (as an outlet) and the button (as an action) to its file (<strong><code>signup.swift</code></strong>)<br>
     3. Add the function to <a href="https://blog.sashido.io/xcode-collection-of-useful-functions-and-tips">dismiss the keyboard</a><br>
     4. Inside the button action function (<code>signupToggled</code>):<br>
         a. Display <a href="https://blog.sashido.io/snapshot-how-to-create-a-digital-scrapbook-in-ios-part-1/LINK_EXTRAS">waiting spinner</a><br>
         b. Create and save the user (<a href="https://docs.parseplatform.org/ios/guide/#users">Parse</a>)<br>
         c. Go to the next controller (<code>settings</code>)</p>
<pre><code class="language-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!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    //get rid of keyboard when touch screen
    override func touchesBegan(_ touches: Set&lt;UITouch&gt;, with event: UIEvent?) {
        usernameTextField.resignFirstResponder()
        passwordTextField.resignFirstResponder()
        emailTextField.resignFirstResponder()
    }

    @IBAction func signupToggled(_ sender: Any) {
        // loading icon
        self.showSpinner()
        // create new user
        let user = PFUser()
        user.username = usernameTextField.text!
        user.password = passwordTextField.text!
        user.email = emailTextField.text!
        // save user
        user.signUpInBackground { (result, error) in
            if error == nil &amp;&amp; result == true {
                //successfully signed up
                // store current user
                let cUser = currentUser(username: user[&quot;username&quot;] as! String, id: user.objectId!)
                sessionManager.shared.user = cUser
                //next screen
                let secondViewController = self.storyboard?.instantiateViewController(withIdentifier: &quot;settings&quot;) as! settings
                self.navigationController?.pushViewController(secondViewController, animated: true)
            }
        }
    }
}
</code></pre>
<h3 id="login">Login</h3>
<p>     1. Same as sign up code but login rather than signup:</p>
<pre><code class="language-swift">import Foundation
import UIKit
import Parse

class login: UIViewController {
    @IBOutlet weak var usernameTextField: UITextField!
    @IBOutlet weak var passwordTextField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    //get rid of keyboard when touch screen
    override func touchesBegan(_ touches: Set&lt;UITouch&gt;, with event: UIEvent?) {
        usernameTextField.resignFirstResponder()
        passwordTextField.resignFirstResponder()
    }

    @IBAction func loginToggled(_ sender: Any) {
        // loading icon
        self.showSpinner()
        // login user
        PFUser.logInWithUsername(inBackground:usernameTextField.text!, password:passwordTextField.text!) {
          (user, error) -&gt; Void in
          if user != nil {
            // successful
            // store current User
            var cUser = currentUser(username: user?[&quot;username&quot;] as! String, id: user?.objectId as! String)
            sessionManager.shared.user = cUser
            // next controller
            let secondViewController = self.storyboard?.instantiateViewController(withIdentifier: &quot;settings&quot;) as! settings
            self.navigationController?.pushViewController(secondViewController, animated: true)
          } else {
            // The login failed.
            print(&quot;incorrect username or password&quot;)
          }
        }
    }
}
</code></pre>
<h3 id="logout">Logout</h3>
<p>To logout you can override the navigation back function on a chosen controller (<strong><code>settings.swift</code></strong>):<br>
     1. In viewDidLoad:</p>
<pre><code class="language-swift">//change back button name for navigation controller
let newBackButton = UIBarButtonItem(title: &quot;Logout&quot;, style: UIBarButtonItem.Style.plain, target: self, action: #selector(settings.back(sender:)))
self.navigationItem.leftBarButtonItem = newBackButton
</code></pre>
<p>     2. In Class:</p>
<pre><code class="language-swift">@objc func back(sender: UIBarButtonItem) {
    // logout
    PFUser.logOut()
    // go back to first viewController
    self.navigationController?.popToRootViewController(animated: true)
}
</code></pre>
<h2 id="part2">Part 2</h2>
<p>This is only the beginning of a larger project. This mainly included the setup and basics, and in <a href="https://blog.sashido.io/snapshot-how-to-create-a-digital-scrapbook-in-ios-part-2">Part 2</a> we're getting more into the specifics of this project and all the maps functions.</p>
<p>Happy coding!</p>
<h2 id="resources">Resources</h2>
<p><a href="https://blog.sashido.io/xcode-collection-of-useful-functions-and-tips">An Xcode Collection of Useful Functions and Tips</a><br>
<a href="https://blog.sashido.io/snapshot-how-to-create-a-digital-scrapbook-in-ios-part-2">SnapShot: How to Create a Digital Scrapbook in iOS - Part 2</a><br>
<a href="https://youtu.be/wTXN-c1tPHs">Video Demo</a><br>
<a href="https://github.com/CarolineBaillie/snapShot">SnapShot GitHub Repo</a><br>
<a href="https://www.sashido.io/en/">SashiDo</a><br>
<a href="https://docs.parseplatform.org/ios/guide/">Parse Documentation</a><br>
<a href="https://www.youtube.com/playlist?list=PLMRqhzcHGw1ZFjFyHGJTTPuvcLbwVCuG4">Parse Video Playlist</a><br>
<a href="https://www.youtube.com/watch?v=BSYLs0wl5ME">Install Cocoapods</a></p>
</div>]]></content:encoded></item></channel></rss>