<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Neo</title>
    <description>The latest articles on DEV Community by Neo (@neo).</description>
    <link>https://dev.to/neo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3385%2Ff7086976-e0d8-4394-a8bb-1067a787557f.png</url>
      <title>DEV Community: Neo</title>
      <link>https://dev.to/neo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/neo"/>
    <language>en</language>
    <item>
      <title>Using Laravel at Scale | Laravel Worldwide Meetup 2020</title>
      <dc:creator>Neo</dc:creator>
      <pubDate>Wed, 26 Aug 2020 12:57:23 +0000</pubDate>
      <link>https://dev.to/neo/using-laravel-at-scale-laravel-worldwide-meetup-2020-94</link>
      <guid>https://dev.to/neo/using-laravel-at-scale-laravel-worldwide-meetup-2020-94</guid>
      <description>&lt;p&gt;The second episode of the &lt;a href="https://meetup.laravel.com"&gt;Laravel Worldwide Meetup&lt;/a&gt; happened on the 25th of August. There, I gave a talk about some of the lessons I learned while working with an &lt;a href="https://twitter.com/aboutyou_tech"&gt;API that handles over 300 million records daily&lt;/a&gt;. Because of the Coronavirus :/ we had to hold the meetup remotely but it was a success nonetheless.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://freek.dev"&gt;Freek&lt;/a&gt; for hosting it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Video
&lt;/h2&gt;

&lt;p&gt;Here is a video of the talk:&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/4WZHRtgMAWo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Slides
&lt;/h2&gt;

&lt;p&gt;Here are the slides for the talk:&lt;br&gt;
&lt;iframe class="speakerdeck-iframe ltag_speakerdeck" src="https://speakerdeck.com/player/97c47be4dfcb40c8a0c1619107da3e56"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>conference</category>
      <category>speaking</category>
      <category>optimisation</category>
    </item>
    <item>
      <title>Create a cryptocurrency tracking app with push notifications using Swift and Laravel - Part 2: The iOS app</title>
      <dc:creator>Neo</dc:creator>
      <pubDate>Thu, 22 Nov 2018 10:47:12 +0000</pubDate>
      <link>https://dev.to/neo/create-a-cryptocurrency-tracking-app-with-push-notifications-using-swift-and-laravel---part-2-the-ios-app-251a</link>
      <guid>https://dev.to/neo/create-a-cryptocurrency-tracking-app-with-push-notifications-using-swift-and-laravel---part-2-the-ios-app-251a</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;You will need the following installed on your machine: Xcode, the Laravel CLI, SQLite, and Cocoapods. Familiarity with the Xcode IDE will be helpful. You should have completed part one of the series.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/neo/create-a-cryptocurrency-tracking-app-with-push-notifications-using-swift-and-laravel---part-1-the-backend-2mdh"&gt;first part&lt;/a&gt; of this article, we started developing our cryptocurrency alert application. We developed the backend of the application that will power the iOS application. As it stands, our backend application can return settings for a device based on its UUID, save the settings for a device based on its UUID and also it can figure out what devices to send push notifications to when the currencies update.&lt;/p&gt;

&lt;p&gt;In this part, we will focus on creating the iOS application using Swift and Xcode.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To follow along you need the following requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Completed the &lt;a href="http://#" rel="noopener noreferrer"&gt;part one&lt;/a&gt; of this article.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.apple.com/xcode" rel="noopener noreferrer"&gt;Xcode&lt;/a&gt; installed on your machine.&lt;/li&gt;
&lt;li&gt;Knowledge of the Xcode IDE.&lt;/li&gt;
&lt;li&gt;Basic knowledge using the &lt;a href="https://laravel.com/" rel="noopener noreferrer"&gt;Laravel framework&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Basic knowledge of the &lt;a href="http://developer.apple.com/swift" rel="noopener noreferrer"&gt;Swift programming language&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://laravel.com/docs/5.6/installation" rel="noopener noreferrer"&gt;Laravel CLI&lt;/a&gt; installed on your machine.&lt;/li&gt;
&lt;li&gt;SQLite installed on your machine. &lt;a href="http://www.sqlitetutorial.net/download-install-sqlite/" rel="noopener noreferrer"&gt;Installation guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://guides.cocoapods.org/using/getting-started.html" rel="noopener noreferrer"&gt;Cocoapods&lt;/a&gt; installed on your machine.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pusher.com/beams" rel="noopener noreferrer"&gt;Pusher Beams&lt;/a&gt; and &lt;a href="https://pusher.com/channels" rel="noopener noreferrer"&gt;Channels&lt;/a&gt; application.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What we will be building
&lt;/h2&gt;

&lt;p&gt;We already started out by building the backend of the application using Laravel. So next, we will build the iOS application using Swift. If you want to test the push notifications then you will need to run the application on a live device.&lt;/p&gt;

&lt;h3&gt;
  
  
  How the client application will work
&lt;/h3&gt;

&lt;p&gt;For the client app, the iOS application, we will create a simple list that will display the available currencies and the current prices to the dollar. Whenever the price of the cryptocurrency changes, we will trigger an event using Pusher Channels so the prices are updated.&lt;/p&gt;

&lt;p&gt;From the application, you will be able to set a minimum and maximum price change when you want to be alerted. For instance, you can configure the application to send a push notification to the application when the price of one Etherium (ETH) goes below $500. You can also configure the application to receive a notification when the price of Bitcoin goes above $5000.&lt;/p&gt;

&lt;h3&gt;
  
  
  How the application will look
&lt;/h3&gt;

&lt;p&gt;When we are done with the application, here's how the application will look:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/1K08NTSdaoIIqWQ2YAyOqo/4efdb4fa01a2ee599bf5b71561a717a4/ios-cryptocurrency-part-1-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/1K08NTSdaoIIqWQ2YAyOqo/4efdb4fa01a2ee599bf5b71561a717a4/ios-cryptocurrency-part-1-demo.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up your client application
&lt;/h2&gt;

&lt;p&gt;Launch Xcode and click &lt;strong&gt;Create a new Xcode project&lt;/strong&gt;. Select &lt;strong&gt;Single View App&lt;/strong&gt; and click &lt;strong&gt;Next&lt;/strong&gt;. Enter your &lt;strong&gt;Product Name&lt;/strong&gt;, we will call our project &lt;em&gt;cryptoalat&lt;/em&gt;, and select &lt;strong&gt;Swift&lt;/strong&gt; from the &lt;strong&gt;Language&lt;/strong&gt; options. You can also change any other detail you wish to on the screen then click &lt;strong&gt;Next&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing dependencies
&lt;/h3&gt;

&lt;p&gt;Now you have your Xcode project. Close Xcode and open a terminal window. &lt;code&gt;cd&lt;/code&gt; to the iOS project directory in terminal and run the command below to create a Podfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ pod init

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The Podfile is a specification that describes the dependencies of the targets of one or more Xcode projects. The file should simply be named Podfile. All the examples in the guides are based on CocoaPods version 1.0 and onwards. - &lt;a href="https://guides.cocoapods.org/using/the-podfile.html" rel="noopener noreferrer"&gt;Cocoapods Guides&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This will generate a new file called &lt;code&gt;Podfile&lt;/code&gt; in the root of your project. Open this file in any editor and update the file as seen below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // File: Podfile
    platform :ios, '11.0'

    target 'cryptoalat' do
      use_frameworks!

      pod 'Alamofire', '~&amp;gt; 4.7.2'
      pod 'PushNotifications', '~&amp;gt; 0.10.8'
      pod 'PusherSwift', '~&amp;gt; 6.1.0'
      pod 'NotificationBannerSwift', '~&amp;gt; 1.6.3'
    end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If you used a project name other than cryptoalat, then change it in the Podfile to match your project’s target name.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Go to terminal and run the command below to install your dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ pod install

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the installation is complete, you will have a &lt;code&gt;*.xcworkspace&lt;/code&gt; file in the root of your project. Open this file in Xcode and let’s start developing our cryptocurrency alert application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the iOS application
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Creating our storyboard
&lt;/h3&gt;

&lt;p&gt;The first thing we need to do is design our storyboard for the application. This is what we want the storyboard to look like when we are done.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/3CgFeKuZnyMkQ0Kseaw2Gk/9e4a0a12e87fe5685f30c563d9f3ea8d/ios-cryptocurrency-part-2-storyboard.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/3CgFeKuZnyMkQ0Kseaw2Gk/9e4a0a12e87fe5685f30c563d9f3ea8d/ios-cryptocurrency-part-2-storyboard.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;Main.storyboard&lt;/code&gt; file and design as seen above.&lt;/p&gt;

&lt;p&gt;Above we have three scenes. The first scene, which is the entry point, is the launch scene. We then draw a manual segue with an identifier called &lt;strong&gt;Main&lt;/strong&gt;. Then we set the segue &lt;strong&gt;Kind&lt;/strong&gt; to &lt;strong&gt;Present Modally&lt;/strong&gt;. This will present the next scene which is a navigation view controller. Navigation controllers already have an attached root view controller by default.&lt;/p&gt;

&lt;p&gt;We will use this attached view controller, which is a &lt;code&gt;TableViewController&lt;/code&gt;, as the main view for our application. It’ll list the available currencies and show us a text field that allows us to change the setting for that currency when it is tapped.&lt;/p&gt;

&lt;p&gt;On the third scene, we set the reuse identifier of the cells to &lt;strong&gt;coin&lt;/strong&gt; and we drag two labels to the prototype cell. The first label will be for the coin name and the second label will be for the price.&lt;/p&gt;

&lt;p&gt;Now that we have the scenes, let’s create some controllers and view classes and connect them to our storyboard scenes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating your controllers
&lt;/h3&gt;

&lt;p&gt;In Xcode, create a new class &lt;code&gt;LaunchViewController&lt;/code&gt; and paste the contents of the file below into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; UIKit

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;LaunchViewController&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;UIViewController&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;viewDidAppear&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; animated: Bool)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;super&amp;lt;/span&amp;gt;.viewDidAppear(animated)

            &amp;lt;span class="hljs-type"&amp;gt;SettingsService&amp;lt;/span&amp;gt;.shared.loadSettings {
                &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.routeToMainController()
            }
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;fileprivate&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;routeToMainController&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            performSegue(withIdentifier: &amp;lt;span class="hljs-string"&amp;gt;"Main"&amp;lt;/span&amp;gt;, sender: &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;)
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Set the controller as the custom class for the first scene in the &lt;code&gt;Main.storyboard&lt;/code&gt; file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the code, we load the settings using a &lt;code&gt;SettingsService&lt;/code&gt; class we will create later. When the settings are loaded for the device, we then call the &lt;code&gt;routeToMainController&lt;/code&gt; method, which routes the application to the main controller using the &lt;strong&gt;Main&lt;/strong&gt; segue we created earlier.&lt;/p&gt;

&lt;p&gt;The next controller we will be creating will be the &lt;code&gt;CoinsTableViewController&lt;/code&gt;. This will be the controller that will be tied to the third scene which is the main scene.&lt;/p&gt;

&lt;p&gt;Create the &lt;code&gt;CoinsTableViewController&lt;/code&gt; and replace the contents with the following code;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; UIKit
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; PusherSwift
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; NotificationBannerSwift

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;struct&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Coin&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; name: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; rate: &amp;lt;span class="hljs-type"&amp;gt;Float&amp;lt;/span&amp;gt;
    }

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;CoinsTableViewController&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;UITableViewController&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{

        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; coins: [&amp;lt;span class="hljs-type"&amp;gt;Coin&amp;lt;/span&amp;gt;] = []

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;viewDidLoad&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;super&amp;lt;/span&amp;gt;.viewDidLoad()
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;numberOfSections&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt; tableView: UITableView)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-number"&amp;gt;1&amp;lt;/span&amp;gt;
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;tableView&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; tableView: UITableView, numberOfRowsInSection section: Int)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; coins.&amp;lt;span class="hljs-built_in"&amp;gt;count&amp;lt;/span&amp;gt;
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;tableView&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; tableView: UITableView, cellForRowAt indexPath: IndexPath)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;UITableViewCell&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; coin = coins[indexPath.row]
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; cell = tableView.dequeueReusableCell(withIdentifier: &amp;lt;span class="hljs-string"&amp;gt;"coin"&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt;: indexPath) &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;! &amp;lt;span class="hljs-type"&amp;gt;CoinTableViewCell&amp;lt;/span&amp;gt;

            cell.name.text = &amp;lt;span class="hljs-string"&amp;gt;"1 \(coin.name) ="&amp;lt;/span&amp;gt;
            cell.amount.text = &amp;lt;span class="hljs-string"&amp;gt;"$\(String(coin.rate))"&amp;lt;/span&amp;gt;

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; cell
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Set the controller as the custom class for the first scene in the &lt;code&gt;Main.storyboard&lt;/code&gt; file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Above we have defined the &lt;code&gt;Coin&lt;/code&gt; struct and it has a &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;rate&lt;/code&gt; property. We have the controller which we define the &lt;code&gt;coins&lt;/code&gt; property as an array of &lt;code&gt;Coin&lt;/code&gt;s. We then have some boilerplate code that comes with creating a table view controller.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;numberOfSections&lt;/code&gt; method specifies how many sections the table will have. In the first &lt;code&gt;tableView&lt;/code&gt; method, we return the number of &lt;code&gt;coins&lt;/code&gt; available and in the second &lt;code&gt;tableView&lt;/code&gt; method, we define how we want each row to be handled.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating other supporting classes
&lt;/h3&gt;

&lt;p&gt;If you noticed in the code above, we referenced a &lt;code&gt;CoinTableViewCell&lt;/code&gt; as the class for each row in the last &lt;code&gt;tableView&lt;/code&gt; method. Let’s create that.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;CoinTableViewCell&lt;/code&gt; class and paste the following code into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;CoinTableViewCell&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;UITableViewCell&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-meta"&amp;gt;@IBOutlet&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;weak&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; name: &amp;lt;span class="hljs-type"&amp;gt;UILabel&amp;lt;/span&amp;gt;!    
        &amp;lt;span class="hljs-meta"&amp;gt;@IBOutlet&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;weak&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; amount: &amp;lt;span class="hljs-type"&amp;gt;UILabel&amp;lt;/span&amp;gt;!
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the &lt;code&gt;Main.storyboard&lt;/code&gt; file and set the class as the custom class for the prototype cell in the third scene of the &lt;code&gt;Main.storyboard&lt;/code&gt; file. When you have set the class, connect the &lt;code&gt;@IBOutlet&lt;/code&gt;s as specified in the cell class above.&lt;/p&gt;

&lt;p&gt;The next class we need to create is the &lt;code&gt;SettingsService&lt;/code&gt;. This class will be responsible for updating and fetching the settings for the device.&lt;/p&gt;

&lt;p&gt;Create a new &lt;code&gt;SettingsService&lt;/code&gt; class and replace the contents with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; Foundation
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; Alamofire
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; NotificationBannerSwift

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;SettingsService&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;static&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; key = &amp;lt;span class="hljs-string"&amp;gt;"CryptoAlat"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;static&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; shared = &amp;lt;span class="hljs-type"&amp;gt;SettingsService&amp;lt;/span&amp;gt;()

        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; settings: &amp;lt;span class="hljs-type"&amp;gt;Settings&amp;lt;/span&amp;gt;? {
            &amp;lt;span class="hljs-keyword"&amp;gt;get&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.getCachedSettings()
            }
            &amp;lt;span class="hljs-keyword"&amp;gt;set&amp;lt;/span&amp;gt;(settings) {
                &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; settings = settings {
                    &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.updateCachedSettings(settings)
                }
            }
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;init&amp;lt;/span&amp;gt;() {}

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;loadSettings&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(completion: @escaping&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Void&amp;lt;/span&amp;gt;) {
            fetchRemoteSettings { settings &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; settings = settings &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
                    &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.saveSettings(&amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.defaultSettings()) { &amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
                        completion()
                    }
                }

                &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.updateCachedSettings(settings)
                completion()
            }
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;fileprivate&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;defaultSettings&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Settings&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Settings&amp;lt;/span&amp;gt;(
                btc_min_notify: &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, 
                btc_max_notify: &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, 
                eth_min_notify: &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, 
                eth_max_notify: &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;
            )
        }

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;saveSettings&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; settings: Settings, completion: @escaping&amp;lt;span class="hljs-params"&amp;gt;(Bool)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Void&amp;lt;/span&amp;gt;) {
            updateRemoteSettings(settings, completion: { saved &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; saved {
                    &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.updateCachedSettings(settings)
                }

                completion(saved)
            })
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;fileprivate&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;fetchRemoteSettings&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(completion: @escaping &amp;lt;span class="hljs-params"&amp;gt;(Settings?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Void&amp;lt;/span&amp;gt;) {
            &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; deviceID = &amp;lt;span class="hljs-type"&amp;gt;AppConstants&amp;lt;/span&amp;gt;.deviceIDFormatted &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; completion(&amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; url = &amp;lt;span class="hljs-string"&amp;gt;"\(AppConstants.API_URL)?u=\(deviceID)"&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-type"&amp;gt;Alamofire&amp;lt;/span&amp;gt;.request(url).validate().responseJSON { resp &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; data = resp.data, resp.result.isSuccess {
                    &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; decoder = &amp;lt;span class="hljs-type"&amp;gt;JSONDecoder&amp;lt;/span&amp;gt;()
                    &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; settings = &amp;lt;span class="hljs-keyword"&amp;gt;try&amp;lt;/span&amp;gt;? decoder.decode(&amp;lt;span class="hljs-type"&amp;gt;Settings&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;, from: data) {
                        &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; completion(settings)
                    }
                }

                completion(&amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)
            }
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;fileprivate&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;updateRemoteSettings&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; settings: Settings, completion: @escaping&amp;lt;span class="hljs-params"&amp;gt;(Bool)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Void&amp;lt;/span&amp;gt;) {
            &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; deviceID = &amp;lt;span class="hljs-type"&amp;gt;AppConstants&amp;lt;/span&amp;gt;.deviceIDFormatted &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; completion(&amp;lt;span class="hljs-literal"&amp;gt;false&amp;lt;/span&amp;gt;)
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; params = settings.toParams()
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; url = &amp;lt;span class="hljs-string"&amp;gt;"\(AppConstants.API_URL)?u=\(deviceID)"&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-type"&amp;gt;Alamofire&amp;lt;/span&amp;gt;.request(url, method: .post, parameters: params).validate().responseJSON { resp &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; resp.result.isSuccess, &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; res = resp.result.value &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;? [&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;] &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
                    &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;StatusBarNotificationBanner&amp;lt;/span&amp;gt;(title: &amp;lt;span class="hljs-string"&amp;gt;"Failed to update settings."&amp;lt;/span&amp;gt;, style: .danger).show()
                }

                completion((res[&amp;lt;span class="hljs-string"&amp;gt;"status"&amp;lt;/span&amp;gt;] == &amp;lt;span class="hljs-string"&amp;gt;"success"&amp;lt;/span&amp;gt;))
            }
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;fileprivate&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;updateCachedSettings&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; settings: Settings)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; encodedSettings = &amp;lt;span class="hljs-keyword"&amp;gt;try&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;JSONEncoder&amp;lt;/span&amp;gt;().encode(settings) {
                &amp;lt;span class="hljs-type"&amp;gt;UserDefaults&amp;lt;/span&amp;gt;.standard.&amp;lt;span class="hljs-keyword"&amp;gt;set&amp;lt;/span&amp;gt;(encodedSettings, forKey: &amp;lt;span class="hljs-type"&amp;gt;SettingsService&amp;lt;/span&amp;gt;.key)
            }
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;fileprivate&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;getCachedSettings&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Settings&amp;lt;/span&amp;gt;? {
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; defaults = &amp;lt;span class="hljs-type"&amp;gt;UserDefaults&amp;lt;/span&amp;gt;.standard
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; data = defaults.object(forKey: &amp;lt;span class="hljs-type"&amp;gt;SettingsService&amp;lt;/span&amp;gt;.key) &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;Data&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; decoder = &amp;lt;span class="hljs-type"&amp;gt;JSONDecoder&amp;lt;/span&amp;gt;()
                &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; decodedSettings = &amp;lt;span class="hljs-keyword"&amp;gt;try&amp;lt;/span&amp;gt;? decoder.decode(&amp;lt;span class="hljs-type"&amp;gt;Settings&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;, from: data) {
                    &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; decodedSettings
                }
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above we have the &lt;code&gt;SettingsService&lt;/code&gt;. The first method &lt;code&gt;loadSettings&lt;/code&gt; loads the settings from the API and then saves it locally. If there is no setting remotely, it calls the &lt;code&gt;defaultSettings&lt;/code&gt; method and saves the response to the API.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;saveSettings&lt;/code&gt; method saves the &lt;code&gt;Settings&lt;/code&gt; remotely using &lt;code&gt;updateRemoteSettings&lt;/code&gt; and then locally using &lt;code&gt;updateCachedSettings&lt;/code&gt;. The &lt;code&gt;fetchRemoteSettings&lt;/code&gt; gets the settings from the API and decodes the response using the &lt;a href="https://blog.pusher.com/swift-4-decoding-json-codable/" rel="noopener noreferrer"&gt;Swift decodable API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, let’s define the &lt;code&gt;Settings&lt;/code&gt; struct and have it extend &lt;code&gt;Codable&lt;/code&gt;. In the same file for the &lt;code&gt;SettingsService&lt;/code&gt;, add this above the &lt;code&gt;SettingsService&lt;/code&gt; class definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;struct&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Settings&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;Codable&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; btc_min_notify: &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt;?
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; btc_max_notify: &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt;?
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; eth_min_notify: &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt;?
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; eth_max_notify: &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt;?

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;toParams&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Parameters&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; params: &amp;lt;span class="hljs-type"&amp;gt;Parameters&amp;lt;/span&amp;gt; = [:]

            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; btcMin = btc_min_notify { params[&amp;lt;span class="hljs-string"&amp;gt;"btc_min_notify"&amp;lt;/span&amp;gt;] = btcMin }
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; btcMax = btc_max_notify { params[&amp;lt;span class="hljs-string"&amp;gt;"btc_max_notify"&amp;lt;/span&amp;gt;] = btcMax }
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; ethMin = eth_min_notify { params[&amp;lt;span class="hljs-string"&amp;gt;"eth_min_notify"&amp;lt;/span&amp;gt;] = ethMin }
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; ethMax = eth_max_notify { params[&amp;lt;span class="hljs-string"&amp;gt;"eth_max_notify"&amp;lt;/span&amp;gt;] = ethMax }

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; params
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above we have a simple &lt;code&gt;Settings&lt;/code&gt; struct that conforms to &lt;code&gt;Codable&lt;/code&gt;. We also have a &lt;code&gt;toParams&lt;/code&gt; method that converts the properties to a &lt;code&gt;Parameters&lt;/code&gt; type so we can use it with &lt;a href="https://github.com/Alamofire/Alamofire" rel="noopener noreferrer"&gt;Alamofire&lt;/a&gt; when making requests.&lt;/p&gt;

&lt;p&gt;One last class we need to create is &lt;code&gt;AppConstants&lt;/code&gt;. We will use this class to keep all the data that we expect to remain constant and unchanged throughout the lifetime of the application.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;AppConstants&lt;/code&gt; file and paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; UIKit

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;struct&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;AppConstants&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;static&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;API_URL&amp;lt;/span&amp;gt; = &amp;lt;span class="hljs-string"&amp;gt;"http://127.0.0.1:8000/api/settings"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;static&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; deviceID = &amp;lt;span class="hljs-type"&amp;gt;UIDevice&amp;lt;/span&amp;gt;.current.identifierForVendor?.uuidString
        &amp;lt;span class="hljs-keyword"&amp;gt;static&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; deviceIDFormatted = &amp;lt;span class="hljs-type"&amp;gt;AppConstants&amp;lt;/span&amp;gt;.deviceID?.replacingOccurrences(of: &amp;lt;span class="hljs-string"&amp;gt;"-"&amp;lt;/span&amp;gt;, with: &amp;lt;span class="hljs-string"&amp;gt;"_"&amp;lt;/span&amp;gt;).lowercased()
        &amp;lt;span class="hljs-keyword"&amp;gt;static&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;PUSHER_INSTANCE_ID&amp;lt;/span&amp;gt; = &amp;lt;span class="hljs-string"&amp;gt;"PUSHER_BEAMS_INSTANCE_ID"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;static&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;PUSHER_APP_KEY&amp;lt;/span&amp;gt; = &amp;lt;span class="hljs-string"&amp;gt;"PUSHER_APP_KEY"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;static&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;PUSHER_APP_CLUSTER&amp;lt;/span&amp;gt; = &amp;lt;span class="hljs-string"&amp;gt;"PUSHER_APP_CLUSTER"&amp;lt;/span&amp;gt;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Replace the &lt;code&gt;PUSHER_*&lt;/code&gt; keys with the values gotten from the Pusher Channels and Beams dashboard.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Updating the settings for the device
&lt;/h3&gt;

&lt;p&gt;Now that we have defined the settings service, let’s update our controller so the user can set the minimum and maximum prices for each currency.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;CoinsTableViewController&lt;/code&gt; class and add the following method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;tableView&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; tableView: UITableView, didSelectRowAt indexPath: IndexPath)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; coin = coins[indexPath.row]

        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; minTextField: &amp;lt;span class="hljs-type"&amp;gt;UITextField&amp;lt;/span&amp;gt;?
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; maxTextField: &amp;lt;span class="hljs-type"&amp;gt;UITextField&amp;lt;/span&amp;gt;?

        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; title = &amp;lt;span class="hljs-string"&amp;gt;"Manage \(coin.name) alerts"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; message = &amp;lt;span class="hljs-string"&amp;gt;"Notification will be sent to you when price exceeds or goes below minimum and maximum price. Set to zero to turn off notification."&amp;lt;/span&amp;gt;

        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; alert = &amp;lt;span class="hljs-type"&amp;gt;UIAlertController&amp;lt;/span&amp;gt;(title: title, message: message, preferredStyle: .alert)

        alert.addTextField { textfield &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
            minTextField = textfield
            textfield.placeholder = &amp;lt;span class="hljs-string"&amp;gt;"Alert when price is below"&amp;lt;/span&amp;gt;
        }

        alert.addTextField { textfield &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
            maxTextField = textfield
            textfield.placeholder = &amp;lt;span class="hljs-string"&amp;gt;"Alert when price is above"&amp;lt;/span&amp;gt;
        }

        alert.addAction(&amp;lt;span class="hljs-type"&amp;gt;UIAlertAction&amp;lt;/span&amp;gt;(title: &amp;lt;span class="hljs-string"&amp;gt;"Cancel"&amp;lt;/span&amp;gt;, style: .cancel, handler: &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;))

        alert.addAction(&amp;lt;span class="hljs-type"&amp;gt;UIAlertAction&amp;lt;/span&amp;gt;(title: &amp;lt;span class="hljs-string"&amp;gt;"Save"&amp;lt;/span&amp;gt;, style: .&amp;lt;span class="hljs-keyword"&amp;gt;default&amp;lt;/span&amp;gt;, handler: { action &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; minPrice = minTextField?.text, &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; maxPrice = maxTextField?.text &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;StatusBarNotificationBanner&amp;lt;/span&amp;gt;(title: &amp;lt;span class="hljs-string"&amp;gt;"Invalid min or max price"&amp;lt;/span&amp;gt;, style: .danger).show()
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; btcMin: &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt;?, btcMax: &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt;?, ethMin: &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt;?, ethMax: &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt;?

            &amp;lt;span class="hljs-keyword"&amp;gt;switch&amp;lt;/span&amp;gt; coin.name {
            &amp;lt;span class="hljs-keyword"&amp;gt;case&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;"BTC"&amp;lt;/span&amp;gt;:
                btcMin = &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt;(minPrice)
                btcMax = &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt;(maxPrice)
            &amp;lt;span class="hljs-keyword"&amp;gt;case&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;"ETH"&amp;lt;/span&amp;gt;:
                ethMin = &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt;(minPrice)
                ethMax = &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt;(maxPrice)
            &amp;lt;span class="hljs-keyword"&amp;gt;default&amp;lt;/span&amp;gt;:
                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt;
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; settings = &amp;lt;span class="hljs-type"&amp;gt;Settings&amp;lt;/span&amp;gt;(
                btc_min_notify: btcMin,
                btc_max_notify: btcMax,
                eth_min_notify: ethMin,
                eth_max_notify: ethMax
            )

            &amp;lt;span class="hljs-type"&amp;gt;SettingsService&amp;lt;/span&amp;gt;.shared.saveSettings(settings, completion: { saved &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; saved {
                    &amp;lt;span class="hljs-type"&amp;gt;StatusBarNotificationBanner&amp;lt;/span&amp;gt;(title: &amp;lt;span class="hljs-string"&amp;gt;"Saved successfully"&amp;lt;/span&amp;gt;).show()
                }
            })
        }))

        present(alert, animated: &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;, completion: &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The method above is automatically called when a row is selected. In this method, we display a &lt;code&gt;UIAlertController&lt;/code&gt; with two text fields for the minimum price and the maximum price. When the prices are submitted, the &lt;code&gt;SettingsService&lt;/code&gt; we created earlier takes care of updating the values both locally and remotely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding realtime cryptocurrency update support
&lt;/h3&gt;

&lt;p&gt;Open the &lt;code&gt;CoinsTableViewController&lt;/code&gt; and add the &lt;code&gt;pusher&lt;/code&gt; property to the class as seen below:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; pusher: &amp;lt;span class="hljs-type"&amp;gt;Pusher&amp;lt;/span&amp;gt;!&lt;/code&gt;Then replace the &lt;code&gt;viewDidLoad&lt;/code&gt; method with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;viewDidLoad&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
        &amp;lt;span class="hljs-keyword"&amp;gt;super&amp;lt;/span&amp;gt;.viewDidLoad()

        pusher = &amp;lt;span class="hljs-type"&amp;gt;Pusher&amp;lt;/span&amp;gt;(
            key: &amp;lt;span class="hljs-type"&amp;gt;AppConstants&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-type"&amp;gt;PUSHER_APP_KEY&amp;lt;/span&amp;gt;, 
            options: &amp;lt;span class="hljs-type"&amp;gt;PusherClientOptions&amp;lt;/span&amp;gt;(host: .cluster(&amp;lt;span class="hljs-type"&amp;gt;AppConstants&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-type"&amp;gt;PUSHER_APP_CLUSTER&amp;lt;/span&amp;gt;))
        )

        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; channel = pusher.subscribe(&amp;lt;span class="hljs-string"&amp;gt;"currency-update"&amp;lt;/span&amp;gt;)

        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; = channel.bind(eventName: &amp;lt;span class="hljs-string"&amp;gt;"currency.updated"&amp;lt;/span&amp;gt;) { data &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; data = data &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;? [&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;: [&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;: [&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-type"&amp;gt;Float&amp;lt;/span&amp;gt;]]] {
                &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; payload = data[&amp;lt;span class="hljs-string"&amp;gt;"payload"&amp;lt;/span&amp;gt;] &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; { &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; }

                &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.coins = []

                &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt; (coin, deets) &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt; payload {
                    &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; currentPrice = deets[&amp;lt;span class="hljs-string"&amp;gt;"current"&amp;lt;/span&amp;gt;] &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; { &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; }
                    &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.coins.append(&amp;lt;span class="hljs-type"&amp;gt;Coin&amp;lt;/span&amp;gt;(name: coin, rate: currentPrice))
                }

                &amp;lt;span class="hljs-type"&amp;gt;Dispatch&amp;lt;/span&amp;gt;.main.async {
                    &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.tableView.reloadData()
                }
            }
        }

        pusher.connect()
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code above, we are using the &lt;a href="https://pusher.com/docs/ios_quick_start" rel="noopener noreferrer"&gt;Pusher Swift SDK&lt;/a&gt; to subscribe to our &lt;code&gt;currency-update&lt;/code&gt; Pusher Channel. We then subscribe to the &lt;code&gt;currency.updated&lt;/code&gt; event on that channel. Whenever that event is triggered, we refresh the price of the cryptocurrency in realtime.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding push notifications to our iOS new application
&lt;/h3&gt;

&lt;p&gt;To add push notification support, open the &lt;code&gt;AppDelegate&lt;/code&gt; class and replace the contents with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; UIKit
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; PushNotifications

    &amp;lt;span class="hljs-meta"&amp;gt;@UIApplicationMain&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;AppDelegate&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;UIResponder&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-title"&amp;gt;UIApplicationDelegate&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{

        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; window: &amp;lt;span class="hljs-type"&amp;gt;UIWindow&amp;lt;/span&amp;gt;?

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;application&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: &amp;lt;span class="hljs-keyword"&amp;gt;Any&amp;lt;/span&amp;gt;]?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Bool&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-type"&amp;gt;PushNotifications&amp;lt;/span&amp;gt;.shared.start(instanceId: &amp;lt;span class="hljs-type"&amp;gt;AppConstants&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-type"&amp;gt;PUSHER_INSTANCE_ID&amp;lt;/span&amp;gt;)
            &amp;lt;span class="hljs-type"&amp;gt;PushNotifications&amp;lt;/span&amp;gt;.shared.registerForRemoteNotifications()
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;
        }

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;application&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-type"&amp;gt;PushNotifications&amp;lt;/span&amp;gt;.shared.registerDeviceToken(deviceToken) {
                &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; deviceID = &amp;lt;span class="hljs-type"&amp;gt;AppConstants&amp;lt;/span&amp;gt;.deviceIDFormatted {
                    &amp;lt;span class="hljs-keyword"&amp;gt;try&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;PushNotifications&amp;lt;/span&amp;gt;.shared.subscribe(interest: &amp;lt;span class="hljs-string"&amp;gt;"\(deviceID)_eth_changed"&amp;lt;/span&amp;gt;)
                    &amp;lt;span class="hljs-keyword"&amp;gt;try&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;PushNotifications&amp;lt;/span&amp;gt;.shared.subscribe(interest: &amp;lt;span class="hljs-string"&amp;gt;"\(deviceID)_btc_changed"&amp;lt;/span&amp;gt;)
                }
            }
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the class above, we use the &lt;a href="https://docs.pusher.com/push-notifications/reference/ios" rel="noopener noreferrer"&gt;Pusher Beams Swift SDK&lt;/a&gt; to register the device for push notifications. We then subscribe to the &lt;code&gt;*_eth_changed&lt;/code&gt; and &lt;code&gt;*_btc_changed&lt;/code&gt; interests, where &lt;code&gt;*&lt;/code&gt; is the device’s unique UUID.&lt;/p&gt;

&lt;p&gt;Now that we have completed the logic for the application, let’s enable push notifications on the application in Xcode.&lt;/p&gt;

&lt;p&gt;In the project navigator, select your project, and click on the &lt;strong&gt;Capabilities&lt;/strong&gt; tab. &lt;a href="http://help.apple.com/xcode/mac/current/#/devdfd3d04a1" rel="noopener noreferrer"&gt;Enable Push Notifications&lt;/a&gt; by turning the switch ON.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/7wLtGQ4Y00EUWiKgw6oQoE/8eb22b60c3271e6d0e47646c6423a7ad/ios-cryptocurrency-part-2-enable-push.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/7wLtGQ4Y00EUWiKgw6oQoE/8eb22b60c3271e6d0e47646c6423a7ad/ios-cryptocurrency-part-2-enable-push.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will create an entitlements file in the root of your project. With that, you have provisioned your application to fully receive push notifications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Allowing our application to connect locally
&lt;/h3&gt;

&lt;p&gt;If you are going to be testing the app’s backend using a local server, then there is one last thing we need to do. Open the &lt;code&gt;info.plist&lt;/code&gt; file and add an entry to the &lt;code&gt;plist&lt;/code&gt; file to allow connection to our local server:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/5SXadC36VOcMs22Ooayw44/a7272c2b7126b3195878e1bcfad3e57b/ios-cryptocurrency-part-2-local-connection.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/5SXadC36VOcMs22Ooayw44/a7272c2b7126b3195878e1bcfad3e57b/ios-cryptocurrency-part-2-local-connection.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s all. We can run our application. However, &lt;strong&gt;remember that to demo the push notifications, you will need an actual iOS device as simulators cannot receive push notifications.&lt;/strong&gt; If you are using a physical device, you’ll need to expose your local API using &lt;a href="https://ngrok.com" rel="noopener noreferrer"&gt;Ngrok&lt;/a&gt; and then change the &lt;code&gt;API_URL&lt;/code&gt; &lt;strong&gt;In&lt;/strong&gt;&lt;code&gt;AppConstants&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Anytime you want to update the currency prices, run the command below manually in your Laravel application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ php artisan schedule:run

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a screen recording of the application in action:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/1K08NTSdaoIIqWQ2YAyOqo/4efdb4fa01a2ee599bf5b71561a717a4/ios-cryptocurrency-part-1-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/1K08NTSdaoIIqWQ2YAyOqo/4efdb4fa01a2ee599bf5b71561a717a4/ios-cryptocurrency-part-1-demo.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we have been able to see how easy it is to create a cryptocurrency alert website using Laravel, Swift, Pusher Channels and Pusher Beams. The source code to the application built in this article is available on &lt;a href="https://github.com/neoighodaro/cryptocurrency-alert-ios-app" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post first appeared on the &lt;a href="https://pusher.com/tutorials/cryptocurrency-tracking-swift-laravel-part-2" rel="noopener noreferrer"&gt;Pusher blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>pusher</category>
      <category>realtime</category>
      <category>ios</category>
      <category>swift</category>
    </item>
    <item>
      <title>Create a cryptocurrency tracking app with push notifications using Swift and Laravel - Part 1: The backend</title>
      <dc:creator>Neo</dc:creator>
      <pubDate>Thu, 22 Nov 2018 10:44:59 +0000</pubDate>
      <link>https://dev.to/neo/create-a-cryptocurrency-tracking-app-with-push-notifications-using-swift-and-laravel---part-1-the-backend-2mdh</link>
      <guid>https://dev.to/neo/create-a-cryptocurrency-tracking-app-with-push-notifications-using-swift-and-laravel---part-1-the-backend-2mdh</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;You will need the following installed on your machine: Xcode, the Laravel CLI, SQLite, and Cocoapods. Familiarity with the Xcode IDE will be helpful.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Cryptocurrency has been and is still one of the biggest trends this year. With currencies like Bitcoin reaching record highs and new companies creating tokens and offerings, it’s showing just how much potential cryptocurrencies have. However, cryptocurrency prices are erratic and can fall or climb at a moments notice, so it’s always a good idea to keep tabs on the changes.&lt;/p&gt;

&lt;p&gt;In this article, we will be building an application that keeps tabs on changes to the crypto market. The application will focus on BTC and ETH and will allow users of the application to set minimum and maximum amounts when they would like to be notified about the coins current price. The application will be built using Swift, Laravel, Pusher Channels, and Pusher Beams.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To follow along you need the following requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developer.apple.com/xcode" rel="noopener noreferrer"&gt;Xcode&lt;/a&gt; installed on your machine.&lt;/li&gt;
&lt;li&gt;Knowledge of the Xcode IDE.&lt;/li&gt;
&lt;li&gt;Basic knowledge using the &lt;a href="https://laravel.com/" rel="noopener noreferrer"&gt;Laravel framework&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Basic knowledge of the &lt;a href="http://developer.apple.com/swift" rel="noopener noreferrer"&gt;Swift programming language&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://laravel.com/docs/5.6/installation" rel="noopener noreferrer"&gt;Laravel CLI&lt;/a&gt; installed on your machine.&lt;/li&gt;
&lt;li&gt;SQLite installed on your machine. &lt;a href="https://www.tutorialspoint.com/sqlite/sqlite_installation.htm" rel="noopener noreferrer"&gt;Installation guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://guides.cocoapods.org/using/getting-started.html" rel="noopener noreferrer"&gt;Cocoapods&lt;/a&gt; installed on your machine.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pusher.com/beams" rel="noopener noreferrer"&gt;Pusher Beams&lt;/a&gt; and &lt;a href="https://pusher.com/channels" rel="noopener noreferrer"&gt;Channels&lt;/a&gt; application.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What we will be building
&lt;/h2&gt;

&lt;p&gt;We will start out by building the backend of the application using Laravel. Then we will build the iOS application using Swift. If you want to test the push notifications then you will need to run the application on a live device.&lt;/p&gt;

&lt;h3&gt;
  
  
  How the client application will work
&lt;/h3&gt;

&lt;p&gt;For the client app, the iOS application, we will create a simple list that will display the available currencies and the current prices to the dollar. Whenever the price of the cryptocurrency changes, we will trigger an event using Pusher Channels so the prices are updated.&lt;/p&gt;

&lt;p&gt;From the application, you will be able to set a minimum and maximum price change when you want to be alerted. For instance, you can configure the application to send a push notification to the application when the price of one Etherium (ETH) goes below $500. You can also configure the application to receive a notification when the price of Bitcoin goes above $5000.&lt;/p&gt;

&lt;h3&gt;
  
  
  How the backend application will work
&lt;/h3&gt;

&lt;p&gt;For the backend application, we will be using Laravel and we will create endpoints that allow a user update the settings and load the settings for a device. The API will be responsible for checking the current prices of the cryptocurrency and sending both a Channels update and a Beams notification when the price changes.&lt;/p&gt;

&lt;p&gt;However, because the prices don’t change very predictably, we will be simulating the currency changes so we can preview the application in action. We will also be using &lt;a href="https://laravel.com/docs/5.6/scheduling" rel="noopener noreferrer"&gt;task scheduling&lt;/a&gt; in Laravel to trigger the checks for the current currency prices.&lt;/p&gt;

&lt;p&gt;In a production environment we will set the scheduler as a cronjob, but because we are in development, we will manually run the command to trigger price changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  How the application will look
&lt;/h3&gt;

&lt;p&gt;When we are done with the application, here's how the application will look:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/1K08NTSdaoIIqWQ2YAyOqo/4efdb4fa01a2ee599bf5b71561a717a4/ios-cryptocurrency-part-1-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/1K08NTSdaoIIqWQ2YAyOqo/4efdb4fa01a2ee599bf5b71561a717a4/ios-cryptocurrency-part-1-demo.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Pusher Beams and Channels
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setting up Pusher Channels
&lt;/h3&gt;

&lt;p&gt;Log in to your &lt;a href="https://dashboard.pusher.com" rel="noopener noreferrer"&gt;Pusher dashboard&lt;/a&gt;. If you don’t have an account, create one. Your dashboard should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/47FA7jeU0w62siO8G6iCQi/aeda1c00644c75e4d327dbe6725694d7/ios-cryptocurrency-part-1-keys.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/47FA7jeU0w62siO8G6iCQi/aeda1c00644c75e4d327dbe6725694d7/ios-cryptocurrency-part-1-keys.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create a new Channels app. You can easily do this by clicking the big &lt;strong&gt;Create new Channels app&lt;/strong&gt; card at the bottom right. When you create a new app, you are provided with keys. Keep them safe as you will soon need them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up Pusher Beams
&lt;/h3&gt;

&lt;p&gt;Next, log in to the new &lt;a href="https://dash.pusher.com/" rel="noopener noreferrer"&gt;Pusher dashboard&lt;/a&gt;, in here we will create a Pusher Beams instance. You should sign up if you don’t have an account yet. Click on the &lt;strong&gt;Beams&lt;/strong&gt; button on the sidebar then click &lt;strong&gt;Create&lt;/strong&gt;, this will launch a pop up to &lt;strong&gt;Create a new Beams instance&lt;/strong&gt;. Name it &lt;code&gt;cryptoalat&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/4xKD0goX606gGqUSQW2EcC/3dcd7757df87351ddc54cb62397ce2e7/ios-cryptocurrency-part-1-new-beams.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/4xKD0goX606gGqUSQW2EcC/3dcd7757df87351ddc54cb62397ce2e7/ios-cryptocurrency-part-1-new-beams.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As soon as you create the instance, you will be presented with a quickstart guide. Select the &lt;strong&gt;IOS&lt;/strong&gt; quickstart and follow through the wizard.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/27fmUEHf64Qc0KcIQo0KAw/265bde6f67ea031f5d5495a10273e678/ios-cryptocurrency-part-1-beams-quickstart.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/27fmUEHf64Qc0KcIQo0KAw/265bde6f67ea031f5d5495a10273e678/ios-cryptocurrency-part-1-beams-quickstart.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you are done creating the Beams application, you will be provided with an instance ID and a secret key, we will need these later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up your backend application
&lt;/h2&gt;

&lt;p&gt;In your terminal, run the command below to create a new Laravel project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ laravel new cryptoapi

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will create a new Laravel project and install all the required Laravel dependencies.&lt;/p&gt;

&lt;p&gt;Next, let’s install some of the project specific dependencies. Open the &lt;code&gt;composer.json&lt;/code&gt; file and in the &lt;code&gt;require&lt;/code&gt; property, add the following dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // File: composer.json
    "require": {
        [...]

        "neo/pusher-beams": "^1.0",
        "pusher/pusher-php-server": "~3.0"
    },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run the command below to install these dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ composer update

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the installation is complete, open the project in a text editor of your choice. &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt; is pretty nice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up our Pusher Beams library
&lt;/h3&gt;

&lt;p&gt;The first thing we want to do is set up the &lt;a href="https://github.com/neoighodaro/pusher-beams" rel="noopener noreferrer"&gt;Pusher Beams library&lt;/a&gt; we just pulled in using composer. To set up, open the &lt;code&gt;.env&lt;/code&gt; file and add the following keys:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    PUSHER_BEAMS_SECRET_KEY="PUSHER_BEAMS_SECRET_KEY"
    PUSHER_BEAMS_INSTANCE_ID="PUSHER_BEAMS_INSTANCE_ID"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should replace the &lt;code&gt;PUSHER_BEAMS_*&lt;/code&gt; placeholders with the keys you got when setting up your Beams application.&lt;/p&gt;

&lt;p&gt;Next, open the &lt;code&gt;config/broadcasting.php&lt;/code&gt; file and scroll until you see the &lt;code&gt;connections&lt;/code&gt; key. In there, you’ll have the &lt;code&gt;pusher&lt;/code&gt; settings, add the following to the &lt;code&gt;pusher&lt;/code&gt; configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-string"&amp;gt;'pusher'&amp;lt;/span&amp;gt; =&amp;gt; [
        &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;

        &amp;lt;span class="hljs-string"&amp;gt;'beams'&amp;lt;/span&amp;gt; =&amp;gt; [
            &amp;lt;span class="hljs-string"&amp;gt;'secret_key'&amp;lt;/span&amp;gt; =&amp;gt; env(&amp;lt;span class="hljs-string"&amp;gt;'PUSHER_BEAMS_SECRET_KEY'&amp;lt;/span&amp;gt;),
            &amp;lt;span class="hljs-string"&amp;gt;'instance_id'&amp;lt;/span&amp;gt; =&amp;gt; env(&amp;lt;span class="hljs-string"&amp;gt;'PUSHER_BEAMS_INSTANCE_ID'&amp;lt;/span&amp;gt;),
        ],
    ],
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting up our Pusher Channels library
&lt;/h3&gt;

&lt;p&gt;The next step is to set up Pusher Channels. Laravel comes with native support for Pusher Channels so we do not need to do much to set it up.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;.env&lt;/code&gt; file and update the following keys below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    BROADCAST_DRIVER=pusher

    // [...]

    PUSHER_APP_ID="PUSHER_APP_ID"
    PUSHER_APP_KEY="PUSHER_APP_KEY"
    PUSHER_APP_SECRET="PUSHER_APP_SECRET"
    PUSHER_APP_CLUSTER="PUSHER_APP_CLUSTER"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above you set the &lt;code&gt;BROADCAST_DRIVER&lt;/code&gt; to &lt;code&gt;pusher&lt;/code&gt; and then for the other &lt;code&gt;PUSHER_APP_*&lt;/code&gt; keys, replace the placeholders with the keys gotten from your Pusher dashboard. That’s all we need to do to set up Pusher Channels for this application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the backend application
&lt;/h2&gt;

&lt;p&gt;Now that we have set up all the dependencies, we can start building the application. We will start by creating the routes. However, instead of creating controllers to hook into the routes, we will be adding the logic directly to the routes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up the database, migration, and model
&lt;/h3&gt;

&lt;p&gt;Since we will be working with a database, we need to set up the database we are going to be working with. To make things easy we will be using SQLite. Create an empty &lt;code&gt;database.sqlite&lt;/code&gt; file in the &lt;code&gt;database&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;.env&lt;/code&gt; file and replace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    DB_CONNECTION=mysql
    DB_HOST=127.0.0.1
    DB_PORT=3306
    DB_DATABASE=homestead
    DB_USERNAME=homestead
    DB_PASSWORD=secret

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    DB_CONNECTION=sqlite
    DB_DATABASE=/full/path/to/your/database.sqlite

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let’s create a migration for the &lt;code&gt;devices&lt;/code&gt; table. We will use this table to store devices and their notification settings. This will help us know what devices to send push notifications to.&lt;/p&gt;

&lt;p&gt;Run the command below to create the migration and model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ php artisan make:model Device -m

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;-m&lt;/code&gt; flag will instruct artisan to create a migration alongside the model.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This command will generate two files, the migration file in the &lt;code&gt;database/migrations&lt;/code&gt; and the model in the &lt;code&gt;app&lt;/code&gt; directory. Let’s edit the migration file first.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;*_create_devices_table.php&lt;/code&gt; migration file in the &lt;code&gt;database/migrations&lt;/code&gt; directory and replace the contents with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?php&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Support&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Facades&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Schema&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Database&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Schema&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Blueprint&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Database&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Migrations&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Migration&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;CreateDevicesTable&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;extends&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Migration&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-comment"&amp;gt;/**
         * Run the migrations.
         *
         * &amp;lt;span class="hljs-doctag"&amp;gt;@return&amp;lt;/span&amp;gt; void
         */&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;up&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            Schema::create(&amp;lt;span class="hljs-string"&amp;gt;'devices'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-params"&amp;gt;(Blueprint $table)&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
                $table-&amp;gt;increments(&amp;lt;span class="hljs-string"&amp;gt;'id'&amp;lt;/span&amp;gt;);
                $table-&amp;gt;string(&amp;lt;span class="hljs-string"&amp;gt;'uuid'&amp;lt;/span&amp;gt;)-&amp;gt;unique();
                $table-&amp;gt;float(&amp;lt;span class="hljs-string"&amp;gt;'btc_min_notify'&amp;lt;/span&amp;gt;)-&amp;gt;default(&amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;);
                $table-&amp;gt;float(&amp;lt;span class="hljs-string"&amp;gt;'btc_max_notify'&amp;lt;/span&amp;gt;)-&amp;gt;default(&amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;);
                $table-&amp;gt;float(&amp;lt;span class="hljs-string"&amp;gt;'eth_min_notify'&amp;lt;/span&amp;gt;)-&amp;gt;default(&amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;);
                $table-&amp;gt;float(&amp;lt;span class="hljs-string"&amp;gt;'eth_max_notify'&amp;lt;/span&amp;gt;)-&amp;gt;default(&amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;);
            });
        }

        &amp;lt;span class="hljs-comment"&amp;gt;/**
         * Reverse the migrations.
         *
         * &amp;lt;span class="hljs-doctag"&amp;gt;@return&amp;lt;/span&amp;gt; void
         */&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;down&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            Schema::dropIfExists(&amp;lt;span class="hljs-string"&amp;gt;'devices'&amp;lt;/span&amp;gt;);
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;up&lt;/code&gt; method, we have defined the structure of the &lt;code&gt;devices&lt;/code&gt; table. We have the &lt;code&gt;uuid&lt;/code&gt; field which will be a unique string for each device registered. We have two &lt;code&gt;btc_notify&lt;/code&gt; fields which are there to save the minimum and maximum prices of BTC at which point the device should be notified. Same applies to the* &lt;code&gt;eth_*_notify&lt;/code&gt; fields.&lt;/p&gt;

&lt;p&gt;To run the migration, run the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ php artisan migrate

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the &lt;code&gt;app/Device.php&lt;/code&gt; model and replace the contents with the code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?php&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-keyword"&amp;gt;namespace&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Database&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Eloquent&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Model&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Notifications&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Notifiable&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Device&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;extends&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Model&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Notifiable&amp;lt;/span&amp;gt;;

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; $timestamps = &amp;lt;span class="hljs-keyword"&amp;gt;false&amp;lt;/span&amp;gt;;

        &amp;lt;span class="hljs-keyword"&amp;gt;protected&amp;lt;/span&amp;gt; $fillable = [
            &amp;lt;span class="hljs-string"&amp;gt;'uuid'&amp;lt;/span&amp;gt;, 
            &amp;lt;span class="hljs-string"&amp;gt;'btc_min_notify'&amp;lt;/span&amp;gt;, 
            &amp;lt;span class="hljs-string"&amp;gt;'btc_max_notify'&amp;lt;/span&amp;gt;, 
            &amp;lt;span class="hljs-string"&amp;gt;'eth_min_notify'&amp;lt;/span&amp;gt;, 
            &amp;lt;span class="hljs-string"&amp;gt;'eth_max_notify'&amp;lt;/span&amp;gt;,
        ];

        &amp;lt;span class="hljs-keyword"&amp;gt;protected&amp;lt;/span&amp;gt; $cast = [
            &amp;lt;span class="hljs-string"&amp;gt;'btc_min_notify'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'float'&amp;lt;/span&amp;gt;,
            &amp;lt;span class="hljs-string"&amp;gt;'btc_max_notify'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'float'&amp;lt;/span&amp;gt;,
            &amp;lt;span class="hljs-string"&amp;gt;'eth_min_notify'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'float'&amp;lt;/span&amp;gt;,
            &amp;lt;span class="hljs-string"&amp;gt;'eth_max_notify'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'float'&amp;lt;/span&amp;gt;
        ];

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;scopeAffected&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;($query, string $currency, $currentPrice)&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; $query-&amp;gt;where(&amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-params"&amp;gt;($q)&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-params"&amp;gt;($currency, $currentPrice)&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
                $q-&amp;gt;where(&amp;lt;span class="hljs-string"&amp;gt;"${currency}_min_notify"&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'&amp;gt;'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;)
                  -&amp;gt;where(&amp;lt;span class="hljs-string"&amp;gt;"${currency}_min_notify"&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'&amp;gt;'&amp;lt;/span&amp;gt;, $currentPrice);
            })-&amp;gt;orWhere(&amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-params"&amp;gt;($q)&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-params"&amp;gt;($currency, $currentPrice)&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
                $q-&amp;gt;where(&amp;lt;span class="hljs-string"&amp;gt;"${currency}_max_notify"&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'&amp;gt;'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;)
                  -&amp;gt;where(&amp;lt;span class="hljs-string"&amp;gt;"${currency}_max_notify"&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'&amp;lt;'&amp;lt;/span&amp;gt;, $currentPrice);
            });
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the model above, we have set the &lt;code&gt;$timestamps&lt;/code&gt; property to &lt;code&gt;false&lt;/code&gt; to make sure that Eloquent does not try to update the &lt;code&gt;created_at&lt;/code&gt; and &lt;code&gt;updated_at&lt;/code&gt; fields, which is the normal behavior.&lt;/p&gt;

&lt;p&gt;We also have the &lt;code&gt;scopeAffected&lt;/code&gt; method which is an example of an &lt;a href="https://laravel.com/docs/5.6/eloquent#local-scopes" rel="noopener noreferrer"&gt;Eloquent scope&lt;/a&gt;. We use this to get the affected devices after a price change has occurred on a currency. So if, for instance, BTC’s price drops, this method will check the devices and the settings to see the devices that need to be notified of this change.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Local scopes allow you to define common sets of constraints that you may easily re-use throughout your application. For example, you may need to frequently retrieve all users that are considered "popular". To define a scope, prefix an Eloquent model method with &lt;code&gt;scope&lt;/code&gt;. - &lt;a href="https://laravel.com/docs/5.6/eloquent#local-scopes" rel="noopener noreferrer"&gt;Laravel documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We will use this scope later in our application when we need to know what devices to send push notifications to.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the routes
&lt;/h3&gt;

&lt;p&gt;Open the &lt;code&gt;routes/api.php&lt;/code&gt; file and replace the contents of the file with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-comment"&amp;gt;// File: routes/api.php&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?php&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Device&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Http&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Request&amp;lt;/span&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let’s add the first route. Append the code below to the routes file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-comment"&amp;gt;// File: routes/api.php&amp;lt;/span&amp;gt;
    Route::get(&amp;lt;span class="hljs-string"&amp;gt;'/settings'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-params"&amp;gt;(Request $request)&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; Device::whereUuid($request-&amp;gt;query(&amp;lt;span class="hljs-string"&amp;gt;'u'&amp;lt;/span&amp;gt;))-&amp;gt;firstOrFail()[&amp;lt;span class="hljs-string"&amp;gt;'settings'&amp;lt;/span&amp;gt;];
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the route above, we are returning the settings for the device supplied in the &lt;code&gt;u&lt;/code&gt; query parameter. This means if a registered device hits the &lt;code&gt;/settings&lt;/code&gt; endpoint and passes the device UUID through the &lt;code&gt;u&lt;/code&gt; parameter, the settings for that device will be returned.&lt;/p&gt;

&lt;p&gt;Next, in the same routes file, paste the following at the bottom of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    Route::post(&amp;lt;span class="hljs-string"&amp;gt;'/settings'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-params"&amp;gt;(Request $request)&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        $settings = $request-&amp;gt;validate([
            &amp;lt;span class="hljs-string"&amp;gt;'btc_min_notify'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'int|min:0'&amp;lt;/span&amp;gt;,
            &amp;lt;span class="hljs-string"&amp;gt;'btc_max_notify'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'int|min:0'&amp;lt;/span&amp;gt;,
            &amp;lt;span class="hljs-string"&amp;gt;'eth_min_notify'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'int|min:0'&amp;lt;/span&amp;gt;,
            &amp;lt;span class="hljs-string"&amp;gt;'eth_max_notify'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'int|min:0'&amp;lt;/span&amp;gt;,
        ]);

        $settings = array_filter($settings, &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-params"&amp;gt;($value)&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{ &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; $value &amp;gt; &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;; });

        $device = Device::firstOrNew([&amp;lt;span class="hljs-string"&amp;gt;'uuid'&amp;lt;/span&amp;gt; =&amp;gt; $request-&amp;gt;query(&amp;lt;span class="hljs-string"&amp;gt;'u'&amp;lt;/span&amp;gt;)]);
        $device-&amp;gt;fill($settings);
        $saved = $device-&amp;gt;save();

        &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; response()-&amp;gt;json([
            &amp;lt;span class="hljs-string"&amp;gt;'status'&amp;lt;/span&amp;gt; =&amp;gt; $saved ? &amp;lt;span class="hljs-string"&amp;gt;'success'&amp;lt;/span&amp;gt; : &amp;lt;span class="hljs-string"&amp;gt;'failure'&amp;lt;/span&amp;gt;
        ], $saved ? &amp;lt;span class="hljs-number"&amp;gt;200&amp;lt;/span&amp;gt; : &amp;lt;span class="hljs-number"&amp;gt;400&amp;lt;/span&amp;gt;);
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above, we have defined the route for the &lt;code&gt;POST /settings&lt;/code&gt; route. This route saves settings to the database. It will create a new entry if the setting does not already exist or will update the existing one if it does.&lt;/p&gt;

&lt;p&gt;That’s all for the routes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the jobs, events, and notifiers
&lt;/h3&gt;

&lt;p&gt;Next, we need to create the &lt;a href="https://laravel.com/docs/5.6/queues#creating-jobs" rel="noopener noreferrer"&gt;Laravel job&lt;/a&gt; that will run at intervals to check if there is a change in the currency price.&lt;/p&gt;

&lt;p&gt;Run the command below to create a new Laravel job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ php artisan make:job CheckPrices

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a new &lt;code&gt;CheckPrices&lt;/code&gt; class in the &lt;code&gt;app&lt;/code&gt; directory. Open that class and replace the contents with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?php&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-keyword"&amp;gt;namespace&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Jobs&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Device&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Bus&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Queueable&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Queue&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;SerializesModels&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Queue&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;InteractsWithQueue&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Contracts&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Queue&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;ShouldQueue&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Foundation&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Bus&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Dispatchable&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Events&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;CurrencyUpdated&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Notifications&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;CoinPriceChanged&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;CheckPrices&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;implements&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;ShouldQueue&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Dispatchable&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-title"&amp;gt;InteractsWithQueue&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-title"&amp;gt;Queueable&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-title"&amp;gt;SerializesModels&amp;lt;/span&amp;gt;;

        &amp;lt;span class="hljs-keyword"&amp;gt;protected&amp;lt;/span&amp;gt; $supportedCurrencies = [&amp;lt;span class="hljs-string"&amp;gt;'ETH'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'BTC'&amp;lt;/span&amp;gt;];

        &amp;lt;span class="hljs-comment"&amp;gt;/**
         * Execute the job.
         *
         * &amp;lt;span class="hljs-doctag"&amp;gt;@return&amp;lt;/span&amp;gt; void
         */&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;handle&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            $payload = &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;getPricesForSupportedCurrencies();

            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (!&amp;lt;span class="hljs-keyword"&amp;gt;empty&amp;lt;/span&amp;gt;($payload)) {
                &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;triggerPusherUpdate($payload);
                &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;triggerPossiblePushNotification($payload);
            }
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;triggerPusherUpdate&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;($payload)&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            event(&amp;lt;span class="hljs-keyword"&amp;gt;new&amp;lt;/span&amp;gt; CurrencyUpdated($payload));
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;triggerPossiblePushNotification&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;($payload)&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-keyword"&amp;gt;foreach&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;supportedCurrencies &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt; $currency) {
                $currentPrice = $payload[$currency][&amp;lt;span class="hljs-string"&amp;gt;'current'&amp;lt;/span&amp;gt;];

                $currency = strtolower($currency);

                &amp;lt;span class="hljs-keyword"&amp;gt;foreach&amp;lt;/span&amp;gt; (Device::affected($currency, $currentPrice)-&amp;gt;get() &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt; $device) {
                    $device-&amp;gt;notify(&amp;lt;span class="hljs-keyword"&amp;gt;new&amp;lt;/span&amp;gt; CoinPriceChanged($currency, $device, $payload));
                }
            }
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;getPricesForSupportedCurrencies&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;array&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            $payload = [];

            &amp;lt;span class="hljs-keyword"&amp;gt;foreach&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;supportedCurrencies &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt; $currency) {
                &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (config(&amp;lt;span class="hljs-string"&amp;gt;'app.debug'&amp;lt;/span&amp;gt;) === &amp;lt;span class="hljs-keyword"&amp;gt;true&amp;lt;/span&amp;gt;) {
                    $response = [
                        $currency =&amp;gt; [
                            &amp;lt;span class="hljs-string"&amp;gt;'USD'&amp;lt;/span&amp;gt; =&amp;gt; (float) rand(&amp;lt;span class="hljs-number"&amp;gt;100&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;15000&amp;lt;/span&amp;gt;)
                        ]
                    ];
                } &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
                    $url = &amp;lt;span class="hljs-string"&amp;gt;"https://min-api.cryptocompare.com/data/pricehistorical?fsym={$currency}&amp;amp;tsyms=USD&amp;amp;ts={$timestamp}"&amp;lt;/span&amp;gt;;

                    $response = json_decode(file_get_contents($url), &amp;lt;span class="hljs-keyword"&amp;gt;true&amp;lt;/span&amp;gt;);
                }

                &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (json_last_error() === JSON_ERROR_NONE) {
                    $currentPrice = $response[$currency][&amp;lt;span class="hljs-string"&amp;gt;'USD'&amp;lt;/span&amp;gt;];

                    $previousPrice = cache()-&amp;gt;get(&amp;lt;span class="hljs-string"&amp;gt;"PRICE_${currency}"&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-keyword"&amp;gt;false&amp;lt;/span&amp;gt;);

                    &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; ($previousPrice == &amp;lt;span class="hljs-keyword"&amp;gt;false&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;or&amp;lt;/span&amp;gt; $previousPrice !== $currentPrice) {
                        $payload[$currency] = [
                            &amp;lt;span class="hljs-string"&amp;gt;'current'&amp;lt;/span&amp;gt; =&amp;gt; $currentPrice,
                            &amp;lt;span class="hljs-string"&amp;gt;'previous'&amp;lt;/span&amp;gt; =&amp;gt; $previousPrice,
                        ];
                    }

                    cache()-&amp;gt;put(&amp;lt;span class="hljs-string"&amp;gt;"PRICE_${currency}"&amp;lt;/span&amp;gt;, $currentPrice, (&amp;lt;span class="hljs-number"&amp;gt;24&amp;lt;/span&amp;gt; * &amp;lt;span class="hljs-number"&amp;gt;60&amp;lt;/span&amp;gt; * &amp;lt;span class="hljs-number"&amp;gt;60&amp;lt;/span&amp;gt;));
                }
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; $payload;
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the class above, we implement the &lt;code&gt;ShouldQueue&lt;/code&gt; interface. This makes it so that the job can and will be queued. In a production server, queueing jobs makes your application faster as it queues jobs that might take a while to execute for later execution.&lt;/p&gt;

&lt;p&gt;We have four methods in this class. The first one is the &lt;code&gt;handle&lt;/code&gt; method. This one is called automatically when the job is executed. In this method, we fetch the prices for the available currencies and then check if the price has changed. If it has, we publish a Pusher Channel event and then check if there are any devices that need to be notified based on the user’s settings. If there are any, we send a push notification to that device.&lt;/p&gt;

&lt;p&gt;We have the &lt;code&gt;triggerPusherUpdate&lt;/code&gt; method which triggers a &lt;code&gt;CurrencyUpdated&lt;/code&gt; event. We will create this event in the next section. We also have a &lt;code&gt;triggerPossiblePushNotification&lt;/code&gt; method which gets the list of devices which should be notified of the currency change and then notifies the user using the &lt;code&gt;CoinPriceChanged&lt;/code&gt; class, which we will create in the next section.&lt;/p&gt;

&lt;p&gt;Lastly, we have the &lt;code&gt;getPricesForSupportedCurrencies&lt;/code&gt; method which just fetches the current price of a currency. In this method, we have a debug mode that simulates the current price of a currency.&lt;/p&gt;

&lt;p&gt;To make sure this class we just created is scheduled properly, open the &lt;code&gt;app/Console/Kernel.php&lt;/code&gt; file and in the &lt;code&gt;schedule&lt;/code&gt; method, add the following code to the &lt;code&gt;schedule&lt;/code&gt; method:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$schedule-&amp;gt;job(&amp;lt;span class="hljs-keyword"&amp;gt;new&amp;lt;/span&amp;gt; \App\Jobs\CheckPrices)-&amp;gt;everyMinute();&lt;/code&gt;Now every time we run the command &lt;code&gt;php artisan schedule:run&lt;/code&gt; all the jobs in this &lt;code&gt;schedule&lt;/code&gt; method will be run. Normally, in a production environment, we will need to add the schedule command as a cronjob, however, we will run this command manually.&lt;/p&gt;

&lt;p&gt;The next thing to do will be to create the notifiers and events. In your terminal, run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ php artisan make:event CurrencyUpdated
    $ php artisan make:notification CoinPriceChanged

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a class in the &lt;code&gt;Events&lt;/code&gt; and &lt;code&gt;Notifications&lt;/code&gt; directories.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://laravel.com/docs/5.6/events" rel="noopener noreferrer"&gt;event&lt;/a&gt; class, &lt;code&gt;CurrencyUpdated&lt;/code&gt; paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?php&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-keyword"&amp;gt;namespace&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Events&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Broadcasting&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Channel&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Queue&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;SerializesModels&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Foundation&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Events&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Dispatchable&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Broadcasting&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;InteractsWithSockets&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Contracts&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Broadcasting&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;ShouldBroadcast&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;CurrencyUpdated&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;implements&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;ShouldBroadcast&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Dispatchable&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-title"&amp;gt;InteractsWithSockets&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-title"&amp;gt;SerializesModels&amp;lt;/span&amp;gt;;

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; $payload;

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;__construct&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;($payload)&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;payload = $payload;
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;broadcastOn&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;new&amp;lt;/span&amp;gt; Channel(&amp;lt;span class="hljs-string"&amp;gt;'currency-update'&amp;lt;/span&amp;gt;);
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;broadcastAs&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'currency.updated'&amp;lt;/span&amp;gt;;
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the event class above, we have the &lt;code&gt;broadcastOn&lt;/code&gt; method that specifies the Pusher channel we want to broadcast an event on. We also have the &lt;code&gt;broadcastAs&lt;/code&gt; method which specifies the name of the event we want to broadcast to the channel.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;CoinPriceChanged&lt;/code&gt; &lt;a href="https://laravel.com/docs/5.6/notifications" rel="noopener noreferrer"&gt;notification&lt;/a&gt; class, replace the contents with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?php&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-keyword"&amp;gt;namespace&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Notifications&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Device&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Bus&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Queueable&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Neo&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;PusherBeams&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;PusherBeams&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Neo&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;PusherBeams&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;PusherMessage&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Notifications&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Notification&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;CoinPriceChanged&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;extends&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Notification&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Queueable&amp;lt;/span&amp;gt;;

        &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; $currency;
        &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; $device;
        &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; $payload;

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;__construct&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(string $currency, Device $device, array $payload)&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;currency = $currency;
            &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;device = $device;
            &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;payload = $payload;
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;via&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;($notifiable)&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; [PusherBeams::class];
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;toPushNotification&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;($notifiable)&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            $currentPrice = &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;payload[strtoupper(&amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;currency)][&amp;lt;span class="hljs-string"&amp;gt;'current'&amp;lt;/span&amp;gt;];

            $previousPrice = &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;payload[strtoupper(&amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;currency)][&amp;lt;span class="hljs-string"&amp;gt;'current'&amp;lt;/span&amp;gt;];

            $direction = $currentPrice &amp;gt; $previousPrice ? &amp;lt;span class="hljs-string"&amp;gt;'climbed'&amp;lt;/span&amp;gt; : &amp;lt;span class="hljs-string"&amp;gt;'dropped'&amp;lt;/span&amp;gt;;

            $currentPriceFormatted = number_format($currentPrice);

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; PusherMessage::create()
                    -&amp;gt;iOS()
                    -&amp;gt;sound(&amp;lt;span class="hljs-string"&amp;gt;'success'&amp;lt;/span&amp;gt;)
                    -&amp;gt;title(&amp;lt;span class="hljs-string"&amp;gt;"Price of {$this-&amp;gt;currency} has {$direction}"&amp;lt;/span&amp;gt;)
                    -&amp;gt;body(&amp;lt;span class="hljs-string"&amp;gt;"The price of {$this-&amp;gt;currency} has {$direction} and is now \${$currentPriceFormatted}"&amp;lt;/span&amp;gt;);
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;pushNotificationInterest&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            $uuid = strtolower(str_replace(&amp;lt;span class="hljs-string"&amp;gt;'-'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'_'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;device-&amp;gt;uuid));

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;"{$uuid}_{$this-&amp;gt;currency}_changed"&amp;lt;/span&amp;gt;;
        }
    } 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the class above we have the &lt;code&gt;toPushNotification&lt;/code&gt; class which prepares the push notification using the Pusher Beams library. We also have the &lt;code&gt;pushNotificationInterest&lt;/code&gt; method which sets the name for the interest of the push notification depending on the currency and device ID.&lt;/p&gt;

&lt;p&gt;That’s all for the backend, now just run the command below to start the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ php artisan serve

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will start a PHP server with our application running. Also if you need to manually trigger a currency change, run the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ php artisan schedule:run

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we are done with the backend, we can create the application using Swift and Xcode.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this part of the article, we have created the backend for our cryptocurrency alert application. &lt;a href="https://dev.to/neo/create-a-cryptocurrency-tracking-app-with-push-notifications-using-swift-and-laravel---part-2-the-ios-app-251a"&gt;In the next part&lt;/a&gt;, we will be seeing how we can create the application that will consume the API we just created in this part.&lt;/p&gt;

&lt;p&gt;The source code to this application is available on &lt;a href="https://github.com/neoighodaro/cryptocurrency-alert-ios-app" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post first appeared on the &lt;a href="https://pusher.com/tutorials/cryptocurrency-tracking-swift-laravel-part-1" rel="noopener noreferrer"&gt;Pusher blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>pusher</category>
      <category>swift</category>
      <category>ios</category>
      <category>realtime</category>
    </item>
    <item>
      <title>Create a two-player game with Python and Vue</title>
      <dc:creator>Neo</dc:creator>
      <pubDate>Tue, 20 Nov 2018 06:53:58 +0000</pubDate>
      <link>https://dev.to/neo/create-a-two-player-game-with-python-and-vue-4c3j</link>
      <guid>https://dev.to/neo/create-a-two-player-game-with-python-and-vue-4c3j</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;You will need Python 3+, virtualenv and Flask installed on your machine.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The advent of the PC and the internet has redefined the term “entertainment” and the means by which it can be obtained. While a console or some special hardware would have been required to play games in the past, games are only a click away in today's world of technology.&lt;/p&gt;

&lt;p&gt;In this tutorial, we will create a realtime tic-tac-toe game using Python and Pusher channels. Here’s a demo of how the game will look and behave upon creation:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/1RGRfjXfgQgYWYy2kqImOs/0c1db39f9371dbb773638ffe57bfd385/python-vue-game-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/1RGRfjXfgQgYWYy2kqImOs/0c1db39f9371dbb773638ffe57bfd385/python-vue-game-demo.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This multiplayer game will allow a player to connect using their preferred username (or generate a random username where a player doesn’t connect with a username) and choose to play with another player from a list of other online players.&lt;/p&gt;

&lt;p&gt;The game itself follows the conventional principles of the popular &lt;a href="https://en.wikipedia.org/wiki/Tic-tac-toe" rel="noopener noreferrer"&gt;tic-tac-toe&lt;/a&gt; game. The “online player(s)” feature is powered by &lt;a href="https://pusher.com/docs/client_api_guide/client_presence_channels" rel="noopener noreferrer"&gt;Pusher presence channels&lt;/a&gt; and the realtime updates of a player’s move across multiple windows is powered by &lt;a href="https://pusher.com/docs/client_api_guide/client_private_channels" rel="noopener noreferrer"&gt;Pusher private channels.&lt;/a&gt; The source code for this tutorial is available here &lt;a href="https://github.com/neoighodaro/python-pusher-multiplayer-game" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To follow along, a basic knowledge of Python, Flask, JavaScript (ES6 syntax) and Vue is required. You will also need the following installed on your machine:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.python.org/" rel="noopener noreferrer"&gt;Python (v3.x)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://virtualenv.pypa.io/en/stable/" rel="noopener noreferrer"&gt;Virtualenv&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://flask.pocoo.org/" rel="noopener noreferrer"&gt;Flask&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Virtualenv is great for creating isolated Python environments, so we can install dependencies in an isolated environment without polluting our global packages directory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the app environment
&lt;/h2&gt;

&lt;p&gt;We will create the project folder and activate a virtual environment within it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ mkdir python-pusher-mutiplayer-game
    $ cd python-pusher-mutiplayer-game
    $ virtualenv .venv
    $ source .venv/bin/activate # Linux based systems
    $ \path\to\env\Scripts\activate # Windows users

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will install &lt;a href="http://flask.pocoo.org/" rel="noopener noreferrer"&gt;Flask&lt;/a&gt; using this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ pip install flask

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting up Pusher
&lt;/h2&gt;

&lt;p&gt;To integrate Pusher into the multiplayer game, we need to create a Pusher channels application from the Pusher dashboard. If you don’t already have a Pusher account, head over to the &lt;a href="https://pusher.com/" rel="noopener noreferrer"&gt;Pusher website&lt;/a&gt; and create one.&lt;/p&gt;

&lt;p&gt;After creating an account, create a new channels application and enable client events from the application dashboard. To enable client events, click on &lt;strong&gt;App settings&lt;/strong&gt; and scroll to the bottom of the page then select the option that says &lt;strong&gt;Enable client events,&lt;/strong&gt; and update the &lt;strong&gt;App settings.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the backend server
&lt;/h2&gt;

&lt;p&gt;Back in the project directory, let’s install the &lt;a href="https://github.com/pusher/pusher-http-python" rel="noopener noreferrer"&gt;Python Pusher library&lt;/a&gt; with this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ pip install pusher
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will create a new file and call it &lt;code&gt;app.py&lt;/code&gt;, this is where we will write all the code for the Flask backend server. We will also create a folder and call it &lt;code&gt;templates&lt;/code&gt;, this folder will hold the markup files for this application.&lt;/p&gt;

&lt;p&gt;Let’s write some code to register the endpoints for the game and serve the view, open the &lt;code&gt;app.py&lt;/code&gt; file and paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // File: ./app.py
    &amp;lt;span class="hljs-keyword"&amp;gt;from&amp;lt;/span&amp;gt; flask &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; Flask, render_template, request, jsonify, make_response, json
    &amp;lt;span class="hljs-keyword"&amp;gt;from&amp;lt;/span&amp;gt; pusher &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; pusher

    app = Flask(__name__)

    pusher = pusher_client = pusher.Pusher(
      app_id=&amp;lt;span class="hljs-string"&amp;gt;'PUSHER_APP_ID'&amp;lt;/span&amp;gt;,
      key=&amp;lt;span class="hljs-string"&amp;gt;'PUSHER_APP_KEY'&amp;lt;/span&amp;gt;,
      secret=&amp;lt;span class="hljs-string"&amp;gt;'PUSHER_APP_SECRET'&amp;lt;/span&amp;gt;,
      cluster=&amp;lt;span class="hljs-string"&amp;gt;'PUSHER_APP_CLUSTER'&amp;lt;/span&amp;gt;,
      ssl=&amp;lt;span class="hljs-keyword"&amp;gt;True&amp;lt;/span&amp;gt;
    )

    name = &amp;lt;span class="hljs-string"&amp;gt;''&amp;lt;/span&amp;gt;

&amp;lt;span class="hljs-meta"&amp;gt;    @app.route('/')&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;def&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;index&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;:&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; render_template(&amp;lt;span class="hljs-string"&amp;gt;'index.html'&amp;lt;/span&amp;gt;)

&amp;lt;span class="hljs-meta"&amp;gt;    @app.route('/play')&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;def&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;play&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;:&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-keyword"&amp;gt;global&amp;lt;/span&amp;gt; name
      name = request.args.get(&amp;lt;span class="hljs-string"&amp;gt;'username'&amp;lt;/span&amp;gt;)
      &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; render_template(&amp;lt;span class="hljs-string"&amp;gt;'play.html'&amp;lt;/span&amp;gt;)

&amp;lt;span class="hljs-meta"&amp;gt;    @app.route("/pusher/auth", methods=['POST'])&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;def&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;pusher_authentication&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;:&amp;lt;/span&amp;gt;
      auth = pusher.authenticate(
        channel=request.form[&amp;lt;span class="hljs-string"&amp;gt;'channel_name'&amp;lt;/span&amp;gt;],
        socket_id=request.form[&amp;lt;span class="hljs-string"&amp;gt;'socket_id'&amp;lt;/span&amp;gt;],
        custom_data={
          &amp;lt;span class="hljs-string"&amp;gt;u'user_id'&amp;lt;/span&amp;gt;: name,
          &amp;lt;span class="hljs-string"&amp;gt;u'user_info'&amp;lt;/span&amp;gt;: {
            &amp;lt;span class="hljs-string"&amp;gt;u'role'&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-string"&amp;gt;u'player'&amp;lt;/span&amp;gt;
          }
        }
      )
      &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; json.dumps(auth)

    &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; __name__ == &amp;lt;span class="hljs-string"&amp;gt;'__main__'&amp;lt;/span&amp;gt;:
        app.run(host=&amp;lt;span class="hljs-string"&amp;gt;'0.0.0.0'&amp;lt;/span&amp;gt;, port=&amp;lt;span class="hljs-number"&amp;gt;5000&amp;lt;/span&amp;gt;, debug=&amp;lt;span class="hljs-keyword"&amp;gt;True&amp;lt;/span&amp;gt;)

    name = &amp;lt;span class="hljs-string"&amp;gt;''&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Replace the &lt;code&gt;PUSHER_APP_*&lt;/code&gt; keys with the values on your Pusher dashboard.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the code above, we defined three endpoints, here’s what they do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/&lt;/code&gt; - renders the front page that asks a player to connect with a username.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/play&lt;/code&gt; - renders the game view.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/pusher/auth&lt;/code&gt; - authenticates Pusher’s presence and private channels for connected players.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building the frontend
&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;templates&lt;/code&gt; folder, we will create two files:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;index.html&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;play.html&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;index.html&lt;/code&gt; file will render the connection page, so open the &lt;code&gt;templates/index.html&lt;/code&gt; file and paste the following code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;span class="hljs-comment"&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;html&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;lang&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"en"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;head&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;meta&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;charset&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"utf-8"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;meta&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;name&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"viewport"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;content&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"width=device-width, initial-scale=1, shrink-to-fit=no"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;meta&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;name&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"description"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;content&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;""&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;meta&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;name&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"author"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;content&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"Neo Ighodaro"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;title&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;TIC-TAC-TOE&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;title&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;link&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;rel&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"stylesheet"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;href&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;style&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="css"&amp;gt;
              &amp;lt;span class="hljs-selector-pseudo"&amp;gt;:root&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-attribute"&amp;gt;--input-padding-x&amp;lt;/span&amp;gt;: .&amp;lt;span class="hljs-number"&amp;gt;75rem&amp;lt;/span&amp;gt;;
                &amp;lt;span class="hljs-attribute"&amp;gt;--input-padding-y&amp;lt;/span&amp;gt;: .&amp;lt;span class="hljs-number"&amp;gt;75rem&amp;lt;/span&amp;gt;;
              }
              &amp;lt;span class="hljs-selector-tag"&amp;gt;html&amp;lt;/span&amp;gt;,
              &amp;lt;span class="hljs-selector-tag"&amp;gt;body&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-selector-tag"&amp;gt;body&amp;lt;/span&amp;gt; &amp;gt; &amp;lt;span class="hljs-selector-tag"&amp;gt;div&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-attribute"&amp;gt;height&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;100%&amp;lt;/span&amp;gt;;
              }
              &amp;lt;span class="hljs-selector-tag"&amp;gt;body&amp;lt;/span&amp;gt; &amp;gt; &amp;lt;span class="hljs-selector-tag"&amp;gt;div&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-attribute"&amp;gt;display&amp;lt;/span&amp;gt;: -ms-flexbox;
                &amp;lt;span class="hljs-attribute"&amp;gt;display&amp;lt;/span&amp;gt;: flex;
                &amp;lt;span class="hljs-attribute"&amp;gt;-ms-flex-align&amp;lt;/span&amp;gt;: center;
                &amp;lt;span class="hljs-attribute"&amp;gt;align-items&amp;lt;/span&amp;gt;: center;
                &amp;lt;span class="hljs-attribute"&amp;gt;padding-top&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;40px&amp;lt;/span&amp;gt;;
                &amp;lt;span class="hljs-attribute"&amp;gt;padding-bottom&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;40px&amp;lt;/span&amp;gt;;
                &amp;lt;span class="hljs-attribute"&amp;gt;background-color&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;#f5f5f5&amp;lt;/span&amp;gt;;
              }
              &amp;lt;span class="hljs-selector-class"&amp;gt;.form-signin&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-attribute"&amp;gt;width&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;100%&amp;lt;/span&amp;gt;;
                &amp;lt;span class="hljs-attribute"&amp;gt;max-width&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;420px&amp;lt;/span&amp;gt;;
                &amp;lt;span class="hljs-attribute"&amp;gt;padding&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;15px&amp;lt;/span&amp;gt;;
                &amp;lt;span class="hljs-attribute"&amp;gt;margin&amp;lt;/span&amp;gt;: auto;
              }
              &amp;lt;span class="hljs-selector-class"&amp;gt;.form-label-group&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-attribute"&amp;gt;position&amp;lt;/span&amp;gt;: relative;
                &amp;lt;span class="hljs-attribute"&amp;gt;margin-bottom&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;1rem&amp;lt;/span&amp;gt;;
              }
              &amp;lt;span class="hljs-selector-class"&amp;gt;.form-label-group&amp;lt;/span&amp;gt; &amp;gt; &amp;lt;span class="hljs-selector-tag"&amp;gt;input&amp;lt;/span&amp;gt;,
              &amp;lt;span class="hljs-selector-class"&amp;gt;.form-label-group&amp;lt;/span&amp;gt; &amp;gt; &amp;lt;span class="hljs-selector-tag"&amp;gt;label&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-attribute"&amp;gt;padding&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-built_in"&amp;gt;var&amp;lt;/span&amp;gt;(--input-padding-y) &amp;lt;span class="hljs-built_in"&amp;gt;var&amp;lt;/span&amp;gt;(--input-padding-x);
              }
              &amp;lt;span class="hljs-selector-class"&amp;gt;.form-label-group&amp;lt;/span&amp;gt; &amp;gt; &amp;lt;span class="hljs-selector-tag"&amp;gt;label&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-attribute"&amp;gt;position&amp;lt;/span&amp;gt;: absolute;
                &amp;lt;span class="hljs-attribute"&amp;gt;top&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;;
                &amp;lt;span class="hljs-attribute"&amp;gt;left&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;;
                &amp;lt;span class="hljs-attribute"&amp;gt;display&amp;lt;/span&amp;gt;: block;
                &amp;lt;span class="hljs-attribute"&amp;gt;width&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;100%&amp;lt;/span&amp;gt;;
                &amp;lt;span class="hljs-attribute"&amp;gt;margin-bottom&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;; &amp;lt;span class="hljs-comment"&amp;gt;/* Override default `&amp;lt;label&amp;gt;` margin */&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-attribute"&amp;gt;line-height&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;1.5&amp;lt;/span&amp;gt;;
                &amp;lt;span class="hljs-attribute"&amp;gt;color&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;#495057&amp;lt;/span&amp;gt;;
                &amp;lt;span class="hljs-attribute"&amp;gt;cursor&amp;lt;/span&amp;gt;: text; &amp;lt;span class="hljs-comment"&amp;gt;/* Match the input under the label */&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-attribute"&amp;gt;border&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;1px&amp;lt;/span&amp;gt; solid transparent;
                &amp;lt;span class="hljs-attribute"&amp;gt;border-radius&amp;lt;/span&amp;gt;: .&amp;lt;span class="hljs-number"&amp;gt;25rem&amp;lt;/span&amp;gt;;
                &amp;lt;span class="hljs-attribute"&amp;gt;transition&amp;lt;/span&amp;gt;: all .&amp;lt;span class="hljs-number"&amp;gt;1s&amp;lt;/span&amp;gt; ease-in-out;
              }
              &amp;lt;span class="hljs-selector-class"&amp;gt;.form-label-group&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-selector-tag"&amp;gt;input&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-selector-pseudo"&amp;gt;::-webkit-input-placeholder&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-attribute"&amp;gt;color&amp;lt;/span&amp;gt;: transparent;
              }
              &amp;lt;span class="hljs-selector-class"&amp;gt;.form-label-group&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-selector-tag"&amp;gt;input&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-selector-pseudo"&amp;gt;:-ms-input-placeholder&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-attribute"&amp;gt;color&amp;lt;/span&amp;gt;: transparent;
              }
              &amp;lt;span class="hljs-selector-class"&amp;gt;.form-label-group&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-selector-tag"&amp;gt;input&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-selector-pseudo"&amp;gt;::-ms-input-placeholder&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-attribute"&amp;gt;color&amp;lt;/span&amp;gt;: transparent;
              }
              &amp;lt;span class="hljs-selector-class"&amp;gt;.form-label-group&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-selector-tag"&amp;gt;input&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-selector-pseudo"&amp;gt;::-moz-placeholder&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-attribute"&amp;gt;color&amp;lt;/span&amp;gt;: transparent;
              }
              &amp;lt;span class="hljs-selector-class"&amp;gt;.form-label-group&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-selector-tag"&amp;gt;input&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-selector-pseudo"&amp;gt;::placeholder&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-attribute"&amp;gt;color&amp;lt;/span&amp;gt;: transparent;
              }
              &amp;lt;span class="hljs-selector-class"&amp;gt;.form-label-group&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-selector-tag"&amp;gt;input&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-selector-pseudo"&amp;gt;:not(&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-selector-pseudo"&amp;gt;:placeholder-shown)&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-attribute"&amp;gt;padding-top&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-built_in"&amp;gt;calc&amp;lt;/span&amp;gt;(var(--input-padding-y) + &amp;lt;span class="hljs-built_in"&amp;gt;var&amp;lt;/span&amp;gt;(--input-padding-y) * (&amp;lt;span class="hljs-number"&amp;gt;2&amp;lt;/span&amp;gt; / &amp;lt;span class="hljs-number"&amp;gt;3&amp;lt;/span&amp;gt;));
                &amp;lt;span class="hljs-attribute"&amp;gt;padding-bottom&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-built_in"&amp;gt;calc&amp;lt;/span&amp;gt;(var(--input-padding-y) / &amp;lt;span class="hljs-number"&amp;gt;3&amp;lt;/span&amp;gt;);
              }
              &amp;lt;span class="hljs-selector-class"&amp;gt;.form-label-group&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-selector-tag"&amp;gt;input&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-selector-pseudo"&amp;gt;:not(&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-selector-pseudo"&amp;gt;:placeholder-shown)&amp;lt;/span&amp;gt; ~ &amp;lt;span class="hljs-selector-tag"&amp;gt;label&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-attribute"&amp;gt;padding-top&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-built_in"&amp;gt;calc&amp;lt;/span&amp;gt;(var(--input-padding-y) / &amp;lt;span class="hljs-number"&amp;gt;3&amp;lt;/span&amp;gt;);
                &amp;lt;span class="hljs-attribute"&amp;gt;padding-bottom&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-built_in"&amp;gt;calc&amp;lt;/span&amp;gt;(var(--input-padding-y) / &amp;lt;span class="hljs-number"&amp;gt;3&amp;lt;/span&amp;gt;);
                &amp;lt;span class="hljs-attribute"&amp;gt;font-size&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;12px&amp;lt;/span&amp;gt;;
                &amp;lt;span class="hljs-attribute"&amp;gt;color&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;#777&amp;lt;/span&amp;gt;;
              }
        &amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;style&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;head&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;body&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"app"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;form&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"form-signin"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"text-center mb-4"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
              &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;img&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"mb-4"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;src&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"https://thestore.gameops.com/v/vspfiles/photos/Tic-Tac-Go-14.gif"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;alt&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;""&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;width&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"72"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;height&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"72"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
              &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;h1&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"h3 mb-3 font-weight-normal"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;TIC-TAC-TOE&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;h1&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
              &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;p&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;PUT IN YOUR DETAILS TO PLAY&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;p&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"form-label-group"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;input&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;type&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"name"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"inputUsername"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;ref&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"username"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"form-control"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;placeholder&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"Username"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;required&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;""&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;autofocus&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;""&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                  &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;label&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;for&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"inputUsername"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;Username&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;label&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"form-label-group"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
              &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;input&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;type&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"email"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"inputEmail"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;ref&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"email"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"form-control"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;placeholder&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"Email address"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;autofocus&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;""&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;required&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;label&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;for&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"inputEmail"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;Email address&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;label&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;button&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"btn btn-lg btn-primary btn-block"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;type&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"submit"&amp;lt;/span&amp;gt; @&amp;lt;span class="hljs-attr"&amp;gt;click.prevent&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"login"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;Connect&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;button&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;p&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"mt-5 mb-3 text-muted text-center"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;© 2017-2018&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;p&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;form&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;script&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;src&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="undefined"&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;script&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;script&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="javascript"&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; app = &amp;lt;span class="hljs-keyword"&amp;gt;new&amp;lt;/span&amp;gt; Vue({
          &amp;lt;span class="hljs-attr"&amp;gt;el&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-string"&amp;gt;'#app'&amp;lt;/span&amp;gt;,
          &amp;lt;span class="hljs-attr"&amp;gt;methods&amp;lt;/span&amp;gt;: {
            &amp;lt;span class="hljs-attr"&amp;gt;login&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-params"&amp;gt;&amp;lt;/span&amp;gt;) &amp;lt;/span&amp;gt;{
              &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; username = &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.$refs.username.value
              &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; email = &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.$refs.email.value
              &amp;lt;span class="hljs-built_in"&amp;gt;window&amp;lt;/span&amp;gt;.location.replace(&amp;lt;span class="hljs-string"&amp;gt;`/play?username=&amp;lt;span class="hljs-subst"&amp;gt;${username}&amp;lt;/span&amp;gt;&amp;amp;email=&amp;lt;span class="hljs-subst"&amp;gt;${email}&amp;lt;/span&amp;gt;`&amp;lt;/span&amp;gt;);
            }
          }
        })
        &amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;script&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;body&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;html&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;When a player visits the connection page and puts in a username and email, the browser window will be redirected to the game view.&lt;/p&gt;

&lt;p&gt;Let’s write the markup for the game view. Open the &lt;code&gt;play.html&lt;/code&gt; file and paste the following code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;span class="hljs-comment"&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;html&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;lang&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"en"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;head&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
  &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;meta&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;charset&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"utf-8"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
  &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;meta&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;name&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"viewport"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;content&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"width=device-width, initial-scale=1, shrink-to-fit=no"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
  &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;link&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;rel&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"stylesheet"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;href&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
  &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;title&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;TIC-TAC-TOE&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;title&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;head&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;body&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
  &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"app"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"container-fluid"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"container-fluid clearfix mb-3 shadow"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;img&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"float-left my-3"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;src&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"https://thestore.gameops.com/v/vspfiles/photos/Tic-Tac-Go-14.gif"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;height&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"62px"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;width&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"62px"&amp;lt;/span&amp;gt;
      /&amp;gt;&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"float-right w-25 py-3"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;img&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"my-3 mx-3 rounded-circle border"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;src&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"http://dfsanonymous.club/wp-content/uploads/2017/11/DFSAnonymous-NewLogo.png"&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-attr"&amp;gt;height&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"62px"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;width&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"62px"&amp;lt;/span&amp;gt; /&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;p&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"d-inline"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;  {{ username }}  &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;p&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"row mx-5"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;style&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"height: 50vh"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"col-8 h-50 align-self-center"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"row border rounded invisible h-50 w-75 m-auto"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;style&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"font-size: 3.6rem"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;ref&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"gameboard"&amp;lt;/span&amp;gt; @&amp;lt;span class="hljs-attr"&amp;gt;click&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"playerAction"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"h-100 pr-2 col border border-dark"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;data-id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"1"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;ref&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"1"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"col pr-2 border border-dark"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;data-id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"2"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;ref&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"2"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"col pr-2 border border-dark"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;data-id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"3"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;ref&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"3"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"w-100"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"h-100 pr-2 col border border-dark"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;data-id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"4"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;ref&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"4"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"col pr-2 border border-dark"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;data-id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"5"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;ref&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"5"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"col pr-2 border border-dark"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;data-id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"6"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;ref&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"6"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"w-100"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"h-100 pr-2 col border border-dark"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;data-id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"7"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;ref&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"7"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"col pr-2 border border-dark"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;data-id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"8"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;ref&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"8"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"col pr-2 border border-dark"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;data-id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"9"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;ref&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"9"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"col-4 pl-3"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"row h-100"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"col border h-75 text-center"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;style&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"background: rgb(114, 230, 147);"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;p&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"my-3"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;  {{ players }}  online player(s) &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;p&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;hr&amp;lt;/span&amp;gt;/&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;li&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"m-auto py-3 text-dark"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;style&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"cursor: pointer;"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;v-for&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"member in connectedPlayers"&amp;lt;/span&amp;gt; @&amp;lt;span class="hljs-attr"&amp;gt;click&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"choosePlayer"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
               {{ member }} 
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;li&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"w-100"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"col text-center py-3 border h-25"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;style&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"background: #b6c0ca; font-size: 1em; font-weight: bold"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
             {{ status }} 
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
  &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
  &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;script&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;src&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="undefined"&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;script&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
  &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;script&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;src&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"https://js.pusher.com/4.2/pusher.min.js"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="undefined"&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;script&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
  &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;script&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="undefined"&amp;gt;

  &amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;script&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;body&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;html&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The code above defines the layout of the game view but does not contain any interactivity or realtime features. In the scripts section, before the closing &lt;code&gt;body&lt;/code&gt; tag, we included the Vue and Pusher libraries because they are required for the game to work.&lt;/p&gt;

&lt;p&gt;Let’s include the JavaScript code that will drive the entire game process and define its logic.&lt;/p&gt;

&lt;p&gt;In the same file, add the code below in between the &lt;code&gt;script&lt;/code&gt; tag that is just before the closing &lt;code&gt;body&lt;/code&gt; tag:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; app = &amp;lt;span class="hljs-keyword"&amp;gt;new&amp;lt;/span&amp;gt; Vue({
  &amp;lt;span class="hljs-attr"&amp;gt;el&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-string"&amp;gt;'#app'&amp;lt;/span&amp;gt;,

  &amp;lt;span class="hljs-attr"&amp;gt;data&amp;lt;/span&amp;gt;: {
    &amp;lt;span class="hljs-attr"&amp;gt;username&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-string"&amp;gt;''&amp;lt;/span&amp;gt;,
    &amp;lt;span class="hljs-attr"&amp;gt;players&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;,
    &amp;lt;span class="hljs-attr"&amp;gt;connectedPlayers&amp;lt;/span&amp;gt;: [],
    &amp;lt;span class="hljs-attr"&amp;gt;status&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-string"&amp;gt;''&amp;lt;/span&amp;gt;,
    &amp;lt;span class="hljs-attr"&amp;gt;pusher&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-keyword"&amp;gt;new&amp;lt;/span&amp;gt; Pusher(&amp;lt;span class="hljs-string"&amp;gt;'PUSHER_APP_KEY'&amp;lt;/span&amp;gt;, {
      &amp;lt;span class="hljs-attr"&amp;gt;authEndpoint&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-string"&amp;gt;'/pusher/auth'&amp;lt;/span&amp;gt;,
      &amp;lt;span class="hljs-attr"&amp;gt;cluster&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-string"&amp;gt;'PUSHER_APP_CLUSTER'&amp;lt;/span&amp;gt;,
      &amp;lt;span class="hljs-attr"&amp;gt;encrypted&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;
    }),
    &amp;lt;span class="hljs-attr"&amp;gt;otherPlayerName&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-string"&amp;gt;''&amp;lt;/span&amp;gt;,
    &amp;lt;span class="hljs-attr"&amp;gt;mychannel&amp;lt;/span&amp;gt;: {},
    &amp;lt;span class="hljs-attr"&amp;gt;otherPlayerChannel&amp;lt;/span&amp;gt;: {},
    &amp;lt;span class="hljs-attr"&amp;gt;firstPlayer&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;,
    &amp;lt;span class="hljs-attr"&amp;gt;turn&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;,
    &amp;lt;span class="hljs-attr"&amp;gt;boxes&amp;lt;/span&amp;gt;: [&amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;]
  },

  created () {
    &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; url = &amp;lt;span class="hljs-keyword"&amp;gt;new&amp;lt;/span&amp;gt; URL(&amp;lt;span class="hljs-built_in"&amp;gt;window&amp;lt;/span&amp;gt;.location.href);
    &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; name = url.searchParams.get(&amp;lt;span class="hljs-string"&amp;gt;"username"&amp;lt;/span&amp;gt;);

    &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (name) {
      &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.username = name
      &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.subscribe();
      &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.listeners();
    } &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
      &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.username = &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.generateRandomName();
      location.assign(&amp;lt;span class="hljs-string"&amp;gt;"/play?username="&amp;lt;/span&amp;gt; + &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.username);
    }
  },

  &amp;lt;span class="hljs-attr"&amp;gt;methods&amp;lt;/span&amp;gt;: {
    &amp;lt;span class="hljs-comment"&amp;gt;// We will add methods here&amp;lt;/span&amp;gt;
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Replace the &lt;code&gt;PUSHER_APP_*&lt;/code&gt; keys with the keys on your Pusher dashboard.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Above, we create a new instance of Vue and we target the &lt;code&gt;#app&lt;/code&gt; selector. We define all the defaults in the &lt;code&gt;data&lt;/code&gt; object and then in the &lt;code&gt;create()&lt;/code&gt; function which is called automatically when the Vue component is created, we check for a user and assign the user to the username if one was supplied.&lt;/p&gt;

&lt;p&gt;We also make calls to the &lt;code&gt;subscribe&lt;/code&gt; and &lt;code&gt;listeners&lt;/code&gt; methods. Let’s define those inside the &lt;code&gt;methods&lt;/code&gt; object. Inside the &lt;code&gt;methods&lt;/code&gt; object, paste the following functions:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;

subscribe: &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-params"&amp;gt;&amp;lt;/span&amp;gt;) &amp;lt;/span&amp;gt;{
  &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; channel = &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.pusher.subscribe(&amp;lt;span class="hljs-string"&amp;gt;'presence-channel'&amp;lt;/span&amp;gt;);
  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.myChannel = &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.pusher.subscribe(&amp;lt;span class="hljs-string"&amp;gt;'private-'&amp;lt;/span&amp;gt; + &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.username)

  channel.bind(&amp;lt;span class="hljs-string"&amp;gt;'pusher:subscription_succeeded'&amp;lt;/span&amp;gt;, (player) =&amp;gt; {
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.players = player.count - &amp;lt;span class="hljs-number"&amp;gt;1&amp;lt;/span&amp;gt;
    player.each(&amp;lt;span class="hljs-function"&amp;gt;(&amp;lt;span class="hljs-params"&amp;gt;player&amp;lt;/span&amp;gt;) =&amp;gt;&amp;lt;/span&amp;gt; {
      &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (player.id != &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.username)
        &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.connectedPlayers.push(player.id)
    });
  });

  channel.bind(&amp;lt;span class="hljs-string"&amp;gt;'pusher:member_added'&amp;lt;/span&amp;gt;, (player) =&amp;gt; {
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.players++;
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.connectedPlayers.push(player.id)
  });

  channel.bind(&amp;lt;span class="hljs-string"&amp;gt;'pusher:member_removed'&amp;lt;/span&amp;gt;, (player) =&amp;gt; {
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.players--;
    &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; index = &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.connectedPlayers.indexOf(player.id);
    &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (index &amp;gt; &amp;lt;span class="hljs-number"&amp;gt;-1&amp;lt;/span&amp;gt;) {
      &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.connectedPlayers.splice(index, &amp;lt;span class="hljs-number"&amp;gt;1&amp;lt;/span&amp;gt;)
    }
  });
},

&amp;lt;span class="hljs-attr"&amp;gt;listeners&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-params"&amp;gt;&amp;lt;/span&amp;gt;) &amp;lt;/span&amp;gt;{
  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.pusher.bind(&amp;lt;span class="hljs-string"&amp;gt;'client-'&amp;lt;/span&amp;gt; + &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.username, (message) =&amp;gt; {
    &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (confirm(&amp;lt;span class="hljs-string"&amp;gt;'Do you want to start a game of Tic Tac Toe with '&amp;lt;/span&amp;gt; + message)) {
      &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.otherPlayerName = message
      &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.otherPlayerChannel = &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.pusher.subscribe(&amp;lt;span class="hljs-string"&amp;gt;'private-'&amp;lt;/span&amp;gt; + &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.otherPlayerName)
      &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.otherPlayerChannel.bind(&amp;lt;span class="hljs-string"&amp;gt;'pusher:subscription_succeeded'&amp;lt;/span&amp;gt;, () =&amp;gt; {
        &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.otherPlayerChannel.trigger(&amp;lt;span class="hljs-string"&amp;gt;'client-game-started'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.username)
      })
      &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.startGame(message)
    } &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
      &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.otherPlayerChannel = &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.pusher.subscribe(&amp;lt;span class="hljs-string"&amp;gt;'private-'&amp;lt;/span&amp;gt; + message)
      &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.otherPlayerChannel.bind(&amp;lt;span class="hljs-string"&amp;gt;'pusher:subscription_succeeded'&amp;lt;/span&amp;gt;, () =&amp;gt; {
        &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.otherPlayerChannel.trigger(&amp;lt;span class="hljs-string"&amp;gt;'client-game-declined'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;""&amp;lt;/span&amp;gt;)
      })
      &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.gameDeclined()
    }
  }),

  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.myChannel.bind(&amp;lt;span class="hljs-string"&amp;gt;'client-game-started'&amp;lt;/span&amp;gt;, (message) =&amp;gt; {
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.status = &amp;lt;span class="hljs-string"&amp;gt;"Game started with "&amp;lt;/span&amp;gt; + message
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.$refs.gameboard.classList.remove(&amp;lt;span class="hljs-string"&amp;gt;'invisible'&amp;lt;/span&amp;gt;);
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.firstPlayer = &amp;lt;span class="hljs-number"&amp;gt;1&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.turn = &amp;lt;span class="hljs-number"&amp;gt;1&amp;lt;/span&amp;gt;;
  })

  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.myChannel.bind(&amp;lt;span class="hljs-string"&amp;gt;'client-game-declined'&amp;lt;/span&amp;gt;, () =&amp;gt; {
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.status = &amp;lt;span class="hljs-string"&amp;gt;"Game declined"&amp;lt;/span&amp;gt;
  })

  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.myChannel.bind(&amp;lt;span class="hljs-string"&amp;gt;'client-new-move'&amp;lt;/span&amp;gt;, (position) =&amp;gt; {
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.$refs[position].innerText = &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.firstPlayer ? &amp;lt;span class="hljs-string"&amp;gt;'O'&amp;lt;/span&amp;gt; : &amp;lt;span class="hljs-string"&amp;gt;'X'&amp;lt;/span&amp;gt;
  })

  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.myChannel.bind(&amp;lt;span class="hljs-string"&amp;gt;'client-your-turn'&amp;lt;/span&amp;gt;, () =&amp;gt; {
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.turn = &amp;lt;span class="hljs-number"&amp;gt;1&amp;lt;/span&amp;gt;;
  })

  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.myChannel.bind(&amp;lt;span class="hljs-string"&amp;gt;'client-box-update'&amp;lt;/span&amp;gt;, (update) =&amp;gt; {
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes = update;
  })

  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.myChannel.bind(&amp;lt;span class="hljs-string"&amp;gt;'client-you-lost'&amp;lt;/span&amp;gt;, () =&amp;gt; {
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.gameLost();
  })
},

&amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the &lt;code&gt;subscribe&lt;/code&gt; method, we subscribe to our Pusher presence channel, and then subscribe to the private channel for the current user. In the &lt;code&gt;listeners&lt;/code&gt; method we register the listeners for all the events we are expecting to be triggered on the private channel we subscribed to.&lt;/p&gt;

&lt;p&gt;Next, we will add other helper methods to our methods class. Inside the methods class, add the following functions to the bottom after the &lt;code&gt;listeners&lt;/code&gt; method:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;span class="hljs-comment"&amp;gt;// Generates a random string we use as a name for a guest user&amp;lt;/span&amp;gt;
generateRandomName: &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-params"&amp;gt;&amp;lt;/span&amp;gt;) &amp;lt;/span&amp;gt;{
  &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; text = &amp;lt;span class="hljs-string"&amp;gt;''&amp;lt;/span&amp;gt;;
  &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; possible = &amp;lt;span class="hljs-string"&amp;gt;'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'&amp;lt;/span&amp;gt;;
  &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; i = &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;; i &amp;lt; &amp;lt;span class="hljs-number"&amp;gt;6&amp;lt;/span&amp;gt;; i++) {
    text += possible.charAt(&amp;lt;span class="hljs-built_in"&amp;gt;Math&amp;lt;/span&amp;gt;.floor(&amp;lt;span class="hljs-built_in"&amp;gt;Math&amp;lt;/span&amp;gt;.random() * possible.length));
  }
  &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; text;
},

&amp;lt;span class="hljs-comment"&amp;gt;// Lets you choose a player to play as.&amp;lt;/span&amp;gt;
choosePlayer: &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-params"&amp;gt;e&amp;lt;/span&amp;gt;) &amp;lt;/span&amp;gt;{
  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.otherPlayerName = e.target.innerText
  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.otherPlayerChannel = &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.pusher.subscribe(&amp;lt;span class="hljs-string"&amp;gt;'private-'&amp;lt;/span&amp;gt; + &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.otherPlayerName)
  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.otherPlayerChannel.bind(&amp;lt;span class="hljs-string"&amp;gt;'pusher:subscription_succeeded'&amp;lt;/span&amp;gt;, () =&amp;gt; {
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.otherPlayerChannel.trigger(&amp;lt;span class="hljs-string"&amp;gt;'client-'&amp;lt;/span&amp;gt; + &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.otherPlayerName, &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.username)
  });
},

&amp;lt;span class="hljs-comment"&amp;gt;// Begins the game&amp;lt;/span&amp;gt;
startGame: &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-params"&amp;gt;name&amp;lt;/span&amp;gt;) &amp;lt;/span&amp;gt;{
  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.status = &amp;lt;span class="hljs-string"&amp;gt;"Game started with "&amp;lt;/span&amp;gt; + name
  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.$refs.gameboard.classList.remove(&amp;lt;span class="hljs-string"&amp;gt;'invisible'&amp;lt;/span&amp;gt;);
},

&amp;lt;span class="hljs-comment"&amp;gt;// User declined to play&amp;lt;/span&amp;gt;
gameDeclined: &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-params"&amp;gt;&amp;lt;/span&amp;gt;) &amp;lt;/span&amp;gt;{
  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.status = &amp;lt;span class="hljs-string"&amp;gt;"Game declined"&amp;lt;/span&amp;gt;
},

&amp;lt;span class="hljs-comment"&amp;gt;// Game has ended with current user winning&amp;lt;/span&amp;gt;
gameWon: &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-params"&amp;gt;&amp;lt;/span&amp;gt;) &amp;lt;/span&amp;gt;{
  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.status = &amp;lt;span class="hljs-string"&amp;gt;"You WON!"&amp;lt;/span&amp;gt;
  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.$refs.gameboard.classList.add(&amp;lt;span class="hljs-string"&amp;gt;'invisible'&amp;lt;/span&amp;gt;);
  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.restartGame()
},

&amp;lt;span class="hljs-comment"&amp;gt;// Game has ended with current user losing&amp;lt;/span&amp;gt;
gameLost: &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-params"&amp;gt;&amp;lt;/span&amp;gt;) &amp;lt;/span&amp;gt;{
  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.turn = &amp;lt;span class="hljs-number"&amp;gt;1&amp;lt;/span&amp;gt;;
  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes = [&amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;]
  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.status = &amp;lt;span class="hljs-string"&amp;gt;"You LOST!"&amp;lt;/span&amp;gt;
  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.$refs.gameboard.classList.add(&amp;lt;span class="hljs-string"&amp;gt;'invisible'&amp;lt;/span&amp;gt;);
  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.restartGame()
},

&amp;lt;span class="hljs-comment"&amp;gt;// Restarts a game&amp;lt;/span&amp;gt;
restartGame: &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-params"&amp;gt;&amp;lt;/span&amp;gt;) &amp;lt;/span&amp;gt;{
  &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt; (i = &amp;lt;span class="hljs-number"&amp;gt;1&amp;lt;/span&amp;gt;; i &amp;lt; &amp;lt;span class="hljs-number"&amp;gt;10&amp;lt;/span&amp;gt;; i++) {
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.$refs[i].innerText = &amp;lt;span class="hljs-string"&amp;gt;""&amp;lt;/span&amp;gt;
  }
  &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.$refs.gameboard.classList.remove(&amp;lt;span class="hljs-string"&amp;gt;'invisible'&amp;lt;/span&amp;gt;);
},

&amp;lt;span class="hljs-comment"&amp;gt;// Checks tiles to see if the tiles passed are a match&amp;lt;/span&amp;gt;
compare: &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-params"&amp;gt;&amp;lt;/span&amp;gt;) &amp;lt;/span&amp;gt;{
  &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; i = &amp;lt;span class="hljs-number"&amp;gt;1&amp;lt;/span&amp;gt;; i &amp;lt; &amp;lt;span class="hljs-built_in"&amp;gt;arguments&amp;lt;/span&amp;gt;.length; i++) {
    &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-built_in"&amp;gt;arguments&amp;lt;/span&amp;gt;[i] === &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt; || &amp;lt;span class="hljs-built_in"&amp;gt;arguments&amp;lt;/span&amp;gt;[i] !== &amp;lt;span class="hljs-built_in"&amp;gt;arguments&amp;lt;/span&amp;gt;[i - &amp;lt;span class="hljs-number"&amp;gt;1&amp;lt;/span&amp;gt;]) {
      &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-literal"&amp;gt;false&amp;lt;/span&amp;gt;
    }
  }

  &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;;
},

&amp;lt;span class="hljs-comment"&amp;gt;// Checks the tiles and returns true if theres a winning play&amp;lt;/span&amp;gt;
theresAMatch: &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-params"&amp;gt;&amp;lt;/span&amp;gt;) &amp;lt;/span&amp;gt;{
  &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.compare(&amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;], &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;1&amp;lt;/span&amp;gt;], &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;2&amp;lt;/span&amp;gt;]) ||
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.compare(&amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;3&amp;lt;/span&amp;gt;], &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;4&amp;lt;/span&amp;gt;], &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;5&amp;lt;/span&amp;gt;]) ||
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.compare(&amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;6&amp;lt;/span&amp;gt;], &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;7&amp;lt;/span&amp;gt;], &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;8&amp;lt;/span&amp;gt;]) ||
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.compare(&amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;], &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;3&amp;lt;/span&amp;gt;], &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;6&amp;lt;/span&amp;gt;]) ||
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.compare(&amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;1&amp;lt;/span&amp;gt;], &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;4&amp;lt;/span&amp;gt;], &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;7&amp;lt;/span&amp;gt;]) ||
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.compare(&amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;2&amp;lt;/span&amp;gt;], &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;5&amp;lt;/span&amp;gt;], &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;8&amp;lt;/span&amp;gt;]) ||
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.compare(&amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;2&amp;lt;/span&amp;gt;], &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;4&amp;lt;/span&amp;gt;], &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;6&amp;lt;/span&amp;gt;]) ||
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.compare(&amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;], &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;4&amp;lt;/span&amp;gt;], &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[&amp;lt;span class="hljs-number"&amp;gt;8&amp;lt;/span&amp;gt;])
},

&amp;lt;span class="hljs-comment"&amp;gt;// Checks to see if the play was a winning play&amp;lt;/span&amp;gt;
playerAction: &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-params"&amp;gt;e&amp;lt;/span&amp;gt;) &amp;lt;/span&amp;gt;{
  &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; index = e.target.dataset.id - &amp;lt;span class="hljs-number"&amp;gt;1&amp;lt;/span&amp;gt;
  &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; tile = &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.firstPlayer ? &amp;lt;span class="hljs-string"&amp;gt;'X'&amp;lt;/span&amp;gt; : &amp;lt;span class="hljs-string"&amp;gt;'O'&amp;lt;/span&amp;gt;

  &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.turn &amp;amp;&amp;amp; &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[index] == &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;) {
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.turn = &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes[index] = tile
    e.target.innerText = tile

    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.otherPlayerChannel.trigger(&amp;lt;span class="hljs-string"&amp;gt;'client-your-turn'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;""&amp;lt;/span&amp;gt;)
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.otherPlayerChannel.trigger(&amp;lt;span class="hljs-string"&amp;gt;'client-box-update'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes)
    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.otherPlayerChannel.trigger(&amp;lt;span class="hljs-string"&amp;gt;'client-new-move'&amp;lt;/span&amp;gt;, e.target.dataset.id)

    &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.theresAMatch()) {
      &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.gameWon()
      &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.boxes = [&amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;]
      &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.otherPlayerChannel.trigger(&amp;lt;span class="hljs-string"&amp;gt;'client-you-lost'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;''&amp;lt;/span&amp;gt;)
    }
  }
},
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Above, we have added several helper methods that the game needs to function properly and before each method, we have added a comment to show what the method does.&lt;/p&gt;

&lt;p&gt;Let’s test the game now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the game
&lt;/h2&gt;

&lt;p&gt;We can test the game by running this command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ flask run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now if we visit &lt;a href="http://localhost:5000" rel="noopener noreferrer"&gt;localhost:5000&lt;/a&gt;, we should see the connection page and test the game:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/1RGRfjXfgQgYWYy2kqImOs/0c1db39f9371dbb773638ffe57bfd385/python-vue-game-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/1RGRfjXfgQgYWYy2kqImOs/0c1db39f9371dbb773638ffe57bfd385/python-vue-game-demo.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we have learned how to leverage the Pusher SDK in creating an online multiplayer game powered by a Python backend server.&lt;/p&gt;

&lt;p&gt;The source code for this tutorial is available on &lt;a href="https://github.com/neoighodaro/python-pusher-multiplayer-game" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post first appeared on the &lt;a href="https://pusher.com/tutorials/game-python-vue" rel="noopener noreferrer"&gt;Pusher blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>python</category>
      <category>pusher</category>
      <category>vue</category>
      <category>realtime</category>
    </item>
    <item>
      <title>Build an Android messenger app with online presence using Kotlin</title>
      <dc:creator>Neo</dc:creator>
      <pubDate>Tue, 20 Nov 2018 06:46:57 +0000</pubDate>
      <link>https://dev.to/neo/build-an-android-messenger-app-with-online-presence-using-kotlin-34lk</link>
      <guid>https://dev.to/neo/build-an-android-messenger-app-with-online-presence-using-kotlin-34lk</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;You will need Android Studio 3+, Node, npm and MongoDB installed on your machine. Some familiarity with Android development is required.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When building a chat application, it is essential to have an online presence feature. It is essential because your users will like to know when their friends are online and are more likely to respond to their messages.&lt;/p&gt;

&lt;p&gt;In this article, we will be building a messenger app with online presence using Pusher Channels, Kotlin and Node.js.&lt;/p&gt;

&lt;p&gt;Here is a demo of what we will build:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/mmdMFfq7WoYsk26cIKocs/486ce2b458e45a41cfeb01ab4441f3b4/android-messenger-presence-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/mmdMFfq7WoYsk26cIKocs/486ce2b458e45a41cfeb01ab4441f3b4/android-messenger-presence-demo.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Prerequisites
&lt;/h1&gt;

&lt;p&gt;To follow along you need the following requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Pusher Channel app. You can create one &lt;a href="https://pusher.com/channels"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Android Studio installed on your machine. You can check &lt;a href="https://developer.android.com/studio/index.html"&gt;here&lt;/a&gt; for the latest stable version. A minimum of version 3.0 is recommended.&lt;/li&gt;
&lt;li&gt;Basic knowledge of Android development and the Android Studio IDE.&lt;/li&gt;
&lt;li&gt;Basic knowledge of Kotlin. Here are the &lt;a href="https://kotlinlang.org/docs/reference/"&gt;official docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Node.js and NPM (Node Package Manager) installed on your machine. Download &lt;a href="https://nodejs.org/en/"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Mongo DB installed on your machine. You can install it following the instructions &lt;a href="https://docs.mongodb.com/manual/installation/"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building the backend server
&lt;/h2&gt;

&lt;p&gt;Our server will be built using Node.js. To start, create a new project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ mkdir backend-server

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next, create a new &lt;code&gt;index.js&lt;/code&gt; file inside the project directory and paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-comment"&amp;gt;// File: ./index.js&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; express = &amp;lt;span class="hljs-built_in"&amp;gt;require&amp;lt;/span&amp;gt;(&amp;lt;span class="hljs-string"&amp;gt;'express'&amp;lt;/span&amp;gt;);
    &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; bodyParser = &amp;lt;span class="hljs-built_in"&amp;gt;require&amp;lt;/span&amp;gt;(&amp;lt;span class="hljs-string"&amp;gt;'body-parser'&amp;lt;/span&amp;gt;);
    &amp;lt;span class="hljs-keyword"&amp;gt;const&amp;lt;/span&amp;gt; mongoose = &amp;lt;span class="hljs-built_in"&amp;gt;require&amp;lt;/span&amp;gt;(&amp;lt;span class="hljs-string"&amp;gt;'mongoose'&amp;lt;/span&amp;gt;);
    &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; Pusher = &amp;lt;span class="hljs-built_in"&amp;gt;require&amp;lt;/span&amp;gt;(&amp;lt;span class="hljs-string"&amp;gt;'pusher'&amp;lt;/span&amp;gt;);

    &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; app = express();

    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ &amp;lt;span class="hljs-attr"&amp;gt;extended&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-literal"&amp;gt;false&amp;lt;/span&amp;gt; }));

    &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; pusher = &amp;lt;span class="hljs-keyword"&amp;gt;new&amp;lt;/span&amp;gt; Pusher({
      &amp;lt;span class="hljs-attr"&amp;gt;appId&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-string"&amp;gt;'PUSHER_APP_ID'&amp;lt;/span&amp;gt;,
      &amp;lt;span class="hljs-attr"&amp;gt;key&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-string"&amp;gt;'PUSHER_APP_KEY'&amp;lt;/span&amp;gt;,
      &amp;lt;span class="hljs-attr"&amp;gt;secret&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-string"&amp;gt;'PUSHER_APP_SECRET'&amp;lt;/span&amp;gt;,
      &amp;lt;span class="hljs-attr"&amp;gt;cluster&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-string"&amp;gt;'PUSHER_APP_CLUSTER'&amp;lt;/span&amp;gt;
    });

    mongoose.connect(&amp;lt;span class="hljs-string"&amp;gt;'mongodb://127.0.0.1/db'&amp;lt;/span&amp;gt;);

    &amp;lt;span class="hljs-keyword"&amp;gt;const&amp;lt;/span&amp;gt; Schema = mongoose.Schema;
    &amp;lt;span class="hljs-keyword"&amp;gt;const&amp;lt;/span&amp;gt; userSchema = &amp;lt;span class="hljs-keyword"&amp;gt;new&amp;lt;/span&amp;gt; Schema({
        &amp;lt;span class="hljs-attr"&amp;gt;name&amp;lt;/span&amp;gt;: { &amp;lt;span class="hljs-attr"&amp;gt;type&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-built_in"&amp;gt;String&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-attr"&amp;gt;required&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;, },
        &amp;lt;span class="hljs-attr"&amp;gt;count&amp;lt;/span&amp;gt;: {&amp;lt;span class="hljs-attr"&amp;gt;type&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-built_in"&amp;gt;Number&amp;lt;/span&amp;gt;}
    });

    &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; User = mongoose.model(&amp;lt;span class="hljs-string"&amp;gt;'User'&amp;lt;/span&amp;gt;, userSchema);
    userSchema.pre(&amp;lt;span class="hljs-string"&amp;gt;'save'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt;(&amp;lt;span class="hljs-params"&amp;gt;next&amp;lt;/span&amp;gt;) &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.isNew) {
            User.count().then(&amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;res&amp;lt;/span&amp;gt; =&amp;gt;&amp;lt;/span&amp;gt; {
              &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.count = res; &amp;lt;span class="hljs-comment"&amp;gt;// Increment count&amp;lt;/span&amp;gt;
              next();
            });
          } &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
            next();
          }
    });

    &amp;lt;span class="hljs-built_in"&amp;gt;module&amp;lt;/span&amp;gt;.exports = User;

    &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; currentUser;

    &amp;lt;span class="hljs-comment"&amp;gt;/* 
    =================================
    We will add our endpoints here!!!
    =================================
    */&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; port = process.env.PORT || &amp;lt;span class="hljs-number"&amp;gt;5000&amp;lt;/span&amp;gt;;

    app.listen(port);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the snippet above, we initialized Pusher, Express, and MongoDB. We are using &lt;a href="http://mongoosejs.com/"&gt;Moongose&lt;/a&gt; to connect to our MongoDB instance.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Replace the &lt;code&gt;PUSHER_APP_*&lt;/code&gt; keys with the ones on your Pusher dashboard.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now let’s add our endpoints. The first endpoint we will add will be to log a user in. Paste the code below in your &lt;code&gt;index.js&lt;/code&gt; file below the &lt;code&gt;currentUser&lt;/code&gt; declaration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-comment"&amp;gt;// File: ./index.js&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;

    app.post(&amp;lt;span class="hljs-string"&amp;gt;'/login'&amp;lt;/span&amp;gt;, (req,res) =&amp;gt; {
        User.findOne({&amp;lt;span class="hljs-attr"&amp;gt;name&amp;lt;/span&amp;gt;: req.body.name}, (err, user) =&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (err) {
                res.send(&amp;lt;span class="hljs-string"&amp;gt;"Error connecting to database"&amp;lt;/span&amp;gt;);
            }

            &amp;lt;span class="hljs-comment"&amp;gt;// User exists&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (user) {
                currentUser = user;
                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; res.status(&amp;lt;span class="hljs-number"&amp;gt;200&amp;lt;/span&amp;gt;).send(user)
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; newuser = &amp;lt;span class="hljs-keyword"&amp;gt;new&amp;lt;/span&amp;gt; User({&amp;lt;span class="hljs-attr"&amp;gt;name&amp;lt;/span&amp;gt;: req.body.name});

            newuser.save(&amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt;(&amp;lt;span class="hljs-params"&amp;gt;err&amp;lt;/span&amp;gt;) &amp;lt;/span&amp;gt;{
                &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (err) &amp;lt;span class="hljs-keyword"&amp;gt;throw&amp;lt;/span&amp;gt; err;
            });

            currentUser = newuser;
            res.status(&amp;lt;span class="hljs-number"&amp;gt;200&amp;lt;/span&amp;gt;).send(newuser)
        });
    })

    &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This endpoint receives a &lt;code&gt;username&lt;/code&gt; with the request, and either create a new user or returns the data of the existing user.&lt;/p&gt;

&lt;p&gt;Let’s add the next endpoint below the one above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-comment"&amp;gt;// File: ./index.js&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;

    app.get(&amp;lt;span class="hljs-string"&amp;gt;'/users'&amp;lt;/span&amp;gt;, (req,res) =&amp;gt; {
        User.find({}, (err, users) =&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (err) &amp;lt;span class="hljs-keyword"&amp;gt;throw&amp;lt;/span&amp;gt; err;
            res.send(users);
        });
    })

    &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This endpoint above fetches all the users from the database and returns them.&lt;/p&gt;

&lt;p&gt;Since we will be using a Pusher presence channel, we need an endpoint to authenticate the user. In the same file, paste this code below the endpoint above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-comment"&amp;gt;// File: ./index.js&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;

    app.post(&amp;lt;span class="hljs-string"&amp;gt;'/pusher/auth/presence'&amp;lt;/span&amp;gt;, (req, res) =&amp;gt; {
        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; socketId = req.body.socket_id;
        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; channel = req.body.channel_name;

        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; presenceData = {
            &amp;lt;span class="hljs-attr"&amp;gt;user_id&amp;lt;/span&amp;gt;: currentUser._id,
            &amp;lt;span class="hljs-attr"&amp;gt;user_info&amp;lt;/span&amp;gt;: {&amp;lt;span class="hljs-attr"&amp;gt;count&amp;lt;/span&amp;gt;: currentUser.count, &amp;lt;span class="hljs-attr"&amp;gt;name&amp;lt;/span&amp;gt;: currentUser.name}
        };

        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; auth = pusher.authenticate(socketId, channel, presenceData);

        res.send(auth);
    });

    &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Since we are going to be using private channels, we need an endpoint for authentication. Add the following endpoint below the endpoint above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-comment"&amp;gt;// File: ./index.js&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;

    app.post(&amp;lt;span class="hljs-string"&amp;gt;'/pusher/auth/private'&amp;lt;/span&amp;gt;, (req, res) =&amp;gt; {
        res.send(pusher.authenticate(req.body.socket_id, req.body.channel_name));
    });

    &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;

Finally, the last endpoint will be to trigger an event &amp;lt;span class="hljs-string"&amp;gt;`new-message`&amp;lt;/span&amp;gt; to a channel. Add the endpoint below the last one:


    &amp;lt;span class="hljs-comment"&amp;gt;// File: ./index.js&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;

    app.post(&amp;lt;span class="hljs-string"&amp;gt;'/send-message'&amp;lt;/span&amp;gt;, (req, res) =&amp;gt; {
        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; payload = {&amp;lt;span class="hljs-attr"&amp;gt;message&amp;lt;/span&amp;gt;: req.body.message, &amp;lt;span class="hljs-attr"&amp;gt;sender_id&amp;lt;/span&amp;gt;: req.body.sender_id}
        pusher.trigger(req.body.channel_name, &amp;lt;span class="hljs-string"&amp;gt;'new-message'&amp;lt;/span&amp;gt;, payload);
        res.send(&amp;lt;span class="hljs-number"&amp;gt;200&amp;lt;/span&amp;gt;);
    });

    &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After adding all the endpoints, install the necessary NPM packages by running this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ npm install express body-parser mongoose pusher

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Before you run your application, make sure MongoDB is running already using this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ mongod --dbpath C:\MongoDB\data\db # Windows
    $ mongod --dbpath=/path/to/db/directory # Mac or Linux

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now you can run your application using the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ node index.js

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Your app will be available here: &lt;a href="http://localhost:5000"&gt;http://localhost:5000&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building our Android application
&lt;/h2&gt;

&lt;p&gt;Create your Android project. In the wizard, enter your project name, let’s say &lt;strong&gt;MessengerApp.&lt;/strong&gt; Next, enter your package name. You can use a minimum SDK of 19 then choose an &lt;strong&gt;Empty Activity&lt;/strong&gt;. On the next page, change the &lt;strong&gt;Activity Name&lt;/strong&gt; to &lt;code&gt;LoginActivity&lt;/code&gt;. After this, Android Studio will build your project for you.&lt;/p&gt;

&lt;p&gt;Now that we have the project, let’s add the required dependencies for our app. Open your app module &lt;code&gt;build.gradle&lt;/code&gt; file and add these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // File ../app/build.gradle
    dependencies {
      // [...]

      implementation 'com.android.support:design:28+'
      implementation 'com.pusher:pusher-java-client:1.6.0'
      implementation "com.squareup.retrofit2:retrofit:2.4.0"
      implementation "com.squareup.retrofit2:converter-scalars:2.4.0"
      implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
    }

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Notably, we added the dependencies for &lt;a href="http://square.github.io/retrofit/"&gt;Retrofit&lt;/a&gt; and Pusher. Retrofit is an HTTP client library used for network calls. We added the design library dependency too as we want to use some classes from it. Sync your gradle files to pull in the dependencies.&lt;/p&gt;

&lt;p&gt;Next, let’s prepare our app to make network calls. Retrofit requires an interface to know the endpoints to be accessed.&lt;/p&gt;

&lt;p&gt;Create a new interface named &lt;code&gt;ApiService&lt;/code&gt; and paste this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-comment"&amp;gt;// File: ./app/src/main/java/com/example/messengerapp/ApiService.kt&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; okhttp3.RequestBody
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; retrofit2.Call
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; retrofit2.http.Body
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; retrofit2.http.GET
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; retrofit2.http.POST

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;interface&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;ApiService&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{

      &amp;lt;span class="hljs-meta"&amp;gt;@POST(&amp;lt;span class="hljs-meta-string"&amp;gt;"/login"&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;login&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-meta"&amp;gt;@Body&amp;lt;/span&amp;gt; body:&amp;lt;span class="hljs-type"&amp;gt;RequestBody&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;: Call&amp;lt;UserModel&amp;gt;

      &amp;lt;span class="hljs-meta"&amp;gt;@POST(&amp;lt;span class="hljs-meta-string"&amp;gt;"/send-message"&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;sendMessage&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-meta"&amp;gt;@Body&amp;lt;/span&amp;gt; body:&amp;lt;span class="hljs-type"&amp;gt;RequestBody&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;: Call&amp;lt;String&amp;gt;

      &amp;lt;span class="hljs-meta"&amp;gt;@GET(&amp;lt;span class="hljs-meta-string"&amp;gt;"/users"&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;getUsers&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;: Call&amp;lt;List&amp;lt;UserModel&amp;gt;&amp;gt;
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here, we have declared three endpoints. They are for logging in, sending messages and fetching users. Notice that in some of our responses, we return &lt;code&gt;Call&amp;lt;UserModel&amp;gt;&lt;/code&gt;. Let’s create the &lt;code&gt;UserModel&lt;/code&gt;. Create a new class called &lt;code&gt;UserModel&lt;/code&gt; and paste the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-comment"&amp;gt;// File: ./app/src/main/java/com/example/messengerapp/UserModel.kt&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; com.google.gson.annotations.Expose
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; com.google.gson.annotations.SerializedName

    &amp;lt;span class="hljs-keyword"&amp;gt;data&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;UserModel&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;(&amp;lt;span class="hljs-meta"&amp;gt;@SerializedName(&amp;lt;span class="hljs-meta-string"&amp;gt;"_id"&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-meta"&amp;gt;@Expose&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; id: String,
                         &amp;lt;span class="hljs-meta"&amp;gt;@SerializedName(&amp;lt;span class="hljs-meta-string"&amp;gt;"name"&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-meta"&amp;gt;@Expose&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; name: String,
                         &amp;lt;span class="hljs-meta"&amp;gt;@SerializedName(&amp;lt;span class="hljs-meta-string"&amp;gt;"count"&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-meta"&amp;gt;@Expose&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; count: &amp;lt;span class="hljs-built_in"&amp;gt;Int&amp;lt;/span&amp;gt;,
                         &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; online:&amp;lt;span class="hljs-built_in"&amp;gt;Boolean&amp;lt;/span&amp;gt; = &amp;lt;span class="hljs-literal"&amp;gt;false&amp;lt;/span&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Above, we used a data class so that some other functions required for model classes such as &lt;code&gt;toString&lt;/code&gt;, &lt;code&gt;hashCode&lt;/code&gt; are added to the class by default.&lt;/p&gt;

&lt;p&gt;We are expecting only the values for the &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;name&lt;/code&gt; from the server. We added the &lt;code&gt;online&lt;/code&gt; property so we can update later on.&lt;/p&gt;

&lt;p&gt;Next, create a new class named &lt;code&gt;RetrofitInstance&lt;/code&gt; and paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-comment"&amp;gt;// File: ./app/src/main/java/com/example/messengerapp/RetrofitInstance.kt&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; okhttp3.OkHttpClient
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; retrofit2.Retrofit
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; retrofit2.converter.gson.GsonConverterFactory
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; retrofit2.converter.scalars.ScalarsConverterFactory

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;RetrofitInstance&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{

      &amp;lt;span class="hljs-keyword"&amp;gt;companion&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;object&amp;lt;/span&amp;gt; {
        &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; retrofit: ApiService &amp;lt;span class="hljs-keyword"&amp;gt;by&amp;lt;/span&amp;gt; lazy {
          &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; httpClient = OkHttpClient.Builder()
          &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; builder = Retrofit.Builder()
              .baseUrl(&amp;lt;span class="hljs-string"&amp;gt;"http://10.0.2.2:5000/"&amp;lt;/span&amp;gt;)
              .addConverterFactory(ScalarsConverterFactory.create())
              .addConverterFactory(GsonConverterFactory.create())

          &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; retrofit = builder
              .client(httpClient.build())
              .build()
          retrofit.create(ApiService::&amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-title"&amp;gt;java&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;
        }
      }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This class contains a class variable called &lt;code&gt;retrofit&lt;/code&gt;. It provides us with an instance for Retrofit that we will reference in more than one class.&lt;/p&gt;

&lt;p&gt;Finally, to request for the internet access permission update the &lt;code&gt;AndroidManifest.xml&lt;/code&gt; file like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-comment"&amp;gt;// File: ./app/src/main/ApiService.kt&amp;lt;/span&amp;gt;
    &amp;lt;manifest xmlns:android=&amp;lt;span class="hljs-string"&amp;gt;"http://schemas.android.com/apk/res/android"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-keyword"&amp;gt;package&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"com.example.messengerapp"&amp;lt;/span&amp;gt;&amp;gt;

      &amp;lt;uses-permission android:name=&amp;lt;span class="hljs-string"&amp;gt;"android.permission.INTERNET"&amp;lt;/span&amp;gt; /&amp;gt;
      [...]

    &amp;lt;/manifest&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we can make requests using Retrofit.&lt;/p&gt;

&lt;p&gt;The next feature we will implement is login. Open the already created &lt;code&gt;LoginActivity&lt;/code&gt; layout file &lt;code&gt;activity_login.xml&lt;/code&gt; file and paste this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // File: ./app/src/main/res/layout/activity_login.xml
    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;android.support.constraint.ConstraintLayout&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;xmlns:android&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"http://schemas.android.com/apk/res/android"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;xmlns:app&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"http://schemas.android.com/apk/res-auto"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;xmlns:tools&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"http://schemas.android.com/tools"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;android:layout_width&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"match_parent"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;android:layout_height&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"match_parent"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;android:layout_margin&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"20dp"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;tools:context&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;".LoginActivity"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;

      &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;EditText&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"@+id/editTextUsername"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_width&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"match_parent"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_height&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"wrap_content"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;app:layout_constraintBottom_toBottomOf&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"parent"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;app:layout_constraintLeft_toLeftOf&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"parent"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;app:layout_constraintRight_toRightOf&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"parent"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;app:layout_constraintTop_toTopOf&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"parent"&amp;lt;/span&amp;gt; /&amp;gt;&amp;lt;/span&amp;gt;

      &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;Button&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"@+id/loginButton"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_width&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"match_parent"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_height&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"wrap_content"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:text&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"Login"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;app:layout_constraintTop_toBottomOf&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"@+id/editTextUsername"&amp;lt;/span&amp;gt; /&amp;gt;&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;android.support.constraint.ConstraintLayout&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This layout contains an input field to take the username and a button to make a login request.&lt;/p&gt;

&lt;p&gt;Next, open the &lt;code&gt;LoginActivity.Kt&lt;/code&gt; file and paste this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-comment"&amp;gt;// File: ./app/src/main/java/com/example/messengerapp/LoginActivity.kt&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.content.Intent
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.os.Bundle
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.support.v7.app.AppCompatActivity
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.util.Log
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; kotlinx.android.synthetic.main.activity_login.*
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; okhttp3.MediaType
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; okhttp3.RequestBody
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; org.json.JSONObject
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; retrofit2.Call
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; retrofit2.Callback
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; retrofit2.Response

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;LoginActivity&amp;lt;/span&amp;gt; : &amp;lt;span class="hljs-type"&amp;gt;AppCompatActivity&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;() {

      &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;onCreate&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(savedInstanceState: &amp;lt;span class="hljs-type"&amp;gt;Bundle&amp;lt;/span&amp;gt;?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
        &amp;lt;span class="hljs-keyword"&amp;gt;super&amp;lt;/span&amp;gt;.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)
        loginButton.setOnClickListener {
          &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (editTextUsername.text.isNotEmpty()) {
            loginFunction(editTextUsername.text.toString())
          }
        }
      }

      &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;loginFunction&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(name:&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
        &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; jsonObject = JSONObject()
        jsonObject.put(&amp;lt;span class="hljs-string"&amp;gt;"name"&amp;lt;/span&amp;gt;, name)

        &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; jsonBody = RequestBody.create(
            MediaType.parse(&amp;lt;span class="hljs-string"&amp;gt;"application/json; charset=utf-8"&amp;lt;/span&amp;gt;), 
            jsonObject.toString()
        )

        RetrofitInstance.retrofit.login(jsonBody).enqueue(&amp;lt;span class="hljs-keyword"&amp;gt;object&amp;lt;/span&amp;gt;:Callback&amp;lt;UserModel&amp;gt; {
          &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;onFailure&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(call: &amp;lt;span class="hljs-type"&amp;gt;Call&amp;lt;/span&amp;gt;&amp;lt;&amp;lt;span class="hljs-type"&amp;gt;UserModel&amp;lt;/span&amp;gt;&amp;gt;?, t: &amp;lt;span class="hljs-type"&amp;gt;Throwable&amp;lt;/span&amp;gt;?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            Log.i(&amp;lt;span class="hljs-string"&amp;gt;"LoginActivity"&amp;lt;/span&amp;gt;,t!!.localizedMessage)
          }

          &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;onResponse&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(call: &amp;lt;span class="hljs-type"&amp;gt;Call&amp;lt;/span&amp;gt;&amp;lt;&amp;lt;span class="hljs-type"&amp;gt;UserModel&amp;lt;/span&amp;gt;&amp;gt;?, response: &amp;lt;span class="hljs-type"&amp;gt;Response&amp;lt;/span&amp;gt;&amp;lt;&amp;lt;span class="hljs-type"&amp;gt;UserModel&amp;lt;/span&amp;gt;&amp;gt;?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (response!!.code() == &amp;lt;span class="hljs-number"&amp;gt;200&amp;lt;/span&amp;gt;) {
              Singleton.getInstance().currentUser = response.body()!!
              startActivity(Intent(&amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-symbol"&amp;gt;@LoginActivity&amp;lt;/span&amp;gt;,ContactListActivity::&amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-title"&amp;gt;java&amp;lt;/span&amp;gt;))&amp;lt;/span&amp;gt;
              finish()
            }
          }
        })
      }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the file, we set up a listener for our login button so that when it is clicked, we can send the text to the server for authentication. We also stored the logged in user in a singleton class so that we can access the user’s details later.&lt;/p&gt;

&lt;p&gt;Create a new class called &lt;code&gt;Singleton&lt;/code&gt; and paste this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-comment"&amp;gt;// File: ./app/src/main/java/com/example/messengerapp/RetrofitInstance.kt&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Singleton&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
      &amp;lt;span class="hljs-keyword"&amp;gt;companion&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;object&amp;lt;/span&amp;gt; {
        &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; ourInstance = Singleton()
        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;getInstance&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;: Singleton {
          &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; ourInstance
        }
      }
      &amp;lt;span class="hljs-keyword"&amp;gt;lateinit&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; currentUser: UserModel
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With this class, we will have access to the &lt;code&gt;currentUser&lt;/code&gt;, which is the logged in user.&lt;/p&gt;

&lt;p&gt;Next, let’s create a new activity named &lt;code&gt;ContactListActivity&lt;/code&gt;. For now, leave the class empty and open the corresponding layout file named &lt;code&gt;activity_contact_list&lt;/code&gt; and paste the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // File: ./app/src/main/res/layout/activity_contact_list.xml
    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;android.support.constraint.ConstraintLayout&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;xmlns:android&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"http://schemas.android.com/apk/res/android"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;xmlns:app&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"http://schemas.android.com/apk/res-auto"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;xmlns:tools&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"http://schemas.android.com/tools"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;android:layout_width&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"match_parent"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;android:layout_height&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"match_parent"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;tools:context&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;".ContactListActivity"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;

      &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;android.support.v7.widget.RecyclerView&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_width&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"match_parent"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"@+id/recyclerViewUserList"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_height&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"match_parent"&amp;lt;/span&amp;gt;/&amp;gt;&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;android.support.constraint.ConstraintLayout&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The layout contains a recycler view, which will give us all the list of our contacts fetched from the database. Since we are displaying items in a list, we will need an adapter class to manage how items are inflated to the layout.&lt;/p&gt;

&lt;p&gt;Create a new class named &lt;code&gt;ContactRecyclerAdapter&lt;/code&gt; and paste this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-comment"&amp;gt;// File: ./app/src/main/java/com/example/messengerapp/ContactRecyclerAdapter.kt&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.support.v7.widget.RecyclerView
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.view.LayoutInflater
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.view.View
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.view.ViewGroup
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.widget.ImageView
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.widget.TextView
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; java.util.*

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;ContactRecyclerAdapter&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;(&amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; list: ArrayList&amp;lt;UserModel&amp;gt;, &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; listener: UserClickListener)
      : RecyclerView.Adapter&amp;lt;ContactRecyclerAdapter.ViewHolder&amp;gt;() {

      &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;onCreateViewHolder&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(parent: &amp;lt;span class="hljs-type"&amp;gt;ViewGroup&amp;lt;/span&amp;gt;, viewType: &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;: ViewHolder {
        &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; ViewHolder(LayoutInflater.from(parent.context)
            .inflate(R.layout.user_list_row, parent, &amp;lt;span class="hljs-literal"&amp;gt;false&amp;lt;/span&amp;gt;))
      }

      &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;onBindViewHolder&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(holder: &amp;lt;span class="hljs-type"&amp;gt;ViewHolder&amp;lt;/span&amp;gt;, position: &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; = holder.bind(list[position])

      &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;getItemCount&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-built_in"&amp;gt;Int&amp;lt;/span&amp;gt; = list.size

      &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;showUserOnline&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(updatedUser: &amp;lt;span class="hljs-type"&amp;gt;UserModel&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
        list.forEachIndexed { index, element -&amp;gt;
          &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (updatedUser.id == element.id) {
            updatedUser.online = &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;
            list[index] = updatedUser
            notifyItemChanged(index)
          }

        }
      }

      &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;showUserOffline&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(updatedUser: &amp;lt;span class="hljs-type"&amp;gt;UserModel&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
        list.forEachIndexed { index, element -&amp;gt;
          &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (updatedUser.id == element.id) {
            updatedUser.online = &amp;lt;span class="hljs-literal"&amp;gt;false&amp;lt;/span&amp;gt;
            list[index] = updatedUser
            notifyItemChanged(index)
          }
        }
      }

      &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;add&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(user: &amp;lt;span class="hljs-type"&amp;gt;UserModel&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
        list.add(user)
        notifyDataSetChanged()
      }

      &amp;lt;span class="hljs-keyword"&amp;gt;inner&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;ViewHolder&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;(itemView: View) : RecyclerView.ViewHolder(itemView) {
        &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; nameTextView: TextView = itemView.findViewById(R.id.usernameTextView)
        &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; presenceImageView: ImageView = itemView.findViewById(R.id.presenceImageView)

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;bind&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(currentValue: &amp;lt;span class="hljs-type"&amp;gt;UserModel&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; = with(itemView) {
          &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.setOnClickListener {
            listener.onUserClicked(currentValue)
          }
          nameTextView.text = currentValue.name
          &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (currentValue.online){
            presenceImageView.setImageDrawable(&amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.context.resources.getDrawable(R.drawable.presence_icon_online))
          } &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
            presenceImageView.setImageDrawable(&amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.context.resources.getDrawable(R.drawable.presence_icon))

          }

        }
      }

      &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;interface&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;UserClickListener&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;onUserClicked&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(user: &amp;lt;span class="hljs-type"&amp;gt;UserModel&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
      }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This adapter has some overridden methods and some custom methods.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;onCreateViewHolder&lt;/code&gt; inflates how each row will look like. &lt;code&gt;onBindViewHolder&lt;/code&gt; binds the data to each item by calling the &lt;code&gt;bind&lt;/code&gt; method in the inner &lt;code&gt;ViewHolder&lt;/code&gt; class. The &lt;code&gt;getItemCount&lt;/code&gt; gives the size of the list.&lt;/p&gt;

&lt;p&gt;For our custom methods, &lt;code&gt;showUserOffline&lt;/code&gt; updates the user and shows when they are offline. While &lt;code&gt;showUserOnline&lt;/code&gt; does the opposite. Finally, we have the &lt;code&gt;add&lt;/code&gt; method, which adds a new contact to the list and refreshes it.&lt;/p&gt;

&lt;p&gt;In the adapter class above, we used a new layout named &lt;code&gt;user_list_row&lt;/code&gt;. Create a new layout &lt;code&gt;user_list_row&lt;/code&gt; and paste this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // File: ./app/src/main/res/layout/user_list_row.xml
    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;LinearLayout&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;android:orientation&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"horizontal"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;xmlns:android&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"http://schemas.android.com/apk/res/android"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;xmlns:app&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"http://schemas.android.com/apk/res-auto"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;xmlns:tools&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"http://schemas.android.com/tools"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;android:layout_width&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"match_parent"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;android:layout_height&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"wrap_content"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;android:layout_margin&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"20dp"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;android:gravity&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"center"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;tools:context&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;".LoginActivity"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;

      &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;ImageView&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"@+id/presenceImageView"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_width&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"15dp"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_height&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"15dp"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;app:srcCompat&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"@drawable/presence_icon"&amp;lt;/span&amp;gt; /&amp;gt;&amp;lt;/span&amp;gt;

      &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;TextView&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_width&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"match_parent"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_height&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"wrap_content"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;tools:text&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"Neo"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:textSize&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"20sp"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_marginStart&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"10dp"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"@+id/usernameTextView"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;app:layout_constraintTop_toBottomOf&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"@+id/editTextUsername"&amp;lt;/span&amp;gt;
        /&amp;gt;&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;LinearLayout&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This layout is the visual representation of how each item on the layout will look like. The layout has an image view that shows the users online status. The layout also has a textview that shows the name of the contact beside the icon. The icons are vector drawables. Let’s create the files.&lt;/p&gt;

&lt;p&gt;Create a new drawable named &lt;code&gt;presence_icon_online&lt;/code&gt; and paste this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // File: ./app/src/main/res/drawable/presence_icon_online.xml
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;vector&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;android:height&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"24dp"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;android:tint&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"#3FFC3C"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:viewportHeight&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"24.0"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;android:viewportWidth&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"24.0"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:width&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"24dp"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;xmlns:android&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"http://schemas.android.com/apk/res/android"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;path&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;android:fillColor&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"#FF000000"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;android:pathData&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2z"&amp;lt;/span&amp;gt;/&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;vector&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Create another drawable named &lt;code&gt;presence_icon&lt;/code&gt; and paste this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // File: ./app/src/main/res/drawable/presence_icon.xml
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;vector&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;android:height&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"24dp"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;android:tint&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"#C0C0C6"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:viewportHeight&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"24.0"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;android:viewportWidth&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"24.0"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:width&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"24dp"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;xmlns:android&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"http://schemas.android.com/apk/res/android"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;path&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;android:fillColor&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"#FF000000"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;android:pathData&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2z"&amp;lt;/span&amp;gt;/&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;vector&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next, open the &lt;code&gt;ContactListActivity&lt;/code&gt; class and paste this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-comment"&amp;gt;// File: ./app/src/main/java/com/example/messengerapp/ContactListActivity.kt&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.content.Intent
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.os.Bundle
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.support.v7.app.AppCompatActivity
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.support.v7.widget.LinearLayoutManager
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.util.Log
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; com.pusher.client.Pusher
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; com.pusher.client.PusherOptions
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; com.pusher.client.channel.PresenceChannelEventListener
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; com.pusher.client.channel.User
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; com.pusher.client.util.HttpAuthorizer
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; kotlinx.android.synthetic.main.activity_contact_list.*
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; retrofit2.Call
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; retrofit2.Callback
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; retrofit2.Response

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;ContactListActivity&amp;lt;/span&amp;gt; : &amp;lt;span class="hljs-type"&amp;gt;AppCompatActivity&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;(),
        ContactRecyclerAdapter.UserClickListener {

      &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; mAdapter = ContactRecyclerAdapter(ArrayList(), &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;)

      &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;onCreate&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(savedInstanceState: &amp;lt;span class="hljs-type"&amp;gt;Bundle&amp;lt;/span&amp;gt;?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
        &amp;lt;span class="hljs-keyword"&amp;gt;super&amp;lt;/span&amp;gt;.onCreate(savedInstanceState)
        setContentView(R.layout.activity_contact_list)
        setupRecyclerView()
        fetchUsers()
        subscribeToChannel()
      }

    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In this class, we initialized the &lt;code&gt;ContactRecyclerAdapter&lt;/code&gt;, then called three functions in the &lt;code&gt;onCreate&lt;/code&gt; method. Let’s create these new functions.&lt;/p&gt;

&lt;p&gt;In the same class, add the following methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;setupRecyclerView&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
      with(recyclerViewUserList) {
        layoutManager = LinearLayoutManager(&amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-symbol"&amp;gt;@ContactListActivity&amp;lt;/span&amp;gt;)
        adapter = mAdapter
      }
    }

    &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;fetchUsers&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
      RetrofitInstance.retrofit.getUsers().enqueue(&amp;lt;span class="hljs-keyword"&amp;gt;object&amp;lt;/span&amp;gt; : Callback&amp;lt;List&amp;lt;UserModel&amp;gt;&amp;gt; {
        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;onFailure&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(call: &amp;lt;span class="hljs-type"&amp;gt;Call&amp;lt;/span&amp;gt;&amp;lt;&amp;lt;span class="hljs-type"&amp;gt;List&amp;lt;/span&amp;gt;&amp;lt;&amp;lt;span class="hljs-type"&amp;gt;UserModel&amp;lt;/span&amp;gt;&amp;gt;&amp;gt;?, t: &amp;lt;span class="hljs-type"&amp;gt;Throwable&amp;lt;/span&amp;gt;?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {}
        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;onResponse&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(call: &amp;lt;span class="hljs-type"&amp;gt;Call&amp;lt;/span&amp;gt;&amp;lt;&amp;lt;span class="hljs-type"&amp;gt;List&amp;lt;/span&amp;gt;&amp;lt;&amp;lt;span class="hljs-type"&amp;gt;UserModel&amp;lt;/span&amp;gt;&amp;gt;&amp;gt;?, response: &amp;lt;span class="hljs-type"&amp;gt;Response&amp;lt;/span&amp;gt;&amp;lt;&amp;lt;span class="hljs-type"&amp;gt;List&amp;lt;/span&amp;gt;&amp;lt;&amp;lt;span class="hljs-type"&amp;gt;UserModel&amp;lt;/span&amp;gt;&amp;gt;&amp;gt;?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
          &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt; (user &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt; response!!.body()!!) {
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (user.id != Singleton.getInstance().currentUser.id) {
              mAdapter.add(user)
            }
          }
        }
      })
    }

    &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;subscribeToChannel&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {

      &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; authorizer = HttpAuthorizer(&amp;lt;span class="hljs-string"&amp;gt;"http://10.0.2.2:5000/pusher/auth/presence"&amp;lt;/span&amp;gt;)
      &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; options = PusherOptions().setAuthorizer(authorizer)
      options.setCluster(&amp;lt;span class="hljs-string"&amp;gt;"PUSHER_APP_CLUSTER"&amp;lt;/span&amp;gt;)

      &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; pusher = Pusher(&amp;lt;span class="hljs-string"&amp;gt;"PUSHER_APP_KEY"&amp;lt;/span&amp;gt;, options)
      pusher.connect()

      pusher.subscribePresence(&amp;lt;span class="hljs-string"&amp;gt;"presence-channel"&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-keyword"&amp;gt;object&amp;lt;/span&amp;gt; : PresenceChannelEventListener {
        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;onUsersInformationReceived&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(p0: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;?, users: &amp;lt;span class="hljs-type"&amp;gt;MutableSet&amp;lt;/span&amp;gt;&amp;lt;&amp;lt;span class="hljs-type"&amp;gt;User&amp;lt;/span&amp;gt;&amp;gt;?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
          &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt; (user &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt; users!!) {
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (user.id!=Singleton.getInstance().currentUser.id){
              runOnUiThread {
                mAdapter.showUserOnline(user.toUserModel())
              }
            }
          }
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;onEvent&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(p0: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;?, p1: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;?, p2: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {}
        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;onAuthenticationFailure&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(p0: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;?, p1: &amp;lt;span class="hljs-type"&amp;gt;Exception&amp;lt;/span&amp;gt;?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {}
        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;onSubscriptionSucceeded&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(p0: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {}

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;userSubscribed&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(channelName: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;, user: &amp;lt;span class="hljs-type"&amp;gt;User&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
          runOnUiThread {
            mAdapter.showUserOnline(user.toUserModel())
          }
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;userUnsubscribed&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(channelName: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;, user: &amp;lt;span class="hljs-type"&amp;gt;User&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
          runOnUiThread {
            mAdapter.showUserOffline(user.toUserModel())
          }
        }
      })
    }

    &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;onUserClicked&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(user: &amp;lt;span class="hljs-type"&amp;gt;UserModel&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
      &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; intent = Intent(&amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;, ChatRoom::&amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-title"&amp;gt;java&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;
      intent.putExtra(ChatRoom.EXTRA_ID,user.id)
      intent.putExtra(ChatRoom.EXTRA_NAME,user.name)
      intent.putExtra(ChatRoom.EXTRA_COUNT,user.count)
      startActivity(intent)
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Replace the &lt;code&gt;PUSHER_APP_*&lt;/code&gt; keys with the values on your dashboard.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;setupRecyclerView&lt;/code&gt; assigns a layout manager and an adapter to the recycler view. For a recycler view to work, you need these two things.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fetchUsers&lt;/code&gt; fetches all the users from the server and displays on the list. It exempts the current user logged in.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;subcribeToChannel&lt;/code&gt; subscribes to a presence channel. When you subscribe to one, the &lt;code&gt;onUsersInformationReceived&lt;/code&gt; gives you all the users subscribed to the channel including the current user. So, in that callback, we call the &lt;code&gt;showUserOnline&lt;/code&gt; method in the adapter class so that the icon beside the user can be changed to signify that the user is online.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;onUserClicked&lt;/code&gt; is called when a contact is selected. We pass the details of the user to the next activity called &lt;code&gt;ChatRoom&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the previous snippet, we used an extension function to transform the &lt;code&gt;User&lt;/code&gt; object we receive from Pusher to our own &lt;code&gt;UserModel&lt;/code&gt; object. Let’s define this extension.&lt;/p&gt;

&lt;p&gt;Create a new class called &lt;code&gt;Utils&lt;/code&gt; and paste this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-comment"&amp;gt;// File: ./app/src/main/java/com/example/messengerapp/Utils.kt&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; com.pusher.client.channel.User
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; org.json.JSONObject

    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; User.&amp;lt;span class="hljs-title"&amp;gt;toUserModel&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;:UserModel{
      &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; jsonObject = JSONObject(&amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.info)
      &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; name = jsonObject.getString(&amp;lt;span class="hljs-string"&amp;gt;"name"&amp;lt;/span&amp;gt;)
      &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; numb = jsonObject.getInt(&amp;lt;span class="hljs-string"&amp;gt;"count"&amp;lt;/span&amp;gt;)
      &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; UserModel(&amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.id, name, numb)
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, since we referenced a &lt;code&gt;ChatRoom&lt;/code&gt; activity earlier in the &lt;code&gt;onUserClicked&lt;/code&gt; method, let’s create it.&lt;/p&gt;

&lt;p&gt;Create a new activity called &lt;code&gt;ChatRoom&lt;/code&gt;. The activity comes with a layout file &lt;code&gt;activity_chat_room&lt;/code&gt;, paste this in the layout file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // File: ./app/src/main/res/layout/activity_chat_room.xml
    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;android.support.constraint.ConstraintLayout&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;xmlns:android&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"http://schemas.android.com/apk/res/android"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;xmlns:app&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"http://schemas.android.com/apk/res-auto"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;xmlns:tools&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"http://schemas.android.com/tools"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;android:layout_width&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"match_parent"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;android:layout_height&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"match_parent"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;tools:context&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;".ChatRoom"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;

      &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;android.support.v7.widget.RecyclerView&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"@+id/recyclerViewChat"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_width&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"match_parent"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_height&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"match_parent"&amp;lt;/span&amp;gt; /&amp;gt;&amp;lt;/span&amp;gt;

      &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;EditText&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"@+id/editText"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_width&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"0dp"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_height&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"wrap_content"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_margin&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"16dp"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:hint&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"Enter a message"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;app:layout_constraintBottom_toBottomOf&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"parent"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;app:layout_constraintEnd_toStartOf&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"@+id/sendButton"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;app:layout_constraintStart_toStartOf&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"parent"&amp;lt;/span&amp;gt; /&amp;gt;&amp;lt;/span&amp;gt;

      &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;android.support.design.widget.FloatingActionButton&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"@+id/sendButton"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_width&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"wrap_content"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_height&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"wrap_content"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_gravity&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"end|bottom"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_margin&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"16dp"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:src&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"@android:drawable/ic_menu_send"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;app:layout_constraintEnd_toEndOf&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"parent"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;app:layout_constraintBottom_toBottomOf&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"parent"&amp;lt;/span&amp;gt; /&amp;gt;&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;android.support.constraint.ConstraintLayout&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The layout above contains a recycler view for the chat messages, an edit text to collect new messages, and a floating action button to send the message.&lt;/p&gt;

&lt;p&gt;Next, create a new class called &lt;code&gt;ChatRoomAdapter&lt;/code&gt; and paste the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-comment"&amp;gt;// File: ./app/src/main/java/com/example/messengerapp/ChatRoomAdapter.kt&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.support.v7.widget.CardView
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.support.v7.widget.RecyclerView
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.view.LayoutInflater
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.view.View
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.view.ViewGroup
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.widget.RelativeLayout
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.widget.TextView
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; java.util.*

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;ChatRoomAdapter&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;(&amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; list: ArrayList&amp;lt;MessageModel&amp;gt;)
      : RecyclerView.Adapter&amp;lt;ChatRoomAdapter.ViewHolder&amp;gt;() {

      &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;onCreateViewHolder&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(parent: &amp;lt;span class="hljs-type"&amp;gt;ViewGroup&amp;lt;/span&amp;gt;, viewType: &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;: ViewHolder {
        &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; ViewHolder(LayoutInflater.from(parent.context)
            .inflate(R.layout.chat_item, parent, &amp;lt;span class="hljs-literal"&amp;gt;false&amp;lt;/span&amp;gt;))
      }

      &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;onBindViewHolder&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(holder: &amp;lt;span class="hljs-type"&amp;gt;ViewHolder&amp;lt;/span&amp;gt;, position: &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; = holder.bind(list[position])

      &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;getItemCount&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-built_in"&amp;gt;Int&amp;lt;/span&amp;gt; = list.size

      &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;add&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(message: &amp;lt;span class="hljs-type"&amp;gt;MessageModel&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
        list.add(message)
        notifyDataSetChanged()
      }

      &amp;lt;span class="hljs-keyword"&amp;gt;inner&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;ViewHolder&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;(itemView: View) : RecyclerView.ViewHolder(itemView) {
        &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; messageTextView: TextView = itemView.findViewById(R.id.text)
        &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; cardView: CardView = itemView.findViewById(R.id.cardView)

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;bind&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(message: &amp;lt;span class="hljs-type"&amp;gt;MessageModel&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; = with(itemView) {
          messageTextView.text = message.message
          &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; params = cardView.layoutParams &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt; RelativeLayout.LayoutParams
          &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (message.senderId==Singleton.getInstance().currentUser.id) {
            params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
          }
        }
      }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This adapter works in a similar fashion as the one we created earlier. One difference though is that the show online and offline methods are not needed here.&lt;/p&gt;

&lt;p&gt;Next, create another class named &lt;code&gt;MessageModel&lt;/code&gt; and paste this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-comment"&amp;gt;// File: ./app/src/main/java/com/example/messengerapp/MessageModel.kt&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-keyword"&amp;gt;data&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;MessageModel&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;(&amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; message: String, &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; senderId: String)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;chat_item&lt;/code&gt; layout used in the &lt;code&gt;onCreateViewHolder&lt;/code&gt; method of the adapter class represents how each layout will look like. Create a new layout called &lt;code&gt;chat_item&lt;/code&gt; and paste this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // File: ./app/src/main/res/layout/chat_item.xml
    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;RelativeLayout&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;xmlns:android&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"http://schemas.android.com/apk/res/android"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;xmlns:app&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"http://schemas.android.com/apk/res-auto"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;android:layout_width&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"wrap_content"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;android:layout_height&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"wrap_content"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;android:layout_margin&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"16dp"&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-attr"&amp;gt;android:orientation&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"vertical"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;

      &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;android.support.v7.widget.CardView&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"@+id/cardView"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_width&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"wrap_content"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_height&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"wrap_content"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;android:layout_gravity&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"start"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;app:cardCornerRadius&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"8dp"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-attr"&amp;gt;app:cardUseCompatPadding&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"true"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;

        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;LinearLayout&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-attr"&amp;gt;android:layout_width&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"wrap_content"&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-attr"&amp;gt;android:layout_height&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"wrap_content"&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-attr"&amp;gt;android:gravity&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"start"&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-attr"&amp;gt;android:orientation&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"vertical"&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-attr"&amp;gt;android:padding&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"8dp"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;

          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;TextView&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-attr"&amp;gt;android:id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"@+id/text"&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-attr"&amp;gt;android:layout_width&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"wrap_content"&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-attr"&amp;gt;android:layout_height&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"wrap_content"&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-attr"&amp;gt;android:layout_gravity&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"center_vertical|start"&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-attr"&amp;gt;android:layout_marginBottom&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"4dp"&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-attr"&amp;gt;android:textStyle&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"bold"&amp;lt;/span&amp;gt; /&amp;gt;&amp;lt;/span&amp;gt;

        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;LinearLayout&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;

      &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;android.support.v7.widget.CardView&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;RelativeLayout&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Finally, open the &lt;code&gt;ChatRoom&lt;/code&gt; activity class and paste this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-comment"&amp;gt;// File: ./app/src/main/java/com/example/messengerapp/ChatRoom.kt&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.app.Activity
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.os.Bundle
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.support.v7.app.AppCompatActivity
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.support.v7.widget.LinearLayoutManager
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.util.Log
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.view.View
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; android.view.inputmethod.InputMethodManager
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; com.pusher.client.Pusher
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; com.pusher.client.PusherOptions
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; com.pusher.client.channel.PrivateChannelEventListener
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; com.pusher.client.util.HttpAuthorizer
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; kotlinx.android.synthetic.main.activity_chat_room.*
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; okhttp3.MediaType
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; okhttp3.RequestBody
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; org.json.JSONObject
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; retrofit2.Call
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; retrofit2.Callback
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; retrofit2.Response
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; java.lang.Exception
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; java.util.*

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;ChatRoom&amp;lt;/span&amp;gt; : &amp;lt;span class="hljs-type"&amp;gt;AppCompatActivity&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;() {

      &amp;lt;span class="hljs-keyword"&amp;gt;companion&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;object&amp;lt;/span&amp;gt; {
        &amp;lt;span class="hljs-keyword"&amp;gt;const&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; EXTRA_ID = &amp;lt;span class="hljs-string"&amp;gt;"id"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;const&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; EXTRA_NAME = &amp;lt;span class="hljs-string"&amp;gt;"name"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;const&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; EXTRA_COUNT = &amp;lt;span class="hljs-string"&amp;gt;"numb"&amp;lt;/span&amp;gt;
      }

      &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;lateinit&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; contactName: String
      &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;lateinit&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; contactId: String
      &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; contactNumb: &amp;lt;span class="hljs-built_in"&amp;gt;Int&amp;lt;/span&amp;gt; = -&amp;lt;span class="hljs-number"&amp;gt;1&amp;lt;/span&amp;gt;
      &amp;lt;span class="hljs-keyword"&amp;gt;lateinit&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; nameOfChannel: String
      &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; mAdapter = ChatRoomAdapter(ArrayList())

      &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;onCreate&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(savedInstanceState: &amp;lt;span class="hljs-type"&amp;gt;Bundle&amp;lt;/span&amp;gt;?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
        &amp;lt;span class="hljs-keyword"&amp;gt;super&amp;lt;/span&amp;gt;.onCreate(savedInstanceState)
        setContentView(R.layout.activity_chat_room)
        fetchExtras()
        setupRecyclerView()
        subscribeToChannel()
        setupClickListener()
      }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In this file, we declared constants used to send data to the activity through intents. We also initialized variables we will use later like the adapter the contact details. We then called some additional methods in the &lt;code&gt;onCreate&lt;/code&gt; method. Let’s add them to the class.&lt;/p&gt;

&lt;p&gt;Add the &lt;code&gt;fetchExtras&lt;/code&gt; method defined below to the class. The method gets the extras sent from the chatroom activity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;fetchExtras&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
      contactName = intent.extras.getString(ChatRoom.EXTRA_NAME)
      contactId = intent.extras.getString(ChatRoom.EXTRA_ID)
      contactNumb = intent.extras.getInt(ChatRoom.EXTRA_COUNT)
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The next method is the &lt;code&gt;setupRecyclerView&lt;/code&gt; method. This initializes the recycler view with an adapter and a layout manager. Paste the function in the same class as before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;setupRecyclerView&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
      with(recyclerViewChat) {
        layoutManager = LinearLayoutManager(&amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-symbol"&amp;gt;@ChatRoom&amp;lt;/span&amp;gt;)
        adapter = mAdapter
      }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The next method is the &lt;code&gt;subscribeToChannel&lt;/code&gt; method. This method subscribes the user to a private channel with the selected contact. Paste the following code to the same class as before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;subscribeToChannel&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
      &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; authorizer = HttpAuthorizer(&amp;lt;span class="hljs-string"&amp;gt;"http://10.0.2.2:5000/pusher/auth/private"&amp;lt;/span&amp;gt;)
      &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; options = PusherOptions().setAuthorizer(authorizer)
      options.setCluster(&amp;lt;span class="hljs-string"&amp;gt;"PUSHER_APP_CLUSTER"&amp;lt;/span&amp;gt;)

      &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; pusher = Pusher(&amp;lt;span class="hljs-string"&amp;gt;"PUSHER_APP_KEY"&amp;lt;/span&amp;gt;, options)
      pusher.connect()

      nameOfChannel = &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (Singleton.getInstance().currentUser.count &amp;gt; contactNumb) {
        &amp;lt;span class="hljs-string"&amp;gt;"private-"&amp;lt;/span&amp;gt; + Singleton.getInstance().currentUser.id + &amp;lt;span class="hljs-string"&amp;gt;"-"&amp;lt;/span&amp;gt; + contactId
      } &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
        &amp;lt;span class="hljs-string"&amp;gt;"private-"&amp;lt;/span&amp;gt; + contactId + &amp;lt;span class="hljs-string"&amp;gt;"-"&amp;lt;/span&amp;gt; + Singleton.getInstance().currentUser.id
      }

      Log.i(&amp;lt;span class="hljs-string"&amp;gt;"ChatRoom"&amp;lt;/span&amp;gt;, nameOfChannel)

      pusher.subscribePrivate(nameOfChannel, &amp;lt;span class="hljs-keyword"&amp;gt;object&amp;lt;/span&amp;gt; : PrivateChannelEventListener {
        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;onEvent&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(channelName: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;?, eventName: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;?, &amp;lt;span class="hljs-keyword"&amp;gt;data&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
          &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; obj = JSONObject(&amp;lt;span class="hljs-keyword"&amp;gt;data&amp;lt;/span&amp;gt;)
          &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; messageModel = MessageModel(obj.getString(&amp;lt;span class="hljs-string"&amp;gt;"message"&amp;lt;/span&amp;gt;), obj.getString(&amp;lt;span class="hljs-string"&amp;gt;"sender_id"&amp;lt;/span&amp;gt;))

          runOnUiThread {
            mAdapter.add(messageModel)
          }
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;onAuthenticationFailure&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(p0: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;?, p1: &amp;lt;span class="hljs-type"&amp;gt;Exception&amp;lt;/span&amp;gt;?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {}
        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;onSubscriptionSucceeded&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(p0: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {}
      }, &amp;lt;span class="hljs-string"&amp;gt;"new-message"&amp;lt;/span&amp;gt;)
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Replace the &lt;code&gt;PUSHER_APP_*&lt;/code&gt; keys with the values on your dashboard.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The code above allows a user to subscribe to a private channel. A private channel requires authorization like the presence channel. However, it does not expose a callback that is triggered when other users subscribe.&lt;/p&gt;

&lt;p&gt;Next method to be added is the &lt;code&gt;setupClickListener&lt;/code&gt;. Paste the method to the same class as before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;setupClickListener&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
      sendButton.setOnClickListener{
        &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (editText.text.isNotEmpty()) {
          &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; jsonObject = JSONObject()
          jsonObject.put(&amp;lt;span class="hljs-string"&amp;gt;"message"&amp;lt;/span&amp;gt;,editText.text.toString())
          jsonObject.put(&amp;lt;span class="hljs-string"&amp;gt;"channel_name"&amp;lt;/span&amp;gt;,nameOfChannel)
          jsonObject.put(&amp;lt;span class="hljs-string"&amp;gt;"sender_id"&amp;lt;/span&amp;gt;,Singleton.getInstance().currentUser.id)

          &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; jsonBody = RequestBody.create(
              MediaType.parse(&amp;lt;span class="hljs-string"&amp;gt;"application/json; charset=utf-8"&amp;lt;/span&amp;gt;), 
              jsonObject.toString()
          )

          RetrofitInstance.retrofit.sendMessage(jsonBody).enqueue(&amp;lt;span class="hljs-keyword"&amp;gt;object&amp;lt;/span&amp;gt;: Callback&amp;lt;String&amp;gt;{
            &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;onFailure&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(call: &amp;lt;span class="hljs-type"&amp;gt;Call&amp;lt;/span&amp;gt;&amp;lt;&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;&amp;gt;?, t: &amp;lt;span class="hljs-type"&amp;gt;Throwable&amp;lt;/span&amp;gt;?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {}
            &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;onResponse&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(call: &amp;lt;span class="hljs-type"&amp;gt;Call&amp;lt;/span&amp;gt;&amp;lt;&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;&amp;gt;?, response: &amp;lt;span class="hljs-type"&amp;gt;Response&amp;lt;/span&amp;gt;&amp;lt;&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;&amp;gt;?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {}
          })

          editText.text.clear()
          hideKeyBoard()
        }

      }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The method above assigns a click listener to the floating action button to send the message to the server. After the message is sent, we clear the text view and hide the keyboard.&lt;/p&gt;

&lt;p&gt;Add a method to the same class for hiding the keyboard like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;fun&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;hideKeyBoard&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
      &amp;lt;span class="hljs-keyword"&amp;gt;val&amp;lt;/span&amp;gt; imm = getSystemService(Activity.INPUT_METHOD_SERVICE) &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt; InputMethodManager
      &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; view = currentFocus

      &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (view == &amp;lt;span class="hljs-literal"&amp;gt;null&amp;lt;/span&amp;gt;) {
        view = View(&amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;)
      }

      imm.hideSoftInputFromWindow(view.windowToken, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;)
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That’s all for the application. Now you can run your application in Android Studio and you should see the application in action.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Make sure the Node.js API we built earlier is running before running the Android application.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/mmdMFfq7WoYsk26cIKocs/486ce2b458e45a41cfeb01ab4441f3b4/android-messenger-presence-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/mmdMFfq7WoYsk26cIKocs/486ce2b458e45a41cfeb01ab4441f3b4/android-messenger-presence-demo.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, you have been introduced yet again to some Pusher’s capabilities such as the private and presence channel. We learned how to authenticate our users for the various channels. We used these channels to implement a private chat between two persons and an online notification for a contact.&lt;/p&gt;

&lt;p&gt;The source code to the application built in this article is available on &lt;a href="https://github.com/neoighodaro/kotlin-messenger-app-with-online-presence-status"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post first appeared on the &lt;a href="https://pusher.com/tutorials/android-messenger-presence-kotlin"&gt;Pusher blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>pusher</category>
      <category>kotlin</category>
      <category>node</category>
      <category>realtime</category>
    </item>
    <item>
      <title>Send push notifications in a social network iOS app - Part 2: Build the app</title>
      <dc:creator>Neo</dc:creator>
      <pubDate>Mon, 19 Nov 2018 17:17:10 +0000</pubDate>
      <link>https://dev.to/neo/send-push-notifications-in-a-social-network-ios-app---part-2-build-the-app-3d8p</link>
      <guid>https://dev.to/neo/send-push-notifications-in-a-social-network-ios-app---part-2-build-the-app-3d8p</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;To follow this tutorial you will need a Mac with Xcode installed, knowledge of Xcode and Swift, basic knowledge of PHP (including the Laravel framework), a Pusher account, and CocoaPods installed on your machine.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/neo/send-push-notifications-in-a-social-network-ios-app---part-1-build-the-backend-ld5"&gt;previous part&lt;/a&gt;, we were able to set up our Pusher Beams application and also create our API backend with Laravel. We also added push notification support to the backend using the &lt;a href="https://github.com/neoighodaro/pusher-beams"&gt;pusher-beams&lt;/a&gt; package.&lt;/p&gt;

&lt;p&gt;In this part, we will continue where we left off. We will be creating the iOS application using Swift and then integrate push notifications to the application so we can receive notifications when they are sent.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/51GgQzYiqQEMAQWOK8AMgS/fd0d5bb1bbaa7f1f198fed340d25cd53/ios-push-notifications-social-network-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/51GgQzYiqQEMAQWOK8AMgS/fd0d5bb1bbaa7f1f198fed340d25cd53/ios-push-notifications-social-network-demo.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;In order to follow along in this tutorial you need to have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have completed &lt;a href="https://pusher.com/tutorials/social-notifications-ios-part-1"&gt;part one&lt;/a&gt; of the article.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building our iOS application using Swift
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Creating our controllers
&lt;/h3&gt;

&lt;p&gt;In Xcode, create a new class &lt;code&gt;LaunchViewController&lt;/code&gt; and paste the contents of the file below into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; UIKit

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;LaunchViewController&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;UIViewController&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-meta"&amp;gt;@IBOutlet&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;weak&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; loginButton: &amp;lt;span class="hljs-type"&amp;gt;UIButton&amp;lt;/span&amp;gt;!
        &amp;lt;span class="hljs-meta"&amp;gt;@IBOutlet&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;weak&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; signupButton: &amp;lt;span class="hljs-type"&amp;gt;UIButton&amp;lt;/span&amp;gt;!

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;viewDidLoad&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;super&amp;lt;/span&amp;gt;.viewDidLoad()

            loginButton.isHidden = &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;
            signupButton.isHidden = &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;

            loginButton.addTarget(&amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;, action: #selector(loginButtonWasPressed), &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt;: .touchUpInside)
            signupButton.addTarget(&amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;, action: #selector(signupButtonWasPressed), &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt;: .touchUpInside)
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;viewDidAppear&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; animated: Bool)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;super&amp;lt;/span&amp;gt;.viewDidAppear(animated)

            &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;AuthService&amp;lt;/span&amp;gt;.shared.loggedIn() == &amp;lt;span class="hljs-literal"&amp;gt;false&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-type"&amp;gt;SettingsService&amp;lt;/span&amp;gt;.shared.loadFromApi()
                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; performSegue(withIdentifier: &amp;lt;span class="hljs-string"&amp;gt;"Main"&amp;lt;/span&amp;gt;, sender: &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;)
            }

            loginButton.isHidden = &amp;lt;span class="hljs-literal"&amp;gt;false&amp;lt;/span&amp;gt;
            signupButton.isHidden = &amp;lt;span class="hljs-literal"&amp;gt;false&amp;lt;/span&amp;gt;
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;loginButtonWasPressed&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            performSegue(withIdentifier: &amp;lt;span class="hljs-string"&amp;gt;"Login"&amp;lt;/span&amp;gt;, sender: &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;)
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;signupButtonWasPressed&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            performSegue(withIdentifier: &amp;lt;span class="hljs-string"&amp;gt;"Signup"&amp;lt;/span&amp;gt;, sender: &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;)
        }   
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the controller as the custom class for the related storyboard scene.&lt;/p&gt;

&lt;p&gt;Above we have two &lt;code&gt;@IBOutlet&lt;/code&gt; buttons for login and signup. In the &lt;code&gt;viewDidLoad&lt;/code&gt; method we hide the buttons and create a target callback for them when they are pressed. In the &lt;code&gt;viewDidAppear&lt;/code&gt; method we check if the user is logged in and present the timeline if so. If the user is not logged in we unhide the authentication buttons.&lt;/p&gt;

&lt;p&gt;We also have the &lt;code&gt;loginButtonWasPressed&lt;/code&gt; and &lt;code&gt;signupButtonWasPressed&lt;/code&gt; methods. These methods present the login and signup controllers.&lt;/p&gt;

&lt;p&gt;Next, create a &lt;code&gt;SignupViewController&lt;/code&gt; class and paste the following code into the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; UIKit
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; NotificationBannerSwift

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;SignupViewController&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;UIViewController&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-meta"&amp;gt;@IBOutlet&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;weak&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; nameTextField: &amp;lt;span class="hljs-type"&amp;gt;UITextField&amp;lt;/span&amp;gt;!
        &amp;lt;span class="hljs-meta"&amp;gt;@IBOutlet&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;weak&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; emailTextField: &amp;lt;span class="hljs-type"&amp;gt;UITextField&amp;lt;/span&amp;gt;!
        &amp;lt;span class="hljs-meta"&amp;gt;@IBOutlet&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;weak&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; passwordTextfield: &amp;lt;span class="hljs-type"&amp;gt;UITextField&amp;lt;/span&amp;gt;!
        &amp;lt;span class="hljs-meta"&amp;gt;@IBOutlet&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;weak&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; signupButton: &amp;lt;span class="hljs-type"&amp;gt;UIBarButtonItem&amp;lt;/span&amp;gt;!

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;viewDidLoad&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;super&amp;lt;/span&amp;gt;.viewDidLoad()

            activateSignupButtonIfNecessary()

            nameTextField.addTarget(&amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;, action: #selector(textFieldChanged(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt;:)), &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt;: .editingChanged)
            emailTextField.addTarget(&amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;, action: #selector(textFieldChanged(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt;:)), &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt;: .editingChanged)
            passwordTextfield.addTarget(&amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;, action: #selector(textFieldChanged(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt;:)), &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt;: .editingChanged)
        }

        &amp;lt;span class="hljs-meta"&amp;gt;@IBAction&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;closeButtonWasPressed&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; sender: &amp;lt;span class="hljs-keyword"&amp;gt;Any&amp;lt;/span&amp;gt;? = &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            dismiss(animated: &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;, completion: &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)
        }

        &amp;lt;span class="hljs-meta"&amp;gt;@IBAction&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;signupButtonWasPressed&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; sender: &amp;lt;span class="hljs-keyword"&amp;gt;Any&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; credentials = textFields(), signupButton.isEnabled &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt;
            }

            &amp;lt;span class="hljs-type"&amp;gt;ApiService&amp;lt;/span&amp;gt;.shared.signup(credentials: credentials) { token, error &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; token = token, error == &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
                    &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;StatusBarNotificationBanner&amp;lt;/span&amp;gt;(title: &amp;lt;span class="hljs-string"&amp;gt;"Signup failed. Try again."&amp;lt;/span&amp;gt;, style: .danger).show()
                }

                &amp;lt;span class="hljs-type"&amp;gt;AuthService&amp;lt;/span&amp;gt;.shared.saveToken(token).then {
                    &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.closeButtonWasPressed()
                }
            }
        }

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;textFields&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;AuthService&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-type"&amp;gt;SignupCredentials&amp;lt;/span&amp;gt;? {
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; name = nameTextField.text, &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; email = emailTextField.text, &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; pass = passwordTextfield.text {
                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; (name, email, pass)
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;
        }

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;activateSignupButtonIfNecessary&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; field = textFields() {
                signupButton.isEnabled = !field.name.isEmpty &amp;amp;&amp;amp; !field.email.isEmpty &amp;amp;&amp;amp; !field.password.isEmpty
            }
        }

        &amp;lt;span class="hljs-meta"&amp;gt;@objc&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;textFieldChanged&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; sender: UITextField)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            activateSignupButtonIfNecessary()
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the controller as the custom class for the signup storyboard scene.&lt;/p&gt;

&lt;p&gt;Above we have three &lt;code&gt;@IBOutlet&lt;/code&gt;'s for our signup text fields and one &lt;code&gt;@IBOutlet&lt;/code&gt; for our signup button. In the &lt;code&gt;viewDidLoad&lt;/code&gt; method we add a callback for our text fields to be triggered when the text is changed. We also call the &lt;code&gt;activateSignupButtonIfNecessary&lt;/code&gt; method, which activates the signup button if all the field’s contents are valid.&lt;/p&gt;

&lt;p&gt;We have two &lt;code&gt;@IBAction&lt;/code&gt; functions. The first for when the close button is pressed and the other for when the signup button is pressed. When the &lt;strong&gt;Sign up&lt;/strong&gt; button is pressed, the &lt;code&gt;signupButtonWasPressed&lt;/code&gt; method is called, which uses the &lt;code&gt;ApiService&lt;/code&gt; to create an account for the user and log the user in. If the signup fails we use the &lt;a href="https://github.com/Daltron/NotificationBanner"&gt;NotificationBanner&lt;/a&gt; package to display an error.&lt;/p&gt;

&lt;p&gt;We also have other helper methods. The &lt;code&gt;textFields&lt;/code&gt; method returns a tuple of the text fields contents and the &lt;code&gt;textFieldChanged&lt;/code&gt; method is fired every time a text field’s content is modified.&lt;/p&gt;

&lt;p&gt;Next, create a &lt;code&gt;LoginViewController&lt;/code&gt; class and paste the following code into the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; UIKit
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; NotificationBannerSwift

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;LoginViewController&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;UIViewController&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-meta"&amp;gt;@IBOutlet&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;weak&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; emailTextField: &amp;lt;span class="hljs-type"&amp;gt;UITextField&amp;lt;/span&amp;gt;!
        &amp;lt;span class="hljs-meta"&amp;gt;@IBOutlet&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;weak&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; passwordTextField: &amp;lt;span class="hljs-type"&amp;gt;UITextField&amp;lt;/span&amp;gt;!
        &amp;lt;span class="hljs-meta"&amp;gt;@IBOutlet&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;weak&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; loginButton: &amp;lt;span class="hljs-type"&amp;gt;UIBarButtonItem&amp;lt;/span&amp;gt;!

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;viewDidLoad&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;super&amp;lt;/span&amp;gt;.viewDidLoad()

            activateLoginButtonIfNecessary()

            emailTextField.addTarget(&amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;, action: #selector(textFieldChanged(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt;:)), &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt;: .editingChanged)
            passwordTextField.addTarget(&amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;, action: #selector(textFieldChanged(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt;:)), &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt;: .editingChanged)
        }

        &amp;lt;span class="hljs-meta"&amp;gt;@IBAction&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;closeButtonWasPressed&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; sender: &amp;lt;span class="hljs-keyword"&amp;gt;Any&amp;lt;/span&amp;gt;? = &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            dismiss(animated: &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;, completion: &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)
        }

        &amp;lt;span class="hljs-meta"&amp;gt;@IBAction&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;loginButtonWasPressed&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; sender: &amp;lt;span class="hljs-keyword"&amp;gt;Any&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; credentials = textFields(), loginButton.isEnabled &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt;
            }

            &amp;lt;span class="hljs-type"&amp;gt;ApiService&amp;lt;/span&amp;gt;.shared.login(credentials: credentials) { token, error &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; token = token, error == &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
                    &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;StatusBarNotificationBanner&amp;lt;/span&amp;gt;(title: &amp;lt;span class="hljs-string"&amp;gt;"Login failed, try again."&amp;lt;/span&amp;gt;, style: .danger).show()
                }

                &amp;lt;span class="hljs-type"&amp;gt;AuthService&amp;lt;/span&amp;gt;.shared.saveToken(token).then {
                    &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.closeButtonWasPressed()
                }
            }
        }

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;textFields&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;AuthService&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-type"&amp;gt;LoginCredentials&amp;lt;/span&amp;gt;? {
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; email = emailTextField.text, &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; password = passwordTextField.text {
                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; (email, password)
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;
        }

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;activateLoginButtonIfNecessary&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; field = textFields() {
                loginButton.isEnabled = !field.email.isEmpty &amp;amp;&amp;amp; !field.password.isEmpty
            }
        }

        &amp;lt;span class="hljs-meta"&amp;gt;@objc&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;textFieldChanged&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; sender: UITextField)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            activateLoginButtonIfNecessary()
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the controller as the custom class for the login storyboard scene.&lt;/p&gt;

&lt;p&gt;The controller above functions very similarly to the &lt;code&gt;SignupViewController&lt;/code&gt;. When the &lt;code&gt;loginButtonWasPressed&lt;/code&gt; method is called it uses the &lt;code&gt;ApiService&lt;/code&gt; to log the user in and save the token.&lt;/p&gt;

&lt;p&gt;Next, we need to create the settings controller. This will be where the settings can be managed. Create a &lt;code&gt;SettingsTableViewController&lt;/code&gt; and paste the following code into the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; UIKit

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;SettingsTableViewController&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;UITableViewController&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; settings = {
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;SettingsService&amp;lt;/span&amp;gt;.shared.settings
        }()

        &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;shouldCheckCell&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(at index: IndexPath, with setting: String)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Bool&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; status = &amp;lt;span class="hljs-type"&amp;gt;Setting&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-type"&amp;gt;Notification&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-type"&amp;gt;Comments&amp;lt;/span&amp;gt;(rawValue: setting)

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; (status == .off &amp;amp;&amp;amp; index.row == &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;) ||
                   (status == .following &amp;amp;&amp;amp; index.row == &amp;lt;span class="hljs-number"&amp;gt;1&amp;lt;/span&amp;gt;) ||
                   (status == .everyone &amp;amp;&amp;amp; index.row == &amp;lt;span class="hljs-number"&amp;gt;2&amp;lt;/span&amp;gt;)
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;tableView&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; tableView: UITableView, cellForRowAt indexPath: IndexPath)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;UITableViewCell&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; cell = &amp;lt;span class="hljs-keyword"&amp;gt;super&amp;lt;/span&amp;gt;.tableView(tableView, cellForRowAt: indexPath)
            cell.accessoryType = .&amp;lt;span class="hljs-keyword"&amp;gt;none&amp;lt;/span&amp;gt;

            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; setting = settings[&amp;lt;span class="hljs-string"&amp;gt;"notification_comments"&amp;lt;/span&amp;gt;], shouldCheckCell(at: indexPath, with: setting) {
                cell.accessoryType = .checkmark
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; cell
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;tableView&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; tableView: UITableView, didSelectRowAt indexPath: IndexPath)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; rowsCount = &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.tableView.numberOfRows(inSection: indexPath.section)

            &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt; i &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;..&amp;lt;rowsCount  {
                &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt;  rowIndexPath = &amp;lt;span class="hljs-type"&amp;gt;IndexPath&amp;lt;/span&amp;gt;(row: i, section: indexPath.section)

                &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; cell = &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.tableView.cellForRow(at: rowIndexPath) {
                    cell.accessoryType = indexPath.row == i ? .checkmark : .&amp;lt;span class="hljs-keyword"&amp;gt;none&amp;lt;/span&amp;gt;
                }
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; setting = indexPath.row == &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt; ? &amp;lt;span class="hljs-string"&amp;gt;"Off"&amp;lt;/span&amp;gt; : (indexPath.row == &amp;lt;span class="hljs-number"&amp;gt;1&amp;lt;/span&amp;gt; ? &amp;lt;span class="hljs-string"&amp;gt;"Following"&amp;lt;/span&amp;gt; : &amp;lt;span class="hljs-string"&amp;gt;"Everyone"&amp;lt;/span&amp;gt;)

            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; status = &amp;lt;span class="hljs-type"&amp;gt;Setting&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-type"&amp;gt;Notification&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-type"&amp;gt;Comments&amp;lt;/span&amp;gt;(rawValue: setting) {
                &amp;lt;span class="hljs-type"&amp;gt;SettingsService&amp;lt;/span&amp;gt;.shared.updateCommentsNotificationSetting(status)
            }
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the controller as the custom class for the settings storyboard scene.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;SettingsTableViewController&lt;/code&gt;, we load the settings from the &lt;code&gt;SettingsService&lt;/code&gt; class, which we will create later. We then define a &lt;code&gt;shouldCheckCell&lt;/code&gt; method, which will determine if the cell row should be checked by checking the users setting.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/4TH2I2tOQ82eAIcueuEoqC/4597c23c2705aaf92044a0dc69a08c70/ios-push-notifications-social-network-settings.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/4TH2I2tOQ82eAIcueuEoqC/4597c23c2705aaf92044a0dc69a08c70/ios-push-notifications-social-network-settings.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen from the storyboard scene, there are three possible settings for the comments notification section: ‘&lt;strong&gt;Off&lt;/strong&gt;’, ‘&lt;strong&gt;From people I follow&lt;/strong&gt;’ and ‘&lt;strong&gt;From everyone&lt;/strong&gt;’. The settings controller attempts to update the setting locally and remotely using the &lt;code&gt;SettingsService&lt;/code&gt; when the setting is changed.&lt;/p&gt;

&lt;p&gt;Next, create the &lt;code&gt;SearchTableViewController&lt;/code&gt; and paste the following code into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; UIKit
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; NotificationBannerSwift

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;SearchTableViewController&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;UITableViewController&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{

        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; users: &amp;lt;span class="hljs-type"&amp;gt;Users&amp;lt;/span&amp;gt; = []

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;viewDidLoad&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;super&amp;lt;/span&amp;gt;.viewDidLoad()

            &amp;lt;span class="hljs-type"&amp;gt;ApiService&amp;lt;/span&amp;gt;.shared.fetchUsers { users &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; users = users &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
                    &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;StatusBarNotificationBanner&amp;lt;/span&amp;gt;(title: &amp;lt;span class="hljs-string"&amp;gt;"Unable to fetch users."&amp;lt;/span&amp;gt;, style: .danger).show()
                }

                &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.users = users
                &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.tableView.reloadData()
            }
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;tableView&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; tableView: UITableView, numberOfRowsInSection section: Int)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.users.&amp;lt;span class="hljs-built_in"&amp;gt;count&amp;lt;/span&amp;gt;
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;tableView&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; tableView: UITableView, cellForRowAt indexPath: IndexPath)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;UITableViewCell&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; user = &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.users[indexPath.row]
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; cell = tableView.dequeueReusableCell(withIdentifier: &amp;lt;span class="hljs-string"&amp;gt;"User"&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt;: indexPath) &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;! &amp;lt;span class="hljs-type"&amp;gt;UserListTableViewCell&amp;lt;/span&amp;gt;

            cell.delegate = &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;
            cell.indexPath = indexPath
            cell.textLabel?.text = user.name

            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; following = user.follows {
                cell.setFollowStatus(following)
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; cell
        }

    }

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;extension&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;SearchTableViewController&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;UserListCellFollowButtonDelegate&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;followButtonTapped&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(at indexPath: IndexPath)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; user = &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.users[indexPath.row]
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; userFollows = user.follows ?? &amp;lt;span class="hljs-literal"&amp;gt;false&amp;lt;/span&amp;gt;

            &amp;lt;span class="hljs-type"&amp;gt;ApiService&amp;lt;/span&amp;gt;.shared.toggleFollowStatus(forUserId: user.id, following: userFollows) { successful &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; successful = successful, successful &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; { &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; }

                &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.users[indexPath.row].follows = !userFollows
                &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.tableView.reloadData()
            }
        }

    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the controller as the custom class for the search storyboard scene.&lt;/p&gt;

&lt;p&gt;Though we have named the class &lt;code&gt;SearchTableViewController&lt;/code&gt; we are actually not going to be doing any searches. We are going to have a make-believe search result, which will display the list of users on the service with a &lt;strong&gt;Follow/Unfollow&lt;/strong&gt; button to make it easy to follow or unfollow a user.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;viewDidLoad&lt;/code&gt; method we call the &lt;code&gt;fetchUsers&lt;/code&gt; method on the &lt;code&gt;ApiService&lt;/code&gt; class and then we load the users to the &lt;code&gt;users&lt;/code&gt; property, which is then used as the table’s data. In the class extension, we implement the &lt;code&gt;UserListCellFollowButtonDelegate&lt;/code&gt; protocol, which makes it easy for us to know when the &lt;strong&gt;Follow/Unfollow&lt;/strong&gt; button is tapped. We use the &lt;a href="https://www.appcoda.com/swift-delegate/"&gt;delegation pattern&lt;/a&gt; to make this possible.&lt;/p&gt;

&lt;p&gt;Next, create the &lt;code&gt;TimelineTableViewController&lt;/code&gt; class and paste the following code into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; UIKit
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; Alamofire
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; NotificationBannerSwift
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; PushNotifications

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;TimelineTableViewController&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;UITableViewController&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; photos: &amp;lt;span class="hljs-type"&amp;gt;Photos&amp;lt;/span&amp;gt; = []
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; selectedPhoto: &amp;lt;span class="hljs-type"&amp;gt;Photo&amp;lt;/span&amp;gt;?
        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; picker = &amp;lt;span class="hljs-type"&amp;gt;UIImagePickerController&amp;lt;/span&amp;gt;()

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;viewDidLoad&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;super&amp;lt;/span&amp;gt;.viewDidLoad()
            &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.reloadButtonWasPressed()
            &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.picker.delegate = &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;
        }

        &amp;lt;span class="hljs-meta"&amp;gt;@IBAction&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;userButtonWasPressed&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; sender: &amp;lt;span class="hljs-keyword"&amp;gt;Any&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-type"&amp;gt;AuthService&amp;lt;/span&amp;gt;.shared.logout()
            dismiss(animated: &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;, completion: &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)
        }

        &amp;lt;span class="hljs-meta"&amp;gt;@IBAction&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;reloadButtonWasPressed&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; sender: &amp;lt;span class="hljs-keyword"&amp;gt;Any&amp;lt;/span&amp;gt;? = &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-type"&amp;gt;ApiService&amp;lt;/span&amp;gt;.shared.fetchPosts { photos &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; photos = photos {
                    &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.photos = photos
                    &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.tableView.reloadData()
                }
            }
        }

        &amp;lt;span class="hljs-meta"&amp;gt;@IBAction&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;addButtonWasPressed&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; sender: &amp;lt;span class="hljs-keyword"&amp;gt;Any&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            picker.sourceType = .photoLibrary
            picker.mediaTypes = &amp;lt;span class="hljs-type"&amp;gt;UIImagePickerController&amp;lt;/span&amp;gt;.availableMediaTypes(&amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt;: .photoLibrary)!
            picker.modalPresentationStyle = .popover
            picker.popoverPresentationController?.barButtonItem = &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;
            present(picker, animated: &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;, completion: &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;prepare&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt; segue: UIStoryboardSegue, sender: &amp;lt;span class="hljs-keyword"&amp;gt;Any&amp;lt;/span&amp;gt;?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; vc = segue.destination &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;CommentsTableViewController&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; photo = selectedPhoto {
                selectedPhoto = &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;
                vc.photoId = photo.id
                vc.comments = photo.comments
            }
        } 
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the controller as the custom class for the timeline storyboard scene.&lt;/p&gt;

&lt;p&gt;In the controller above we have the &lt;code&gt;photos&lt;/code&gt; property, which is an array of all the photos on the service, the &lt;code&gt;selectedPhoto&lt;/code&gt;, which will temporarily hold the selected photo object, and the &lt;code&gt;picker&lt;/code&gt; property, which we will use for the image picker when trying to upload images to the service.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;viewDidLoad&lt;/code&gt; method, we load the posts by calling the &lt;code&gt;reloadButtonWasPressed&lt;/code&gt; method, then we set the class as the &lt;code&gt;picker.delegate&lt;/code&gt;. We have the &lt;code&gt;@IBAction&lt;/code&gt; method &lt;code&gt;addButtonWasPressed&lt;/code&gt;, which launches the iOS image picker.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;prepare&lt;/code&gt; method is called automatically when the controller is navigating to the comments controller. So in here, we set the comments to the comments controller so we have something to display immediately. We also set the &lt;code&gt;photoId&lt;/code&gt; to the comments controller.&lt;/p&gt;

&lt;p&gt;Next, in the same class, paste the following at the bottom:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;extension&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;TimelineTableViewController&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;tableView&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; tableView: UITableView, numberOfRowsInSection section: Int)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; photos.&amp;lt;span class="hljs-built_in"&amp;gt;count&amp;lt;/span&amp;gt;
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;tableView&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; tableView: UITableView, cellForRowAt indexPath: IndexPath)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;UITableViewCell&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; photo = photos[indexPath.row]
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; cell = tableView.dequeueReusableCell(withIdentifier: &amp;lt;span class="hljs-string"&amp;gt;"PhotoCell"&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt;: indexPath) &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;! &amp;lt;span class="hljs-type"&amp;gt;PhotoListTableViewCell&amp;lt;/span&amp;gt;

            cell.delegate = &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;
            cell.indexPath = indexPath
            cell.nameLabel.text = photo.user.name
            cell.photo.image = &amp;lt;span class="hljs-type"&amp;gt;UIImage&amp;lt;/span&amp;gt;(named: &amp;lt;span class="hljs-string"&amp;gt;"loading"&amp;lt;/span&amp;gt;)

            &amp;lt;span class="hljs-type"&amp;gt;Alamofire&amp;lt;/span&amp;gt;.request(photo.image).responseData { response &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; response.error == &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; data = response.data {
                    cell.photo.image = &amp;lt;span class="hljs-type"&amp;gt;UIImage&amp;lt;/span&amp;gt;(data: data)
                }
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; cell
        } 

    }

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;extension&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;TimelineTableViewController&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;PhotoListCellDelegate&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;commentButtonWasTapped&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(at indexPath: IndexPath)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.selectedPhoto = photos[indexPath.row]
            &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.performSegue(withIdentifier: &amp;lt;span class="hljs-string"&amp;gt;"Comments"&amp;lt;/span&amp;gt;, sender: &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;)
        }

    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code above, we have two extensions for the &lt;code&gt;TimelineTableViewController&lt;/code&gt;. The first extension defines how we want to present the photos to the table view. The second extension is an implementation of the &lt;code&gt;PhotoListCellDelegate&lt;/code&gt;, which is another implementation of the delegation pattern. The method defined here, &lt;code&gt;commentButtonWasTapped&lt;/code&gt;, will be triggered when the Comment button is pressed on a photo cell.&lt;/p&gt;

&lt;p&gt;In the same file add the last class extension at the bottom of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;extension&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;TimelineTableViewController&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;UIImagePickerControllerDelegate&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-title"&amp;gt;UINavigationControllerDelegate&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{

        &amp;lt;span class="hljs-meta"&amp;gt;@objc&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;imagePickerController&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : &amp;lt;span class="hljs-keyword"&amp;gt;Any&amp;lt;/span&amp;gt;])&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; selected = info[&amp;lt;span class="hljs-string"&amp;gt;"UIImagePickerControllerOriginalImage"&amp;lt;/span&amp;gt;] &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;UIImage&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; image = &amp;lt;span class="hljs-type"&amp;gt;UIImageJPEGRepresentation&amp;lt;/span&amp;gt;(selected, &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;) &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; { 
                    &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; 
                }

                &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; uploadPhotoHandler: (() -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Void&amp;lt;/span&amp;gt;)? = {
                    &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; caption: &amp;lt;span class="hljs-type"&amp;gt;UITextField&amp;lt;/span&amp;gt;?

                    &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; alert = &amp;lt;span class="hljs-type"&amp;gt;UIAlertController&amp;lt;/span&amp;gt;(title: &amp;lt;span class="hljs-string"&amp;gt;"Add Caption"&amp;lt;/span&amp;gt;, message: &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;, preferredStyle: .alert)
                    alert.addTextField(configurationHandler: { textfield &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt; caption = textfield })
                    alert.addAction(&amp;lt;span class="hljs-type"&amp;gt;UIAlertAction&amp;lt;/span&amp;gt;(title: &amp;lt;span class="hljs-string"&amp;gt;"Cancel"&amp;lt;/span&amp;gt;, style: .cancel, handler: &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;))
                    alert.addAction(&amp;lt;span class="hljs-type"&amp;gt;UIAlertAction&amp;lt;/span&amp;gt;(title: &amp;lt;span class="hljs-string"&amp;gt;"Save"&amp;lt;/span&amp;gt;, style: .&amp;lt;span class="hljs-keyword"&amp;gt;default&amp;lt;/span&amp;gt;, handler: { action &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
                        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; filename = &amp;lt;span class="hljs-string"&amp;gt;"upload.jpg"&amp;lt;/span&amp;gt;
                        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; caption = caption?.text ?? &amp;lt;span class="hljs-string"&amp;gt;"No caption"&amp;lt;/span&amp;gt;

                        &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; url = info[&amp;lt;span class="hljs-type"&amp;gt;UIImagePickerControllerImageURL&amp;lt;/span&amp;gt;] &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;NSURL&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; name = url.lastPathComponent {
                            filename = name
                        }

                        &amp;lt;span class="hljs-type"&amp;gt;ApiService&amp;lt;/span&amp;gt;.shared.uploadImage(image, caption: caption, name: filename) { photo, error &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
                            &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; photo = photo, error == &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
                                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;StatusBarNotificationBanner&amp;lt;/span&amp;gt;(title: &amp;lt;span class="hljs-string"&amp;gt;"Failed to upload image"&amp;lt;/span&amp;gt;, style: .danger).show()
                            }

                            &amp;lt;span class="hljs-keyword"&amp;gt;try&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;PushNotifications&amp;lt;/span&amp;gt;.shared.subscribe(interest: &amp;lt;span class="hljs-string"&amp;gt;"photo_\(photo.id)-comment_following"&amp;lt;/span&amp;gt;)
                            &amp;lt;span class="hljs-keyword"&amp;gt;try&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;PushNotifications&amp;lt;/span&amp;gt;.shared.subscribe(interest: &amp;lt;span class="hljs-string"&amp;gt;"photo_\(photo.id)-comment_everyone"&amp;lt;/span&amp;gt;)

                            &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.photos.insert(photo, at: &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;)
                            &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.tableView.reloadData()

                            &amp;lt;span class="hljs-type"&amp;gt;StatusBarNotificationBanner&amp;lt;/span&amp;gt;(title: &amp;lt;span class="hljs-string"&amp;gt;"Uploaded successfully"&amp;lt;/span&amp;gt;, style: .success).show()
                        }
                    }))

                    &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.present(alert, animated: &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;, completion: &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)
                }

                &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.dismiss(animated: &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;, completion: uploadPhotoHandler)
            }
        }

    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the extension above, we implement the &lt;code&gt;UIImagePickerControllerDelegate&lt;/code&gt;, which let’s us handle image selection from the &lt;code&gt;UIImagePickerController&lt;/code&gt;. When an image is selected, the method above will be called.&lt;/p&gt;

&lt;p&gt;We handle it by getting the selected image, displaying an alert controller with a text field so we can get a caption for the image and then we send the image and the caption to the API using the &lt;code&gt;ApiService&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When the upload is complete, we add the newly added photo to the table and then we subscribe the user to the Pusher Beam Interest so they can receive push notifications when comments are made to the photo.&lt;/p&gt;

&lt;p&gt;Also above we subscribed to two interests. The first is &lt;code&gt;photo_\(id)-comment_following&lt;/code&gt; and the second one is &lt;code&gt;photo_\(id)-comment_everyone&lt;/code&gt;. We do this so that we can segment notifications depending on the users setting. On the server, when a comment is added, if the photo owner sets the comment notification setting to following then the push notification will be published to the &lt;code&gt;photo_\(id)-comment_following&lt;/code&gt; interest.&lt;/p&gt;

&lt;p&gt;Next, create the &lt;code&gt;CommentsTableViewController&lt;/code&gt; class and paste the following code into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; UIKit
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; NotificationBannerSwift

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;CommentsTableViewController&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;UITableViewController&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; photoId: &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt; = &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; commentField: &amp;lt;span class="hljs-type"&amp;gt;UITextField&amp;lt;/span&amp;gt;?
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; comments: &amp;lt;span class="hljs-type"&amp;gt;PhotoComments&amp;lt;/span&amp;gt; = []

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;viewDidLoad&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;super&amp;lt;/span&amp;gt;.viewDidLoad()

            navigationItem.title = &amp;lt;span class="hljs-string"&amp;gt;"Comments"&amp;lt;/span&amp;gt;
            navigationController?.navigationBar.prefersLargeTitles = &amp;lt;span class="hljs-literal"&amp;gt;false&amp;lt;/span&amp;gt;
            navigationItem.rightBarButtonItem = &amp;lt;span class="hljs-type"&amp;gt;UIBarButtonItem&amp;lt;/span&amp;gt;(title: &amp;lt;span class="hljs-string"&amp;gt;"Add"&amp;lt;/span&amp;gt;, style: .plain, target: &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;, action: #selector(addCommentButtonWasTapped))

            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; photoId != &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-type"&amp;gt;ApiService&amp;lt;/span&amp;gt;.shared.fetchComments(forPhoto: photoId) { comments &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
                    &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; comments = comments &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; { &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; }

                    &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.comments = comments
                    &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.tableView.reloadData()
                }
            }
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;tableView&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; tableView: UITableView, numberOfRowsInSection section: Int)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; comments.&amp;lt;span class="hljs-built_in"&amp;gt;count&amp;lt;/span&amp;gt;
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;tableView&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; tableView: UITableView, cellForRowAt indexPath: IndexPath)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;UITableViewCell&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; cell = tableView.dequeueReusableCell(withIdentifier: &amp;lt;span class="hljs-string"&amp;gt;"Comment"&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt;: indexPath) &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;! &amp;lt;span class="hljs-type"&amp;gt;CommentsListTableViewCell&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; comment = comments[indexPath.row]

            cell.username?.text = comment.user.name
            cell.comment?.text = comment.comment

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; cell
        }

        &amp;lt;span class="hljs-meta"&amp;gt;@objc&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;addCommentButtonWasTapped&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; alertCtrl = &amp;lt;span class="hljs-type"&amp;gt;UIAlertController&amp;lt;/span&amp;gt;(title: &amp;lt;span class="hljs-string"&amp;gt;"Add Comment"&amp;lt;/span&amp;gt;, message: &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;, preferredStyle: .alert)
            alertCtrl.addAction(&amp;lt;span class="hljs-type"&amp;gt;UIAlertAction&amp;lt;/span&amp;gt;(title: &amp;lt;span class="hljs-string"&amp;gt;"Cancel"&amp;lt;/span&amp;gt;, style: .cancel, handler: &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;))
            alertCtrl.addTextField { textField &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.commentField = textField }
            alertCtrl.addAction(&amp;lt;span class="hljs-type"&amp;gt;UIAlertAction&amp;lt;/span&amp;gt;(title: &amp;lt;span class="hljs-string"&amp;gt;"Add Comment"&amp;lt;/span&amp;gt;, style: .&amp;lt;span class="hljs-keyword"&amp;gt;default&amp;lt;/span&amp;gt;) { &amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; comment = &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.commentField?.text &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; { &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; }

                &amp;lt;span class="hljs-type"&amp;gt;ApiService&amp;lt;/span&amp;gt;.shared.leaveComment(forId: &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.photoId, comment: comment) { newComment &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
                    &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; comment = newComment &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
                        &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;StatusBarNotificationBanner&amp;lt;/span&amp;gt;(title: &amp;lt;span class="hljs-string"&amp;gt;"Failed to post comment"&amp;lt;/span&amp;gt;, style: .danger).show()
                    }

                    &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.comments.insert(comment, at: &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;)
                    &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.tableView.reloadData()
                }
            })

            &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.present(alertCtrl, animated: &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;, completion: &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the controller as the custom class for the timeline storyboard scene.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;CommentsTableViewController&lt;/code&gt; above we have the &lt;code&gt;comments&lt;/code&gt; property, which holds all the comments for the photo, the &lt;code&gt;photoId&lt;/code&gt; property, which holds the ID of the photo whose comments are being loaded and the &lt;code&gt;commentField&lt;/code&gt; property, which is the text field that holds new comments.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;viewDidLoad&lt;/code&gt; method we set up the controller title and add an ‘&lt;strong&gt;Add&lt;/strong&gt;’ button to the right of the navigation bar. Next, we call the &lt;code&gt;fetchComments&lt;/code&gt; method in the &lt;code&gt;ApiService&lt;/code&gt; to load comments for the photo.&lt;/p&gt;

&lt;p&gt;We have the &lt;code&gt;addCommentButtonWasTapped&lt;/code&gt; method in the controller, which is activated when the ‘&lt;strong&gt;Add&lt;/strong&gt;’ button on the navigation bar is pressed. This brings up an alert controller with a text field where we can get the comment text and then send the comment to the API using the &lt;code&gt;ApiService&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating our custom view classes
&lt;/h3&gt;

&lt;p&gt;Since we have created the controllers, let’s create some custom view classes that we need for the cells we used in the controllers earlier.&lt;/p&gt;

&lt;p&gt;The first custom cell we will create will be the &lt;code&gt;PhotoListTableViewCell&lt;/code&gt; class. Create the class and paste the following code into the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; UIKit

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;protocol&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;PhotoListCellDelegate&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;commentButtonWasTapped&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(at indexPath: IndexPath)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
    }

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;PhotoListTableViewCell&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;UITableViewCell&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-meta"&amp;gt;@IBOutlet&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;weak&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; nameLabel: &amp;lt;span class="hljs-type"&amp;gt;UILabel&amp;lt;/span&amp;gt;!
        &amp;lt;span class="hljs-meta"&amp;gt;@IBOutlet&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;weak&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; photo: &amp;lt;span class="hljs-type"&amp;gt;UIImageView&amp;lt;/span&amp;gt;!
        &amp;lt;span class="hljs-meta"&amp;gt;@IBOutlet&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;weak&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; commentButton: &amp;lt;span class="hljs-type"&amp;gt;UIButton&amp;lt;/span&amp;gt;!

        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; indexPath: &amp;lt;span class="hljs-type"&amp;gt;IndexPath&amp;lt;/span&amp;gt;?    
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; delegate: &amp;lt;span class="hljs-type"&amp;gt;PhotoListCellDelegate&amp;lt;/span&amp;gt;?

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;awakeFromNib&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;super&amp;lt;/span&amp;gt;.awakeFromNib()
            &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.selectionStyle = .&amp;lt;span class="hljs-keyword"&amp;gt;none&amp;lt;/span&amp;gt;

            commentButton.addTarget(&amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;, action: #selector(commentButtonWasTapped), &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt;: .touchUpInside)
        }

        &amp;lt;span class="hljs-meta"&amp;gt;@objc&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;commentButtonWasTapped&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; indexPath = indexPath, &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; delegate = delegate {
                delegate.commentButtonWasTapped(at: indexPath)
            }
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set this class as the custom class for the cell in the timeline scene of the storyboard.&lt;/p&gt;

&lt;p&gt;In the class above we have a few &lt;code&gt;@IBOutlet&lt;/code&gt;'s for the name, photo and comment button. We have a &lt;code&gt;commentButtonWasTapped&lt;/code&gt; method that fires the &lt;code&gt;commentWasTapped&lt;/code&gt; method on a delegate of the cell.&lt;/p&gt;

&lt;p&gt;The next cell we want to create is the &lt;code&gt;CommentsListTableViewCell&lt;/code&gt;. Create the class and paste the following code into the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; UIKit

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;CommentsListTableViewCell&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;UITableViewCell&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-meta"&amp;gt;@IBOutlet&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;weak&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; username: &amp;lt;span class="hljs-type"&amp;gt;UILabel&amp;lt;/span&amp;gt;!
        &amp;lt;span class="hljs-meta"&amp;gt;@IBOutlet&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;weak&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; comment: &amp;lt;span class="hljs-type"&amp;gt;UILabel&amp;lt;/span&amp;gt;!
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set this class as the custom class for the cell in the comments scene of the storyboard.&lt;/p&gt;

&lt;p&gt;The next cell we want to create is the &lt;code&gt;UsersListTableViewCell&lt;/code&gt;. Create the class and paste the following code into the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; UIKit

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;protocol&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;UserListCellFollowButtonDelegate&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;followButtonTapped&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(at index:IndexPath)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
    }

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;UserListTableViewCell&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;UITableViewCell&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; indexPath: &amp;lt;span class="hljs-type"&amp;gt;IndexPath&amp;lt;/span&amp;gt;?    
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; delegate: &amp;lt;span class="hljs-type"&amp;gt;UserListCellFollowButtonDelegate&amp;lt;/span&amp;gt;?

        &amp;lt;span class="hljs-meta"&amp;gt;@IBOutlet&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;weak&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; followButton: &amp;lt;span class="hljs-type"&amp;gt;UIButton&amp;lt;/span&amp;gt;!

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;awakeFromNib&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;super&amp;lt;/span&amp;gt;.awakeFromNib()
            &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.selectionStyle = .&amp;lt;span class="hljs-keyword"&amp;gt;none&amp;lt;/span&amp;gt;

            &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.setFollowStatus(&amp;lt;span class="hljs-literal"&amp;gt;false&amp;lt;/span&amp;gt;)
            &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.followButton.layer.cornerRadius = &amp;lt;span class="hljs-number"&amp;gt;5&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.followButton.setTitleColor(&amp;lt;span class="hljs-type"&amp;gt;UIColor&amp;lt;/span&amp;gt;.white, &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt;: .normal)
            &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.followButton.addTarget(&amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;, action: #selector(followButtonTapped(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt;:)), &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt;: .touchUpInside)
        }

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;setFollowStatus&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; following: Bool)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.followButton.backgroundColor = following ? &amp;lt;span class="hljs-type"&amp;gt;UIColor&amp;lt;/span&amp;gt;.red : &amp;lt;span class="hljs-type"&amp;gt;UIColor&amp;lt;/span&amp;gt;.blue
            &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.followButton.setTitle(following ? &amp;lt;span class="hljs-string"&amp;gt;"Unfollow"&amp;lt;/span&amp;gt; : &amp;lt;span class="hljs-string"&amp;gt;"Follow"&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt;: .normal)
        }

        &amp;lt;span class="hljs-meta"&amp;gt;@objc&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;followButtonTapped&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; sender: UIButton)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; delegate = delegate, &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; indexPath = indexPath {
                delegate.followButtonTapped(at: indexPath)
            }
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set this class as the custom class for the cell in the search scene in the storyboard.&lt;/p&gt;

&lt;p&gt;In the class above we have a custom cell to display a user’s name and a follow button. We have a &lt;code&gt;setFollowStatus&lt;/code&gt; method that toggles the state of the follow button and we have a &lt;code&gt;followButtonTapped&lt;/code&gt; method that calls the &lt;code&gt;followButtonTapped&lt;/code&gt; method on a delegate of the cell.&lt;/p&gt;

&lt;p&gt;That’s all for custom cell classes. Let’s move on to creating other classes and setting up push notification.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding other classes and setting up push notifications
&lt;/h3&gt;

&lt;p&gt;We still need to create one last file. Create an &lt;code&gt;AppConstants&lt;/code&gt; file and paste the following code into the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; Foundation

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;struct&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;AppConstants&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;static&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;API_URL&amp;lt;/span&amp;gt; = &amp;lt;span class="hljs-string"&amp;gt;"http://127.0.0.1:8000"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;static&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;API_CLIENT_ID&amp;lt;/span&amp;gt; = &amp;lt;span class="hljs-string"&amp;gt;"API_CLIENT_ID"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;static&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;API_CLIENT_SECRET&amp;lt;/span&amp;gt; = &amp;lt;span class="hljs-string"&amp;gt;"API_CLIENT_SECRET"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;static&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;PUSHER_INSTANCE_ID&amp;lt;/span&amp;gt; = &amp;lt;span class="hljs-string"&amp;gt;"PUSHER_INSTANCE_ID
    }&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the struct above we have some constants that we will be using throughout the application. These will be used to store application credentials and will be unchanged throughout the lifetime of the application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Replace the key values with the actual values gotten from your Passport installation and from your Pusher dashboard.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next, open the &lt;code&gt;AppDelegate&lt;/code&gt; class and replace the contents with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; UIKit
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; PushNotifications

    &amp;lt;span class="hljs-meta"&amp;gt;@UIApplicationMain&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;AppDelegate&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;UIResponder&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-title"&amp;gt;UIApplicationDelegate&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{

        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; window: &amp;lt;span class="hljs-type"&amp;gt;UIWindow&amp;lt;/span&amp;gt;?

        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; pushNotifications = &amp;lt;span class="hljs-type"&amp;gt;PushNotifications&amp;lt;/span&amp;gt;.shared

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;application&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: &amp;lt;span class="hljs-keyword"&amp;gt;Any&amp;lt;/span&amp;gt;]?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Bool&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.pushNotifications.start(instanceId: &amp;lt;span class="hljs-type"&amp;gt;AppConstants&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-type"&amp;gt;PUSHER_INSTANCE_ID&amp;lt;/span&amp;gt;)
            &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.pushNotifications.registerForRemoteNotifications()

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;
        }

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;application&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.pushNotifications.registerDeviceToken(deviceToken)
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the class above, we use the &lt;a href="https://docs.pusher.com/push-notifications/reference/ios"&gt;Pusher Beams Swift SDK&lt;/a&gt; to register the device for push notifications.&lt;/p&gt;

&lt;p&gt;That’s all for our application’s code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding push notifications to our iOS new application
&lt;/h3&gt;

&lt;p&gt;Now that we have completed the logic for the application, let’s enable push notifications on the application in Xcode.&lt;/p&gt;

&lt;p&gt;In the project navigator, select your project, and click on the &lt;strong&gt;Capabilities&lt;/strong&gt; tab. &lt;a href="http://help.apple.com/xcode/mac/current/#/devdfd3d04a1"&gt;Enable Push Notifications&lt;/a&gt; by turning the switch ON.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/nwh9O0inWCAwwsi82iq4o/efa381b5ae180d746608e151f4559f0e/ios-push-notifications-social-network-enable-push-notifications.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/nwh9O0inWCAwwsi82iq4o/efa381b5ae180d746608e151f4559f0e/ios-push-notifications-social-network-enable-push-notifications.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will create an entitlements file in the root of your project. With that, you have provisioned your application to fully receive push notifications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding rich push notifications
&lt;/h3&gt;

&lt;p&gt;Let’s take it one step further and add rich notifications. We will want to be able to see the photo commented on in the notification received as this can increase engagement.&lt;/p&gt;

&lt;p&gt;In Xcode go to ‘File’ &amp;gt; ‘New’ &amp;gt; ‘Target’ and select ‘Notification Service Extension’. Enter the name of the extension and then click proceed. Make sure the extension is added and embedded to the Gram project. We will call our extension &lt;strong&gt;Notification&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When the target has been created you will see a new &lt;code&gt;Notification&lt;/code&gt; group (it may be different depending on what you chose to call your extension) with two files in them. Open the &lt;code&gt;NotificationService&lt;/code&gt; class and replace the &lt;code&gt;didReceive&lt;/code&gt; method with the method below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;didReceive&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; request: UNNotificationRequest, withContentHandler contentHandler: @escaping &amp;lt;span class="hljs-params"&amp;gt;(UNNotificationContent)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Void&amp;lt;/span&amp;gt;) {
        &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;UNMutableNotificationContent&amp;lt;/span&amp;gt;)

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;failEarly&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            contentHandler(request.content)
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; content = (request.content.mutableCopy() &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;UNMutableNotificationContent&amp;lt;/span&amp;gt;),
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; apnsData = content.userInfo[&amp;lt;span class="hljs-string"&amp;gt;"data"&amp;lt;/span&amp;gt;] &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;? [&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-type"&amp;gt;Any&amp;lt;/span&amp;gt;],
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; photoURL = apnsData[&amp;lt;span class="hljs-string"&amp;gt;"attachment-url"&amp;lt;/span&amp;gt;] &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;,
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; attachmentURL = &amp;lt;span class="hljs-type"&amp;gt;URL&amp;lt;/span&amp;gt;(string: photoURL.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!),
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; imageData = &amp;lt;span class="hljs-keyword"&amp;gt;try&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;NSData&amp;lt;/span&amp;gt;(contentsOf: attachmentURL, options: &amp;lt;span class="hljs-type"&amp;gt;NSData&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-type"&amp;gt;ReadingOptions&amp;lt;/span&amp;gt;()),
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; attachment = &amp;lt;span class="hljs-type"&amp;gt;UNNotificationAttachment&amp;lt;/span&amp;gt;.create(imageFileIdentifier: &amp;lt;span class="hljs-string"&amp;gt;"image.png"&amp;lt;/span&amp;gt;, data: imageData, options: &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)
            &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; failEarly()
        }

        content.attachments = [attachment]
        contentHandler(content.copy() &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;! &amp;lt;span class="hljs-type"&amp;gt;UNNotificationContent&amp;lt;/span&amp;gt;)
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above we are simply getting the notifications payload and then extracting the data including the &lt;code&gt;attachment-url&lt;/code&gt;, which is the photo URL. We then create an attachment for the notification and add it to the notification’s content. That’s all we need to do to add the image as an attachment.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Your image URL has to be a secure URL with HTTPS or iOS will not load the image. You can override this setting in your &lt;code&gt;info.plist&lt;/code&gt; file but it is strongly recommended that you don’t.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next, create a new file in the Notification extension called &lt;code&gt;UNNotificationAttachment.swift&lt;/code&gt; and paste the following into the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; Foundation
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; UserNotifications

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;extension&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;UNNotificationAttachment&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{

        &amp;lt;span class="hljs-keyword"&amp;gt;static&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;create&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(imageFileIdentifier: String, data: NSData, options: [NSObject : AnyObject]?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;UNNotificationAttachment&amp;lt;/span&amp;gt;? {
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; fileManager = &amp;lt;span class="hljs-type"&amp;gt;FileManager&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-keyword"&amp;gt;default&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; tmpSubFolderName = &amp;lt;span class="hljs-type"&amp;gt;ProcessInfo&amp;lt;/span&amp;gt;.processInfo.globallyUniqueString
            &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; tmpSubFolderURL = &amp;lt;span class="hljs-type"&amp;gt;NSURL&amp;lt;/span&amp;gt;(fileURLWithPath: &amp;lt;span class="hljs-type"&amp;gt;NSTemporaryDirectory&amp;lt;/span&amp;gt;()).appendingPathComponent(tmpSubFolderName, isDirectory: &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;)

            &amp;lt;span class="hljs-keyword"&amp;gt;do&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-keyword"&amp;gt;try&amp;lt;/span&amp;gt; fileManager.createDirectory(at: tmpSubFolderURL!, withIntermediateDirectories: &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;, attributes: &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)
                &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; fileURL = tmpSubFolderURL?.appendingPathComponent(imageFileIdentifier)
                &amp;lt;span class="hljs-keyword"&amp;gt;try&amp;lt;/span&amp;gt; data.write(to: fileURL!, options: [])
                &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; imageAttachment = &amp;lt;span class="hljs-keyword"&amp;gt;try&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;UNNotificationAttachment&amp;lt;/span&amp;gt;(identifier: imageFileIdentifier, url: fileURL!, options: options)
                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; imageAttachment
            } &amp;lt;span class="hljs-keyword"&amp;gt;catch&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; error {
                &amp;lt;span class="hljs-built_in"&amp;gt;print&amp;lt;/span&amp;gt;(&amp;lt;span class="hljs-string"&amp;gt;"error \(error)"&amp;lt;/span&amp;gt;)
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above is a class extension for the &lt;code&gt;UNNotificationAttachment&lt;/code&gt; class. The extension contains the &lt;code&gt;create&lt;/code&gt; method that allows us to create a temporary image to store the image attachment that was sent as a push notification.&lt;/p&gt;

&lt;p&gt;Now you can build your application using Xcode. Make sure the Laravel application is running or the app won’t be able to fetch the data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Allowing our application to connect locally
&lt;/h3&gt;

&lt;p&gt;If you are going to be testing the app’s backend using a local server, then there is one last thing we need to do. Open the &lt;code&gt;info.plist&lt;/code&gt; file and add an entry to the &lt;code&gt;plist&lt;/code&gt; file to allow connection to our local server:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/5UymGAiLeg22C46SEyay4U/0f66ae00ca879dc03852ece398c955f9/ios-push-notifications-social-network-connect-locally.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/5UymGAiLeg22C46SEyay4U/0f66ae00ca879dc03852ece398c955f9/ios-push-notifications-social-network-connect-locally.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s it now. We can run our application. However, &lt;strong&gt;remember that to demo the push notifications, you will need an actual iOS device as simulators cannot receive push notifications.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is a screen recording of the application in action:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/51GgQzYiqQEMAQWOK8AMgS/fd0d5bb1bbaa7f1f198fed340d25cd53/ios-push-notifications-social-network-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/51GgQzYiqQEMAQWOK8AMgS/fd0d5bb1bbaa7f1f198fed340d25cd53/ios-push-notifications-social-network-demo.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we have seen how you can use Pusher Beams to send push notifications from a Laravel backend and a Swift iOS client application. When creating social networks it is essential that the push notifications we send are relevant and not spammy and Pusher Beams can help with this.&lt;/p&gt;

&lt;p&gt;The source code to the application is on &lt;a href="https://github.com/neoighodaro-articles/pusher-beams-ios-social-network"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post first appeared on the &lt;a href="https://pusher.com/tutorials/social-notifications-ios-part-2"&gt;Pusher blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>pusher</category>
      <category>ios</category>
      <category>swift</category>
      <category>realtime</category>
    </item>
    <item>
      <title>Send push notifications in a social network iOS app - Part 1: Build the backend</title>
      <dc:creator>Neo</dc:creator>
      <pubDate>Mon, 19 Nov 2018 17:14:21 +0000</pubDate>
      <link>https://dev.to/neo/send-push-notifications-in-a-social-network-ios-app---part-1-build-the-backend-ld5</link>
      <guid>https://dev.to/neo/send-push-notifications-in-a-social-network-ios-app---part-1-build-the-backend-ld5</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;To follow this tutorial you will need a Mac with Xcode installed, and knowledge of Xcode and Swift. You'll also need basic knowledge of PHP (including the Laravel framework), a Pusher account, and Cocoapods installed on your machine.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Push notifications are a great way to engage users of your application. It lets you send notifications when certain events happen on your service. This can lead to re-engagement.&lt;/p&gt;

&lt;p&gt;While building a social network app, you'll need to send push notifications to your users. These notifications will help users know when certain events happen in your application. For instance, you can send push notifications to a user when someone comments on their photo.&lt;/p&gt;

&lt;p&gt;As powerful as push notifications are, they are a double-edged sword. Most users will uninstall your application if they feel like they are being spammed.&lt;/p&gt;

&lt;p&gt;Over the course of two articles, we will see how we can build a social networking iOS application. We will add push notifications to the user when someone comments on a photo they uploaded. Then we'll add settings so users can specify when they want to receive notifications.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/51GgQzYiqQEMAQWOK8AMgS/fd0d5bb1bbaa7f1f198fed340d25cd53/ios-push-notifications-social-network-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/51GgQzYiqQEMAQWOK8AMgS/fd0d5bb1bbaa7f1f198fed340d25cd53/ios-push-notifications-social-network-demo.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To follow along in this tutorial you need to have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Mac with &lt;a href="https://developer.apple.com/xcode/"&gt;Xcode&lt;/a&gt; installed.&lt;/li&gt;
&lt;li&gt;Knowledge of using Xcode.&lt;/li&gt;
&lt;li&gt;Knowledge of the &lt;a href="https://developer.apple.com/swift/"&gt;Swift&lt;/a&gt; programming language.&lt;/li&gt;
&lt;li&gt;Knowledge of PHP and &lt;a href="https://laravel.com/docs/"&gt;Laravel&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://laravel.com/docs/5.6/installation"&gt;Laravel CLI&lt;/a&gt; installed on your machine.&lt;/li&gt;
&lt;li&gt;SQLite installed on your machine. &lt;a href="http://www.sqlitetutorial.net/download-install-sqlite/"&gt;See installation guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A Pusher beams API Key. &lt;a href="https://dash.pusher.com/authenticate/register?ref=pn-landing-page"&gt;Create one here.&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Cocoapods installed on your machine. &lt;a href="https://guides.cocoapods.org/using/getting-started.html"&gt;See installation guide&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Creating our Pusher application
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ To use push notifications, you have to &lt;a href="https://developer.apple.com/programs/enroll/"&gt;be a part of the Apple Developer program&lt;/a&gt;. Also, push notifications do not work on simulators so you will need an actual iOS device to test.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Pusher &lt;a href="https://pusher.com/beams"&gt;Beams&lt;/a&gt; has first-class support for native iOS applications. Your iOS app instances subscribe to &lt;strong&gt;Interests&lt;/strong&gt;; then your servers send push notifications to those interests. Every app instance subscribed to that interest will receive the notification, even if the app is not open on the device at the time.&lt;/p&gt;

&lt;p&gt;This section describes how you can set up an iOS app to receive transactional push notifications about news updates through Pusher.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure APNs
&lt;/h3&gt;

&lt;p&gt;Pusher relies on the Apple Push Notification service (APNs) to deliver push notifications to iOS application users on your behalf. When we deliver push notifications, we use your key that has APNs service enabled. This page guides you through the process of getting the key and how to provide it to Pusher.&lt;/p&gt;

&lt;p&gt;Head over to the Apple Developer dashboard by clicking &lt;a href="https://developer.apple.com/account"&gt;here&lt;/a&gt; and then create a new key as seen below:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/3XWdqEhwo8AuGKIywEsC6s/b260c8ac1052beceb634e1dcbeb99c6f/ios-push-notifications-social-network-create-APN.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/3XWdqEhwo8AuGKIywEsC6s/b260c8ac1052beceb634e1dcbeb99c6f/ios-push-notifications-social-network-create-APN.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you have created the key, download it. Keep it safe as we will need it in the next section.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ You have to keep the generated key safe as you cannot get it back if you lose it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Creating your Pusher application
&lt;/h3&gt;

&lt;p&gt;The next thing you need to do is create a new Pusher Beams application from the &lt;a href="https://dash.pusher.com"&gt;Pusher dashboard&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/2Af98mjyDGqUA8wMkgYyaU/9497179efc4643ae5c48e844b8644011/ios-push-notifications-social-network-create-beams.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/2Af98mjyDGqUA8wMkgYyaU/9497179efc4643ae5c48e844b8644011/ios-push-notifications-social-network-create-beams.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you have created the application, you should be presented with a quick start that will help you set up the application.&lt;/p&gt;

&lt;p&gt;In order to configure your Beams instance, you will need to get the key with APNs service enabled by Apple. This is the same key as the one we downloaded in the previous section. Once you’ve got the key, upload it.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/1OvPaR9NrieciOQuWYuCCE/4059623254d3571bf71f5069a32904ad/ios-push-notifications-social-network-add-APN-to-beams.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/1OvPaR9NrieciOQuWYuCCE/4059623254d3571bf71f5069a32904ad/ios-push-notifications-social-network-add-APN-to-beams.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter your Apple Team ID. You can get the Team ID from &lt;a href="https://developer.apple.com/account/#/membership"&gt;here&lt;/a&gt;. You can then continue with the setup wizard and copy the instance ID and secret key for your Pusher application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the backend
&lt;/h2&gt;

&lt;p&gt;Before we start building the iOS application, let’s build the backend API using Laravel. To get started we need to set up our Laravel application. Run the command below using your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ Laravel new gram

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will create a new Laravel application in the &lt;code&gt;gram&lt;/code&gt; directory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring our database
&lt;/h3&gt;

&lt;p&gt;Our application will need to connect to a database and we will be using SQLite as our database of choice as it's the easiest to set up.&lt;/p&gt;

&lt;p&gt;To get started, create a new &lt;code&gt;database.sqlite&lt;/code&gt; file in the &lt;code&gt;database&lt;/code&gt; directory. Next open the &lt;code&gt;.env&lt;/code&gt; file that comes with the Laravel project and replace the following lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    DB_CONNECTION=mysql
    DB_HOST=127.0.0.1
    DB_PORT=3306
    DB_DATABASE=homestead
    DB_USERNAME=homestead
    DB_PASSWORD=secret

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    DB_CONNECTION=sqlite
    DB_DATABASE=/full/path/to/database.sqlite

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we have a connection to the database.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating our migrations, models, and controllers
&lt;/h3&gt;

&lt;p&gt;When you want to create a migration, model, and controller, you should use the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ php artisan make:model ModelName -mc

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Using the above command as a template, create the following models, migrations, and controllers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Photo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PhotoComment&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UserFollow&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UserSetting&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In that order.&lt;/p&gt;

&lt;p&gt;After running the commands, we should have migrations in the &lt;code&gt;database/migrations&lt;/code&gt; directory, models in the &lt;code&gt;app&lt;/code&gt; directory, and controllers in the &lt;code&gt;app/Http/Controllers&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;Let’s update the migrations. Open the &lt;code&gt;*_create_photos_table.php&lt;/code&gt; migration and replace the &lt;code&gt;up&lt;/code&gt; method with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;up&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;{
        Schema::create(&amp;lt;span class="hljs-string"&amp;gt;'photos'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-params"&amp;gt;(Blueprint $table)&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
            $table-&amp;gt;increments(&amp;lt;span class="hljs-string"&amp;gt;'id'&amp;lt;/span&amp;gt;);
            $table-&amp;gt;unsignedInteger(&amp;lt;span class="hljs-string"&amp;gt;'user_id'&amp;lt;/span&amp;gt;);
            $table-&amp;gt;foreign(&amp;lt;span class="hljs-string"&amp;gt;'user_id'&amp;lt;/span&amp;gt;)-&amp;gt;references(&amp;lt;span class="hljs-string"&amp;gt;'id'&amp;lt;/span&amp;gt;)-&amp;gt;on(&amp;lt;span class="hljs-string"&amp;gt;'users'&amp;lt;/span&amp;gt;);
            $table-&amp;gt;string(&amp;lt;span class="hljs-string"&amp;gt;'image'&amp;lt;/span&amp;gt;);
            $table-&amp;gt;string(&amp;lt;span class="hljs-string"&amp;gt;'image_path'&amp;lt;/span&amp;gt;);
            $table-&amp;gt;string(&amp;lt;span class="hljs-string"&amp;gt;'caption'&amp;lt;/span&amp;gt;)-&amp;gt;nullable();
            $table-&amp;gt;timestamps();
        });
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Open the &lt;code&gt;*_create_photo_comments_table.php&lt;/code&gt; migration and replace the &lt;code&gt;up&lt;/code&gt; method with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;up&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;{
        Schema::create(&amp;lt;span class="hljs-string"&amp;gt;'photo_comments'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-params"&amp;gt;(Blueprint $table)&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
            $table-&amp;gt;increments(&amp;lt;span class="hljs-string"&amp;gt;'id'&amp;lt;/span&amp;gt;);
            $table-&amp;gt;unsignedInteger(&amp;lt;span class="hljs-string"&amp;gt;'photo_id'&amp;lt;/span&amp;gt;);
            $table-&amp;gt;foreign(&amp;lt;span class="hljs-string"&amp;gt;'photo_id'&amp;lt;/span&amp;gt;)-&amp;gt;references(&amp;lt;span class="hljs-string"&amp;gt;'id'&amp;lt;/span&amp;gt;)-&amp;gt;on(&amp;lt;span class="hljs-string"&amp;gt;'photos'&amp;lt;/span&amp;gt;);
            $table-&amp;gt;unsignedInteger(&amp;lt;span class="hljs-string"&amp;gt;'user_id'&amp;lt;/span&amp;gt;);
            $table-&amp;gt;foreign(&amp;lt;span class="hljs-string"&amp;gt;'user_id'&amp;lt;/span&amp;gt;)-&amp;gt;references(&amp;lt;span class="hljs-string"&amp;gt;'id'&amp;lt;/span&amp;gt;)-&amp;gt;on(&amp;lt;span class="hljs-string"&amp;gt;'users'&amp;lt;/span&amp;gt;);
            $table-&amp;gt;text(&amp;lt;span class="hljs-string"&amp;gt;'comment'&amp;lt;/span&amp;gt;);
            $table-&amp;gt;timestamps();
        });
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Open the &lt;code&gt;*_create_user_follows_table.php&lt;/code&gt; migration and replace the &lt;code&gt;up&lt;/code&gt; method with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;up&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;{
        Schema::create(&amp;lt;span class="hljs-string"&amp;gt;'user_follows'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-params"&amp;gt;(Blueprint $table)&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
            $table-&amp;gt;increments(&amp;lt;span class="hljs-string"&amp;gt;'id'&amp;lt;/span&amp;gt;);
            $table-&amp;gt;unsignedInteger(&amp;lt;span class="hljs-string"&amp;gt;'follower_id'&amp;lt;/span&amp;gt;);
            $table-&amp;gt;foreign(&amp;lt;span class="hljs-string"&amp;gt;'follower_id'&amp;lt;/span&amp;gt;)-&amp;gt;references(&amp;lt;span class="hljs-string"&amp;gt;'id'&amp;lt;/span&amp;gt;)-&amp;gt;on(&amp;lt;span class="hljs-string"&amp;gt;'users'&amp;lt;/span&amp;gt;);
            $table-&amp;gt;unsignedInteger(&amp;lt;span class="hljs-string"&amp;gt;'following_id'&amp;lt;/span&amp;gt;);
            $table-&amp;gt;foreign(&amp;lt;span class="hljs-string"&amp;gt;'following_id'&amp;lt;/span&amp;gt;)-&amp;gt;references(&amp;lt;span class="hljs-string"&amp;gt;'id'&amp;lt;/span&amp;gt;)-&amp;gt;on(&amp;lt;span class="hljs-string"&amp;gt;'users'&amp;lt;/span&amp;gt;);
            $table-&amp;gt;timestamps();
        });
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Open the &lt;code&gt;*_create_user_settings_table.php&lt;/code&gt; migration and replace the &lt;code&gt;up&lt;/code&gt; method with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;up&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;{
        Schema::create(&amp;lt;span class="hljs-string"&amp;gt;'user_settings'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-params"&amp;gt;(Blueprint $table)&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
            $table-&amp;gt;increments(&amp;lt;span class="hljs-string"&amp;gt;'id'&amp;lt;/span&amp;gt;);
            $table-&amp;gt;unsignedInteger(&amp;lt;span class="hljs-string"&amp;gt;'user_id'&amp;lt;/span&amp;gt;);
            $table-&amp;gt;foreign(&amp;lt;span class="hljs-string"&amp;gt;'user_id'&amp;lt;/span&amp;gt;)-&amp;gt;references(&amp;lt;span class="hljs-string"&amp;gt;'id'&amp;lt;/span&amp;gt;)-&amp;gt;on(&amp;lt;span class="hljs-string"&amp;gt;'users'&amp;lt;/span&amp;gt;);
            $table-&amp;gt;enum(&amp;lt;span class="hljs-string"&amp;gt;'notification_comments'&amp;lt;/span&amp;gt;, [&amp;lt;span class="hljs-string"&amp;gt;'Off'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'Following'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'Everyone'&amp;lt;/span&amp;gt;])
                  -&amp;gt;default(&amp;lt;span class="hljs-string"&amp;gt;'Following'&amp;lt;/span&amp;gt;);
        });
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That’s all for the migrations. Execute the migrations by running the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ php artisan migrate

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When that’s done, we can update our models. Open the &lt;code&gt;Photo&lt;/code&gt; model in the &lt;code&gt;app&lt;/code&gt; directory and replace the contents with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?php&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-keyword"&amp;gt;namespace&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Database&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Eloquent&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Model&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Photo&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;extends&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Model&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;protected&amp;lt;/span&amp;gt; $hidden = [&amp;lt;span class="hljs-string"&amp;gt;'image_path'&amp;lt;/span&amp;gt;];

        &amp;lt;span class="hljs-keyword"&amp;gt;protected&amp;lt;/span&amp;gt; $with = [&amp;lt;span class="hljs-string"&amp;gt;'user'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'comments'&amp;lt;/span&amp;gt;];

        &amp;lt;span class="hljs-keyword"&amp;gt;protected&amp;lt;/span&amp;gt; $fillable = [&amp;lt;span class="hljs-string"&amp;gt;'user_id'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'caption'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'image'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'image_path'&amp;lt;/span&amp;gt;];

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;user&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;belongsTo(User::class);
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;comments&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;hasMany(PhotoComment::class)-&amp;gt;orderBy(&amp;lt;span class="hljs-string"&amp;gt;'id'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'desc'&amp;lt;/span&amp;gt;);
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the model above we have the &lt;code&gt;user&lt;/code&gt; and &lt;code&gt;comments&lt;/code&gt; methods, which are relationships to the &lt;code&gt;User&lt;/code&gt; model and the &lt;code&gt;PhotoComment&lt;/code&gt; model.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;PhotoComment&lt;/code&gt; class in the &lt;code&gt;app&lt;/code&gt; directory and replace the content with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?php&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-keyword"&amp;gt;namespace&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Database&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Eloquent&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Model&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Notifications&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Notifiable&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;PhotoComment&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;extends&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Model&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Notifiable&amp;lt;/span&amp;gt;;

        &amp;lt;span class="hljs-keyword"&amp;gt;protected&amp;lt;/span&amp;gt; $with = [&amp;lt;span class="hljs-string"&amp;gt;'user'&amp;lt;/span&amp;gt;];

        &amp;lt;span class="hljs-keyword"&amp;gt;protected&amp;lt;/span&amp;gt; $fillable = [&amp;lt;span class="hljs-string"&amp;gt;'photo_id'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'user_id'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'comment'&amp;lt;/span&amp;gt;];

        &amp;lt;span class="hljs-keyword"&amp;gt;protected&amp;lt;/span&amp;gt; $casts = [&amp;lt;span class="hljs-string"&amp;gt;'photo_id'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'int'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'user_id'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'int'&amp;lt;/span&amp;gt;];

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;scopeForPhoto&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;($query, int $id)&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; $query-&amp;gt;where(&amp;lt;span class="hljs-string"&amp;gt;'photo_id'&amp;lt;/span&amp;gt;, $id);
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;user&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;belongsTo(User::class);
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the model above we are using the &lt;code&gt;Notifiable&lt;/code&gt; trait because we want to be able to send push notifications when new comments are made on photos later in the article. We also have a &lt;code&gt;scopeForPhoto&lt;/code&gt; method, which is an &lt;a href="https://laravel.com/docs/5.6/eloquent#query-scopes"&gt;Eloquent query scope&lt;/a&gt;. We also have a &lt;code&gt;user&lt;/code&gt; method, which is a relationship to the &lt;code&gt;User&lt;/code&gt; model.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;User&lt;/code&gt; model in the &lt;code&gt;app&lt;/code&gt; directory and replace the content with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?php&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-keyword"&amp;gt;namespace&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Notifications&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Notifiable&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Foundation&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Auth&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;User&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;as&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Authenticatable&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;User&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;extends&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Authenticatable&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Notifiable&amp;lt;/span&amp;gt;;

        &amp;lt;span class="hljs-keyword"&amp;gt;protected&amp;lt;/span&amp;gt; $fillable = [&amp;lt;span class="hljs-string"&amp;gt;'name'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'email'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'password'&amp;lt;/span&amp;gt;];

        &amp;lt;span class="hljs-keyword"&amp;gt;protected&amp;lt;/span&amp;gt; $hidden = [&amp;lt;span class="hljs-string"&amp;gt;'password'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'remember_token'&amp;lt;/span&amp;gt;];

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;allowsCommentsNotifications&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(User $actor)&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            $status = strtolower(&amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;settings-&amp;gt;notification_comments);

            &amp;lt;span class="hljs-keyword"&amp;gt;switch&amp;lt;/span&amp;gt; ($status) {
                &amp;lt;span class="hljs-keyword"&amp;gt;case&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'everyone'&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;true&amp;lt;/span&amp;gt;;
                &amp;lt;span class="hljs-keyword"&amp;gt;case&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'following'&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;isFollowing($actor);
                &amp;lt;span class="hljs-keyword"&amp;gt;default&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;false&amp;lt;/span&amp;gt;;
            }
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;isFollowing&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(User $user)&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;bool&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;following-&amp;gt;where(&amp;lt;span class="hljs-string"&amp;gt;'following_id'&amp;lt;/span&amp;gt;, $user-&amp;gt;id)-&amp;gt;count() &amp;gt; &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;;
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;scopeOtherUsers&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;($query)&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; $query-&amp;gt;where(&amp;lt;span class="hljs-string"&amp;gt;'id'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'!='&amp;lt;/span&amp;gt;, auth()-&amp;gt;user()-&amp;gt;id);
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;following&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;hasMany(UserFollow::class, &amp;lt;span class="hljs-string"&amp;gt;'follower_id'&amp;lt;/span&amp;gt;);
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;followers&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;hasMany(UserFollow::class, &amp;lt;span class="hljs-string"&amp;gt;'following_id'&amp;lt;/span&amp;gt;);
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;settings&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;hasOne(UserSetting::class);
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the model above we have six methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;allowsCommentsNotifications&lt;/code&gt; checks to see if the owner of the photo has settings that permit notifications to be sent to them when there is a new comment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isFollowing&lt;/code&gt; checks if a user is following another user.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scopeOtherUsers&lt;/code&gt; is an Eloquent query scope.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;following&lt;/code&gt;, &lt;code&gt;followers&lt;/code&gt; and &lt;code&gt;settings&lt;/code&gt; are methods that define relationships with other models.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open the &lt;code&gt;UserFollow&lt;/code&gt; model in the &lt;code&gt;app&lt;/code&gt; directory and replace the content with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?php&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-keyword"&amp;gt;namespace&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Database&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Eloquent&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Model&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;UserFollow&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;extends&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Model&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;protected&amp;lt;/span&amp;gt; $fillable = [&amp;lt;span class="hljs-string"&amp;gt;'follower_id'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'following_id'&amp;lt;/span&amp;gt;];
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Finally, open the &lt;code&gt;UserSetting&lt;/code&gt; model in the &lt;code&gt;app&lt;/code&gt; directory and replace the content with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?php&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-keyword"&amp;gt;namespace&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Database&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Eloquent&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Model&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;UserSetting&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;extends&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Model&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;protected&amp;lt;/span&amp;gt; $fillable = [&amp;lt;span class="hljs-string"&amp;gt;'notification_comments'&amp;lt;/span&amp;gt;];

        &amp;lt;span class="hljs-keyword"&amp;gt;protected&amp;lt;/span&amp;gt; $hidden = [&amp;lt;span class="hljs-string"&amp;gt;'id'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'user_id'&amp;lt;/span&amp;gt;];

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; $timestamps = &amp;lt;span class="hljs-keyword"&amp;gt;false&amp;lt;/span&amp;gt;;

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;scopeForCurrentUser&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;($query)&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; $query-&amp;gt;where(&amp;lt;span class="hljs-string"&amp;gt;'user_id'&amp;lt;/span&amp;gt;, auth()-&amp;gt;user()-&amp;gt;id);
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Above we have the &lt;code&gt;scopeForCurrentUser&lt;/code&gt; method, which is an Eloquent query scope.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We set the &lt;code&gt;$timestamps&lt;/code&gt; property to false to instruct Eloquent not to attempt to automatically manage the &lt;code&gt;created_at&lt;/code&gt; and &lt;code&gt;updated_at&lt;/code&gt; fields as we do not have them in the user settings table.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One last thing we want to do is, create a new setting automatically when a user is created. For this, we will use an &lt;a href="https://laravel.com/docs/5.6/eloquent#events"&gt;Eloquent event&lt;/a&gt;. Open the &lt;code&gt;AppServiceProvider&lt;/code&gt; class in the &lt;code&gt;app/Providers&lt;/code&gt; directory and replace the boot method with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public function boot()
    {
        \App\User::created(function ($user) {
            $user-&amp;gt;settings()-&amp;gt;save(new \App\UserSetting);
        });
    }

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As seen above, when a new user is created, a new user setting is saved to the user.&lt;/p&gt;

&lt;p&gt;Next, let’s update the logic for the controllers. Open the &lt;code&gt;PhotoController.php&lt;/code&gt; in the &lt;code&gt;app/Http/Controllers&lt;/code&gt; directory and replace the contents with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?php&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-keyword"&amp;gt;namespace&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Http&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Controllers&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Photo&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Http&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Request&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Support&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Facades&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Storage&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;PhotoController&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;extends&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Controller&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;index&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            $photos = Photo::orderBy(&amp;lt;span class="hljs-string"&amp;gt;'id'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'desc'&amp;lt;/span&amp;gt;)-&amp;gt;paginate(&amp;lt;span class="hljs-number"&amp;gt;20&amp;lt;/span&amp;gt;);

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; response()-&amp;gt;json($photos-&amp;gt;toArray());
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;store&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(Request $request)&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            $data = $request-&amp;gt;validate([
                &amp;lt;span class="hljs-string"&amp;gt;'caption'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'required|between:1,1000'&amp;lt;/span&amp;gt;,
                &amp;lt;span class="hljs-string"&amp;gt;'image'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'required|image|mimes:jpeg,gif,png'&amp;lt;/span&amp;gt;,
            ]);

            $path = Storage::disk(&amp;lt;span class="hljs-string"&amp;gt;'public'&amp;lt;/span&amp;gt;)-&amp;gt;putFile(&amp;lt;span class="hljs-string"&amp;gt;'photos'&amp;lt;/span&amp;gt;, $request-&amp;gt;file(&amp;lt;span class="hljs-string"&amp;gt;'image'&amp;lt;/span&amp;gt;));

            $data = array_merge($data, [
                &amp;lt;span class="hljs-string"&amp;gt;'user_id'&amp;lt;/span&amp;gt; =&amp;gt; $request-&amp;gt;user()-&amp;gt;id,
                &amp;lt;span class="hljs-string"&amp;gt;'image'&amp;lt;/span&amp;gt; =&amp;gt; asset(&amp;lt;span class="hljs-string"&amp;gt;"storage/{$path}"&amp;lt;/span&amp;gt;),
                &amp;lt;span class="hljs-string"&amp;gt;'image_path'&amp;lt;/span&amp;gt; =&amp;gt; storage_path(&amp;lt;span class="hljs-string"&amp;gt;'app/public'&amp;lt;/span&amp;gt;) . &amp;lt;span class="hljs-string"&amp;gt;"/{$path}"&amp;lt;/span&amp;gt;,
            ]);

            $photo = Photo::create($data);

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; response()-&amp;gt;json([
                &amp;lt;span class="hljs-string"&amp;gt;'status'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'success'&amp;lt;/span&amp;gt;,
                &amp;lt;span class="hljs-string"&amp;gt;'data'&amp;lt;/span&amp;gt; =&amp;gt; $photo-&amp;gt;load([&amp;lt;span class="hljs-string"&amp;gt;'user'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'comments'&amp;lt;/span&amp;gt;])
            ]);
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;PhotoController&lt;/code&gt; above we have two methods. The &lt;code&gt;index&lt;/code&gt; displays all the available photos, and the &lt;code&gt;store&lt;/code&gt; saves a new photo to disk and database.&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;photos&lt;/code&gt; saved to be available to the public, we need to link the &lt;code&gt;storage&lt;/code&gt; directory to the public directory. To do so run the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ php artisan storage:link

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/6jnvC8UIg0s4EOiU6wIEWg/3323c85e9a4f733865cd0efef1e44d0e/ios-push-notifications-social-network-storage-dir.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/6jnvC8UIg0s4EOiU6wIEWg/3323c85e9a4f733865cd0efef1e44d0e/ios-push-notifications-social-network-storage-dir.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The command above will create a symlink from the &lt;code&gt;public/storage&lt;/code&gt; directory to the &lt;code&gt;storage/app/public&lt;/code&gt; directory that our photos will be uploaded to.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;PhotoCommentController.php&lt;/code&gt; in the &lt;code&gt;app/Http/Controllers&lt;/code&gt; directory and replace the contents with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?php&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-keyword"&amp;gt;namespace&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Http&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Controllers&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Photo&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;PhotoComment&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Http&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Request&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Notifications&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;UserCommented&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;PhotoCommentController&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;extends&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Controller&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;index&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(Request $request)&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            $photo = Photo::with(&amp;lt;span class="hljs-string"&amp;gt;'comments'&amp;lt;/span&amp;gt;)-&amp;gt;findOrFail($request-&amp;gt;route(&amp;lt;span class="hljs-string"&amp;gt;'photo'&amp;lt;/span&amp;gt;));

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; response()-&amp;gt;json([&amp;lt;span class="hljs-string"&amp;gt;'data'&amp;lt;/span&amp;gt; =&amp;gt; $photo-&amp;gt;comments]);
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;store&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(Request $request, Photo $photo)&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            $data = $request-&amp;gt;validate([&amp;lt;span class="hljs-string"&amp;gt;'comment'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'required|string|between:2,500'&amp;lt;/span&amp;gt;]);

            $comment = PhotoComment::create([
                &amp;lt;span class="hljs-string"&amp;gt;'photo_id'&amp;lt;/span&amp;gt; =&amp;gt; $photo-&amp;gt;id,
                &amp;lt;span class="hljs-string"&amp;gt;'comment'&amp;lt;/span&amp;gt; =&amp;gt; $data[&amp;lt;span class="hljs-string"&amp;gt;'comment'&amp;lt;/span&amp;gt;],
                &amp;lt;span class="hljs-string"&amp;gt;'user_id'&amp;lt;/span&amp;gt; =&amp;gt; $request-&amp;gt;user()-&amp;gt;id,
            ]);

            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; ($photo-&amp;gt;user-&amp;gt;allowsCommentsNotifications($request-&amp;gt;user())) {
                $comment-&amp;gt;notify(&amp;lt;span class="hljs-keyword"&amp;gt;new&amp;lt;/span&amp;gt; UserCommented($request-&amp;gt;user(), $photo, $comment));
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; response()-&amp;gt;json([
                &amp;lt;span class="hljs-string"&amp;gt;'status'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'success'&amp;lt;/span&amp;gt;, 
                &amp;lt;span class="hljs-string"&amp;gt;'data'&amp;lt;/span&amp;gt; =&amp;gt; $comment-&amp;gt;load(&amp;lt;span class="hljs-string"&amp;gt;'user'&amp;lt;/span&amp;gt;)
            ]);
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;PhotoCommentController&lt;/code&gt; we have two methods. The &lt;code&gt;index&lt;/code&gt; method displays all the comments for a single photo and the &lt;code&gt;store&lt;/code&gt; creates a new comment.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;store&lt;/code&gt; method on line 30, we have a call to a &lt;code&gt;notify&lt;/code&gt; method and passes a nonexistent &lt;code&gt;UserCommented&lt;/code&gt; class. This class is a &lt;a href="https://laravel.com/docs/5.6/notifications"&gt;Laravel notification&lt;/a&gt; class. We will create this class later in the article. It’s needed to send notifications to the user when comments are made.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;UserController&lt;/code&gt; by running the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ php artisan make:controller UserController

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next open the &lt;code&gt;UserController.php&lt;/code&gt; in the &lt;code&gt;app/Http/Controllers&lt;/code&gt; directory and replace the contents with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?php&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-keyword"&amp;gt;namespace&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Http&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Controllers&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;User&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Support&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Facades&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Hash&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;UserController&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;extends&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Controller&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;index&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            $users = [];

            User::with(&amp;lt;span class="hljs-string"&amp;gt;'followers'&amp;lt;/span&amp;gt;)-&amp;gt;otherUsers()-&amp;gt;get()-&amp;gt;each(&amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-params"&amp;gt;($user, $index)&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-params"&amp;gt;(&amp;amp;$users)&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
                $users[$index] = $user;
                $users[$index][&amp;lt;span class="hljs-string"&amp;gt;'follows'&amp;lt;/span&amp;gt;] = auth()-&amp;gt;user()-&amp;gt;isFollowing($user);
            });

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; response()-&amp;gt;json([&amp;lt;span class="hljs-string"&amp;gt;'data'&amp;lt;/span&amp;gt; =&amp;gt; $users]);
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;create&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(Request $request)&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            $credentials = $request-&amp;gt;validate([
                &amp;lt;span class="hljs-string"&amp;gt;'name'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'required|string|max:255'&amp;lt;/span&amp;gt;,
                &amp;lt;span class="hljs-string"&amp;gt;'email'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'required|string|email|max:255|unique:users'&amp;lt;/span&amp;gt;,
                &amp;lt;span class="hljs-string"&amp;gt;'password'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'required|string|min:6'&amp;lt;/span&amp;gt;,
            ]);

            $credentials[&amp;lt;span class="hljs-string"&amp;gt;'password'&amp;lt;/span&amp;gt;] = Hash::make($credentials[&amp;lt;span class="hljs-string"&amp;gt;'password'&amp;lt;/span&amp;gt;]);

            $user = User::create($credentials);

            $token = $user-&amp;gt;createToken(config(&amp;lt;span class="hljs-string"&amp;gt;'app.name'&amp;lt;/span&amp;gt;));

            $data = [&amp;lt;span class="hljs-string"&amp;gt;'user'&amp;lt;/span&amp;gt; =&amp;gt; $user, &amp;lt;span class="hljs-string"&amp;gt;'access_token'&amp;lt;/span&amp;gt; =&amp;gt; $token-&amp;gt;accessToken];

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; response()-&amp;gt;json([&amp;lt;span class="hljs-string"&amp;gt;'data'&amp;lt;/span&amp;gt; =&amp;gt; $data, &amp;lt;span class="hljs-string"&amp;gt;'status'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'success'&amp;lt;/span&amp;gt;]);
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;UserController&lt;/code&gt; has two methods, one is the &lt;code&gt;index&lt;/code&gt; method that returns all the users on the service, and the second is the &lt;code&gt;create&lt;/code&gt; method that registers a new user and returns an access token that will be used for making authorized requests on behalf of the user.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;UserFollowController.php&lt;/code&gt; in the &lt;code&gt;app/Http/Controllers&lt;/code&gt; directory and replace the contents with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?php&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-keyword"&amp;gt;namespace&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Http&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Controllers&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;User&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;UserFollow&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Http&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Request&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;UserFollowController&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;extends&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Controller&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;follow&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(Request $request)&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            $user = User::findOrFail($request-&amp;gt;get(&amp;lt;span class="hljs-string"&amp;gt;'following_id'&amp;lt;/span&amp;gt;));


            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; ($request-&amp;gt;user()-&amp;gt;isFollowing($user) == &amp;lt;span class="hljs-keyword"&amp;gt;false&amp;lt;/span&amp;gt;) {
                $request-&amp;gt;user()-&amp;gt;following()-&amp;gt;save(
                    &amp;lt;span class="hljs-keyword"&amp;gt;new&amp;lt;/span&amp;gt; UserFollow($request-&amp;gt;only(&amp;lt;span class="hljs-string"&amp;gt;'following_id'&amp;lt;/span&amp;gt;)
                ));
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; response()-&amp;gt;json([&amp;lt;span class="hljs-string"&amp;gt;'status'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'success'&amp;lt;/span&amp;gt;]);
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;unfollow&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(Request $request)&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            $user = User::findOrFail($request-&amp;gt;get(&amp;lt;span class="hljs-string"&amp;gt;'following_id'&amp;lt;/span&amp;gt;));

            $request-&amp;gt;user()-&amp;gt;following()-&amp;gt;whereFollowingId($user-&amp;gt;id)-&amp;gt;delete();

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; response()-&amp;gt;json([&amp;lt;span class="hljs-string"&amp;gt;'status'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'success'&amp;lt;/span&amp;gt;]);
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The controller above simply follows or unfollows a user.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;UserSettingController.php&lt;/code&gt; in the &lt;code&gt;app/Http/Controllers&lt;/code&gt; directory and replace the contents with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?php&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-keyword"&amp;gt;namespace&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Http&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Controllers&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;UserSetting&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Http&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Request&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;UserSettingController&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;extends&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Controller&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;index&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; response()-&amp;gt;json(UserSetting::forCurrentUser()-&amp;gt;first());
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;update&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(Request $request)&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            $settings = $request-&amp;gt;validate([
                &amp;lt;span class="hljs-string"&amp;gt;'notification_comments'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'in:Off,Following,Everyone'&amp;lt;/span&amp;gt;,
            ]);

            $updated = $request-&amp;gt;user()-&amp;gt;settings()-&amp;gt;update($settings);

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; response()-&amp;gt;json([&amp;lt;span class="hljs-string"&amp;gt;'status'&amp;lt;/span&amp;gt; =&amp;gt; $updated ? &amp;lt;span class="hljs-string"&amp;gt;'success'&amp;lt;/span&amp;gt; : &amp;lt;span class="hljs-string"&amp;gt;'error'&amp;lt;/span&amp;gt;]);
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the controller above we return all the settings available for a user in the &lt;code&gt;index&lt;/code&gt; method and then we update the settings for the user in the &lt;code&gt;update&lt;/code&gt; method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating our application’s routes
&lt;/h3&gt;

&lt;p&gt;Since we have created our controllers, let’s create our routes that link the URL to controllers. Open the &lt;code&gt;routes/api.php&lt;/code&gt; file and replace the contents with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?php&amp;lt;/span&amp;gt;

    Route::post(&amp;lt;span class="hljs-string"&amp;gt;'/register'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'UserController@create'&amp;lt;/span&amp;gt;);

    Route::group([&amp;lt;span class="hljs-string"&amp;gt;'middleware'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'auth:api'&amp;lt;/span&amp;gt;], &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        Route::get(&amp;lt;span class="hljs-string"&amp;gt;'/users/settings'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'UserSettingController@index'&amp;lt;/span&amp;gt;);
        Route::put(&amp;lt;span class="hljs-string"&amp;gt;'/users/settings'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'UserSettingController@update'&amp;lt;/span&amp;gt;);
        Route::post(&amp;lt;span class="hljs-string"&amp;gt;'/users/follow'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'UserFollowController@follow'&amp;lt;/span&amp;gt;);
        Route::post(&amp;lt;span class="hljs-string"&amp;gt;'/users/unfollow'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'UserFollowController@unfollow'&amp;lt;/span&amp;gt;);
        Route::get(&amp;lt;span class="hljs-string"&amp;gt;'/users'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'UserController@index'&amp;lt;/span&amp;gt;);
        Route::get(&amp;lt;span class="hljs-string"&amp;gt;'/photos/{photo}/comments'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'PhotoCommentController@index'&amp;lt;/span&amp;gt;);
        Route::post(&amp;lt;span class="hljs-string"&amp;gt;'/photos/{photo}/comments'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'PhotoCommentController@store'&amp;lt;/span&amp;gt;);
        Route::resource(&amp;lt;span class="hljs-string"&amp;gt;'/photos'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'PhotoController'&amp;lt;/span&amp;gt;)-&amp;gt;only([&amp;lt;span class="hljs-string"&amp;gt;'store'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;'index'&amp;lt;/span&amp;gt;]);
    });
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Above we have defined routes for our application. Each route points to a controller and a method in that controller that will handle the route. The route group above has a middleware applied, &lt;code&gt;auth:api&lt;/code&gt;, this will make sure that every request to a route inside the group has to be authorized.&lt;/p&gt;

&lt;p&gt;To manage authorization, let’s install Laravel passport.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing Laravel Passport
&lt;/h3&gt;

&lt;p&gt;Since we have many requests that require authorization, let’s install Laravel Passport. In the root directory of your project and run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ composer require laravel/passport

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will install Laravel Passport to the project. Open the &lt;code&gt;User&lt;/code&gt; model in the &lt;code&gt;app&lt;/code&gt; directory and &lt;code&gt;use&lt;/code&gt; the &lt;code&gt;HasApiTokens&lt;/code&gt; trait:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?php&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Laravel&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Passport&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;HasApiTokens&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;User&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;extends&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Authenticatable&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;HasApiTokens&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-title"&amp;gt;Notifiable&amp;lt;/span&amp;gt;;

        &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next open the &lt;code&gt;AuthServiceProvider&lt;/code&gt; class in the &lt;code&gt;app/Providers&lt;/code&gt; directory and update it to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?php&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Laravel&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Passport&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Passport&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;AuthServiceProvider&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;extends&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;ServiceProvider&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;boot&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;

            Passport::routes();
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Open the &lt;code&gt;config/auth.php&lt;/code&gt; configuration file and set the &lt;code&gt;driver&lt;/code&gt; option of the &lt;code&gt;api&lt;/code&gt; authentication guard to &lt;code&gt;passport&lt;/code&gt;. This will instruct your application to use Passport's &lt;code&gt;TokenGuard&lt;/code&gt; when authenticating incoming API requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-string"&amp;gt;'guards'&amp;lt;/span&amp;gt; =&amp;gt; [
        &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;

        &amp;lt;span class="hljs-string"&amp;gt;'api'&amp;lt;/span&amp;gt; =&amp;gt; [
            &amp;lt;span class="hljs-string"&amp;gt;'driver'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'passport'&amp;lt;/span&amp;gt;,
            &amp;lt;span class="hljs-string"&amp;gt;'provider'&amp;lt;/span&amp;gt; =&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;'users'&amp;lt;/span&amp;gt;,
        ],
    ],
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To complete the installation, run the commands below, which will perform a migration and install Laravel Passport to your application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ php artisan migrate
    $ php artisan passport:install

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/3W31pKCdfW6Y8Eg4CYWqeO/6bca50e55ca38843ac3e82e0879797e0/ios-push-notifications-social-network-passport.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/3W31pKCdfW6Y8Eg4CYWqeO/6bca50e55ca38843ac3e82e0879797e0/ios-push-notifications-social-network-passport.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Passport is successfully installed after the commands finish execution. The &lt;code&gt;passport:install&lt;/code&gt; command will create two files in the &lt;code&gt;storage&lt;/code&gt; directory: &lt;code&gt;oauth-public.key&lt;/code&gt; and &lt;code&gt;oauth-private.key&lt;/code&gt;. These keys will be used to sign and validate access tokens.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Copy and save the client ID and secret for the second client as you will need it later in the article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Adding push notification support
&lt;/h3&gt;

&lt;p&gt;The next thing we want to do is add push notification support. For this, we will be using &lt;a href="https://pusher.com/beams"&gt;Pusher Beams&lt;/a&gt;. For convenience, we will be using a PHP library that is a Laravel supported wrapper for the &lt;a href="https://packagist.org/packages/pusher/pusher-push-notifications"&gt;Pusher Beams PHP library&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In your terminal run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ composer require neo/pusher-beams

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When the installation is completed, open the &lt;code&gt;.env&lt;/code&gt; file and add the following keys to the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    PUSHER_BEAMS_SECRET_KEY="PUSHER_BEAMS_SECRET_KEY"
    PUSHER_BEAMS_INSTANCE_ID="PUSHER_BEAMS_INSTANCE_ID"

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 You need to replace the &lt;code&gt;PUSHER_BEAMS_SECRET_KEY&lt;/code&gt; and &lt;code&gt;PUSHER_BEAMS_INSTANCE_ID&lt;/code&gt; keys with the keys gotten from your Pusher dashboard.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Open the &lt;code&gt;broadcasting.php&lt;/code&gt; file in the &lt;code&gt;config&lt;/code&gt; directory and add the following keys to the pusher connection array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-string"&amp;gt;'connections'&amp;lt;/span&amp;gt; =&amp;gt; [
        &amp;lt;span class="hljs-string"&amp;gt;'pusher'&amp;lt;/span&amp;gt; =&amp;gt; [
            &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;

            &amp;lt;span class="hljs-string"&amp;gt;'beams'&amp;lt;/span&amp;gt; =&amp;gt; [
                &amp;lt;span class="hljs-string"&amp;gt;'secret_key'&amp;lt;/span&amp;gt; =&amp;gt; env(&amp;lt;span class="hljs-string"&amp;gt;'PUSHER_BEAMS_SECRET_KEY'&amp;lt;/span&amp;gt;),
                &amp;lt;span class="hljs-string"&amp;gt;'instance_id'&amp;lt;/span&amp;gt; =&amp;gt; env(&amp;lt;span class="hljs-string"&amp;gt;'PUSHER_BEAMS_INSTANCE_ID'&amp;lt;/span&amp;gt;),
            ],

            &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;
        ],
    ],
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next, create a new notification class where we will add our push notification. In your terminal run the command below to create the class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ php artisan make:notification UserCommented

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will create a new &lt;code&gt;UserCommented&lt;/code&gt; class in the &lt;code&gt;app/Notifications&lt;/code&gt; directory. Open the file and replace the contents with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;?php&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-keyword"&amp;gt;namespace&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Notifications&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Bus&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Queueable&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Illuminate&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Notifications&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Notification&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Neo&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;PusherBeams&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;PusherBeams&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Neo&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;PusherBeams&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;PusherMessage&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;User&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;PhotoComment&amp;lt;/span&amp;gt;;
    &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;App&amp;lt;/span&amp;gt;\&amp;lt;span class="hljs-title"&amp;gt;Photo&amp;lt;/span&amp;gt;;

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;UserCommented&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;extends&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Notification&amp;lt;/span&amp;gt;
    &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;use&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Queueable&amp;lt;/span&amp;gt;;

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; $user;

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; $comment;

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; $photo;

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;__construct&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(User $user, Photo $photo, PhotoComment $comment)&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;user = $user;
            &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;photo = $photo;
            &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;comment = $comment;
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;via&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;($notifiable)&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; [PusherBeams::class];
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;toPushNotification&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;($notifiable)&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; PusherMessage::create()
                -&amp;gt;iOS()
                -&amp;gt;sound(&amp;lt;span class="hljs-string"&amp;gt;'success'&amp;lt;/span&amp;gt;)
                -&amp;gt;title(&amp;lt;span class="hljs-string"&amp;gt;'New Comment'&amp;lt;/span&amp;gt;)
                -&amp;gt;body(&amp;lt;span class="hljs-string"&amp;gt;"{$this-&amp;gt;user-&amp;gt;name} commented on your photo: {$this-&amp;gt;comment-&amp;gt;comment}"&amp;lt;/span&amp;gt;)
                -&amp;gt;setOption(&amp;lt;span class="hljs-string"&amp;gt;'apns.aps.mutable-content'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;1&amp;lt;/span&amp;gt;)
                -&amp;gt;setOption(&amp;lt;span class="hljs-string"&amp;gt;'apns.data.attachment-url'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;photo-&amp;gt;image);
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;public&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;pushNotificationInterest&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;
        &amp;lt;/span&amp;gt;{
            $id = &amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;photo-&amp;gt;id;

            $audience = strtolower(&amp;lt;span class="hljs-keyword"&amp;gt;$this&amp;lt;/span&amp;gt;-&amp;gt;user-&amp;gt;settings-&amp;gt;notification_comments);

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;"photo_{$id}-comment_{$audience}"&amp;lt;/span&amp;gt;;
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the class above we are extending a &lt;code&gt;Notification&lt;/code&gt; class and we have implemented the &lt;code&gt;toPushNotification&lt;/code&gt; method, which will be used to send the push notification when required. In the &lt;code&gt;via&lt;/code&gt; method, we specify what channels we want to send the notification through and in the &lt;code&gt;pushNotificationInterest&lt;/code&gt; we specify the interest we want to publish the push notification to.&lt;/p&gt;

&lt;p&gt;If you remember earlier, we invoked the notification on line 30 of the &lt;code&gt;PhotoCommentController&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Read more about &lt;a href="https://laravel.com/docs/5.6/notifications"&gt;Laravel Notifications&lt;/a&gt; and how it works.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s it. The backend application is complete. To start serving the application, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ php artisan serve

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will start a PHP server running on port 8000.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building our iOS application using Swift
&lt;/h2&gt;

&lt;p&gt;Now that we have a backend server that can serve us all the information we want and also send push notifications, let us create our iOS application, which will be the client application.&lt;/p&gt;

&lt;p&gt;Launch Xcode and create a new ‘Single Page App’ project. Let's call it &lt;strong&gt;Gram&lt;/strong&gt;. When the project is created, exit Xcode and &lt;code&gt;cd&lt;/code&gt; to the root of the project using a terminal. In the root of the project create a &lt;code&gt;Podfile&lt;/code&gt; and paste the following into the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    platform :ios, '11.0'

    target 'Gram' do
      use_frameworks!

      pod 'Alamofire', '~&amp;gt; 4.7.1'
      pod 'PushNotifications', '~&amp;gt; 0.10.6'
      pod 'NotificationBannerSwift'
    end

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then run the command below to start installing the dependencies we defined above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ pod install

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When the installation is complete, we will have a new &lt;code&gt;.xcworkspace&lt;/code&gt; file in the root of the project. Double-click the workspace file to relaunch Xcode.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating our storyboard
&lt;/h3&gt;

&lt;p&gt;Next, let’s create our storyboard. Open your &lt;code&gt;Main.storyboard&lt;/code&gt; file. We want to design it to look similar to this:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/3Q8zckYGGk8GaacuGWqS8a/dcb1a02957daf499b62218111bf2e12e/ios-push-notifications-social-network-main-storyboard.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/3Q8zckYGGk8GaacuGWqS8a/dcb1a02957daf499b62218111bf2e12e/ios-push-notifications-social-network-main-storyboard.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How the storyboard scenes are connected
&lt;/h3&gt;

&lt;p&gt;The first scene we have a launch view controller. This controller connects to the login scene, register scene or the main navigation controller depending on the login status of the user. The login and register scenes are basic and they simply authenticate the user.&lt;/p&gt;

&lt;p&gt;The main navigation controller connects to the main controller that displays the timeline. From that scene, there are connections to the settings scene, the search scene, and the view comments scene. Each segue connection is given an identifier so we can present them from the controller code.&lt;/p&gt;

&lt;p&gt;When you are done creating the storyboard, let’s create the custom classes for each storyboard scene.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating our models
&lt;/h3&gt;

&lt;p&gt;To help us with managing our API’s JSON responses we will be using &lt;a href="https://paper.dropbox.com/doc/Swift-4-decoding-JSON-using-Codable-o9T8RFPWqR3nhodGEt0d1"&gt;Codable in Swift 4&lt;/a&gt;. This will make it extremely easy for us to manage the responses from the API.&lt;/p&gt;

&lt;p&gt;Create a new file named &lt;code&gt;Models.swift&lt;/code&gt; and paste this in the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; Foundation

    &amp;lt;span class="hljs-keyword"&amp;gt;typealias&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Users&amp;lt;/span&amp;gt; = [&amp;lt;span class="hljs-type"&amp;gt;User&amp;lt;/span&amp;gt;]
    &amp;lt;span class="hljs-keyword"&amp;gt;typealias&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Photos&amp;lt;/span&amp;gt; = [&amp;lt;span class="hljs-type"&amp;gt;Photo&amp;lt;/span&amp;gt;]
    &amp;lt;span class="hljs-keyword"&amp;gt;typealias&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;PhotoComments&amp;lt;/span&amp;gt; = [&amp;lt;span class="hljs-type"&amp;gt;PhotoComment&amp;lt;/span&amp;gt;]

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;struct&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;User&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;Codable&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; id: &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; name: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; email: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; follows: &amp;lt;span class="hljs-type"&amp;gt;Bool&amp;lt;/span&amp;gt;?
    }

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;struct&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Photo&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;Codable&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; id: &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; user: &amp;lt;span class="hljs-type"&amp;gt;User&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; image: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; caption: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; comments: &amp;lt;span class="hljs-type"&amp;gt;PhotoComments&amp;lt;/span&amp;gt;    
    }

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;struct&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;PhotoComment&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;Codable&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; id: &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; user: &amp;lt;span class="hljs-type"&amp;gt;User&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; photo_id: &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; user_id: &amp;lt;span class="hljs-type"&amp;gt;Int&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; comment: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating our services
&lt;/h3&gt;

&lt;p&gt;Our services will contain code that we will need to make calls to the API and also other functionality that interacts with the application view.&lt;/p&gt;

&lt;p&gt;Create a new class &lt;code&gt;SettingsService&lt;/code&gt; and paste the following code into the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; Foundation

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;SettingsService&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;NSObject&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{

        &amp;lt;span class="hljs-keyword"&amp;gt;static&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; shared = &amp;lt;span class="hljs-type"&amp;gt;SettingsService&amp;lt;/span&amp;gt;()    
        &amp;lt;span class="hljs-keyword"&amp;gt;static&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; key = &amp;lt;span class="hljs-string"&amp;gt;"gram.settings.notifications"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; settings: [&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;] = [:];

        &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; allSettings: [&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;] {
            &amp;lt;span class="hljs-keyword"&amp;gt;set&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.settings = newValue
            }
            &amp;lt;span class="hljs-keyword"&amp;gt;get&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; settings = loadFromDefaults(), settings[&amp;lt;span class="hljs-string"&amp;gt;"notification_comments"&amp;lt;/span&amp;gt;] != &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; {
                    &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; settings
                }

                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; [
                    &amp;lt;span class="hljs-string"&amp;gt;"notification_comments"&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-type"&amp;gt;Setting&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-type"&amp;gt;Notification&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-type"&amp;gt;Comments&amp;lt;/span&amp;gt;.following.&amp;lt;span class="hljs-built_in"&amp;gt;toString&amp;lt;/span&amp;gt;()
                ];
            }
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;init&amp;lt;/span&amp;gt;() {
            &amp;lt;span class="hljs-keyword"&amp;gt;super&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-keyword"&amp;gt;init&amp;lt;/span&amp;gt;()
            &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.settings = &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.allSettings
        }

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;loadFromDefaults&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; [&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;]? {
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;UserDefaults&amp;lt;/span&amp;gt;.standard.dictionary(forKey: &amp;lt;span class="hljs-type"&amp;gt;SettingsService&amp;lt;/span&amp;gt;.key) &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;? [&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;]
        }

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;loadFromApi&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-type"&amp;gt;ApiService&amp;lt;/span&amp;gt;.shared.loadSettings { settings &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; settings = settings {
                    &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.allSettings = settings
                    &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.saveSettings(saveRemotely: &amp;lt;span class="hljs-literal"&amp;gt;false&amp;lt;/span&amp;gt;)
                }
            }
        }

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;updateCommentsNotificationSetting&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; status: Setting.Notification.Comments)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.allSettings[&amp;lt;span class="hljs-string"&amp;gt;"notification_comments"&amp;lt;/span&amp;gt;] = status.&amp;lt;span class="hljs-built_in"&amp;gt;toString&amp;lt;/span&amp;gt;()
            saveSettings()
        }

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;saveSettings&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(saveRemotely: Bool = &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-type"&amp;gt;UserDefaults&amp;lt;/span&amp;gt;.standard.&amp;lt;span class="hljs-keyword"&amp;gt;set&amp;lt;/span&amp;gt;(settings, forKey: &amp;lt;span class="hljs-type"&amp;gt;SettingsService&amp;lt;/span&amp;gt;.key)

            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; saveRemotely == &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-type"&amp;gt;ApiService&amp;lt;/span&amp;gt;.shared.saveSettings(settings: settings) { &amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt; }
            }
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the class above we have defined the settings service. The class is how we manage settings for our application. In the &lt;code&gt;allSettings&lt;/code&gt; setter, we attempt to fetch the settings from the local store and if we cant, we return some sensible defaults.&lt;/p&gt;

&lt;p&gt;We have the &lt;code&gt;loadFromDefaults&lt;/code&gt; method that loads the settings locally from the &lt;code&gt;UserDefaults&lt;/code&gt;, the &lt;code&gt;loadFromApi&lt;/code&gt; class that loads settings from the API using the &lt;code&gt;ApiService&lt;/code&gt;, the &lt;code&gt;updateCommentsNotificationSetting&lt;/code&gt;, which updates the comment notification settings. Finally, we have the &lt;code&gt;saveSettings&lt;/code&gt; method that saves the comment locally and remotely.&lt;/p&gt;

&lt;p&gt;In the same file, add the following &lt;code&gt;enum&lt;/code&gt; to the bottom:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;enum&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Setting&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{ 
        &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;enum&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Notification&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{            
            &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;enum&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;Comments&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;String&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
                &amp;lt;span class="hljs-keyword"&amp;gt;case&amp;lt;/span&amp;gt; off = &amp;lt;span class="hljs-string"&amp;gt;"Off"&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-keyword"&amp;gt;case&amp;lt;/span&amp;gt; everyone = &amp;lt;span class="hljs-string"&amp;gt;"Everyone"&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-keyword"&amp;gt;case&amp;lt;/span&amp;gt; following = &amp;lt;span class="hljs-string"&amp;gt;"Following"&amp;lt;/span&amp;gt;

                &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;toString&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt; {
                    &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.rawValue
                }
            }
        }    
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The enum is basically a representation of the available settings for our comment notifications.&lt;/p&gt;

&lt;p&gt;The next service we want to define is the &lt;code&gt;AuthService&lt;/code&gt;. This service is used to authenticate users of our service. Create a new &lt;code&gt;AuthService&lt;/code&gt; class and paste the following code into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; Foundation

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;AuthService&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;NSObject&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;static&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; key = &amp;lt;span class="hljs-string"&amp;gt;"gram-token"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;static&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; shared = &amp;lt;span class="hljs-type"&amp;gt;AuthService&amp;lt;/span&amp;gt;()

        &amp;lt;span class="hljs-keyword"&amp;gt;typealias&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;AccessToken&amp;lt;/span&amp;gt; = &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;    
        &amp;lt;span class="hljs-keyword"&amp;gt;typealias&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;LoginCredentials&amp;lt;/span&amp;gt; = (email: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;, password: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;)
        &amp;lt;span class="hljs-keyword"&amp;gt;typealias&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;SignupCredentials&amp;lt;/span&amp;gt; = (name: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;, email: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;, password: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;)

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;init&amp;lt;/span&amp;gt;() {
            &amp;lt;span class="hljs-keyword"&amp;gt;super&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-keyword"&amp;gt;init&amp;lt;/span&amp;gt;()
        }

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;loggedIn&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Bool&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; getToken() != &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;
        }

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;logout&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-type"&amp;gt;UserDefaults&amp;lt;/span&amp;gt;.standard.removeObject(forKey: &amp;lt;span class="hljs-type"&amp;gt;AuthService&amp;lt;/span&amp;gt;.key)
        }

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;getToken&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;AccessToken&amp;lt;/span&amp;gt;? {
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;UserDefaults&amp;lt;/span&amp;gt;.standard.string(forKey: &amp;lt;span class="hljs-type"&amp;gt;AuthService&amp;lt;/span&amp;gt;.key)
        }

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;saveToken&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; token: AccessToken)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;AuthService&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-type"&amp;gt;UserDefaults&amp;lt;/span&amp;gt;.standard.&amp;lt;span class="hljs-keyword"&amp;gt;set&amp;lt;/span&amp;gt;(token, forKey: &amp;lt;span class="hljs-type"&amp;gt;AuthService&amp;lt;/span&amp;gt;.key)
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;
        }

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;deleteToken&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;AuthService&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-type"&amp;gt;UserDefaults&amp;lt;/span&amp;gt;.standard.removeObject(forKey: &amp;lt;span class="hljs-type"&amp;gt;AuthService&amp;lt;/span&amp;gt;.key)
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;
        }

        &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;then&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(completion: @escaping&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Void&amp;lt;/span&amp;gt;) {
            completion()
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The class above is fairly straightforward and it provides methods for authentication. It has the &lt;code&gt;getToken&lt;/code&gt; and &lt;code&gt;saveToken&lt;/code&gt;, which essentially retrieves and saves the access token gotten after authenticating the user.&lt;/p&gt;

&lt;p&gt;Next, let’s create our final service, the &lt;code&gt;ApiService&lt;/code&gt;. Create a new class &lt;code&gt;ApiService&lt;/code&gt; and paste the following into the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; Foundation
    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; Alamofire

    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;ApiService&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;NSObject&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{

        &amp;lt;span class="hljs-keyword"&amp;gt;static&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; shared = &amp;lt;span class="hljs-type"&amp;gt;ApiService&amp;lt;/span&amp;gt;()

        &amp;lt;span class="hljs-keyword"&amp;gt;override&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;init&amp;lt;/span&amp;gt;() {
            &amp;lt;span class="hljs-keyword"&amp;gt;super&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-keyword"&amp;gt;init&amp;lt;/span&amp;gt;()
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now that we have the base of the class, let’s start adding methods to the class. Since it is a large class, we will split adding the methods over a few paragraphs.&lt;/p&gt;

&lt;p&gt;In the class, let’s add our first set of methods, which will handle authentication:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;login&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(credentials: AuthService.LoginCredentials, completion: @escaping&amp;lt;span class="hljs-params"&amp;gt;(AuthService.AccessToken?, ApiError?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Void&amp;lt;/span&amp;gt;) {
        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; params = [
            &amp;lt;span class="hljs-string"&amp;gt;"username"&amp;lt;/span&amp;gt;: credentials.email,
            &amp;lt;span class="hljs-string"&amp;gt;"password"&amp;lt;/span&amp;gt;: credentials.password,
            &amp;lt;span class="hljs-string"&amp;gt;"grant_type"&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-string"&amp;gt;"password"&amp;lt;/span&amp;gt;,
            &amp;lt;span class="hljs-string"&amp;gt;"client_id"&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-type"&amp;gt;AppConstants&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-type"&amp;gt;API_CLIENT_ID&amp;lt;/span&amp;gt;,
            &amp;lt;span class="hljs-string"&amp;gt;"client_secret"&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-type"&amp;gt;AppConstants&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-type"&amp;gt;API_CLIENT_SECRET&amp;lt;/span&amp;gt;
        ]

        request(.post, url: &amp;lt;span class="hljs-string"&amp;gt;"/oauth/token"&amp;lt;/span&amp;gt;, params: params, auth: &amp;lt;span class="hljs-literal"&amp;gt;false&amp;lt;/span&amp;gt;) { data &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; data = data &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; { &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; completion(&amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;, .badCredentials) }
            &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; token = data[&amp;lt;span class="hljs-string"&amp;gt;"access_token"&amp;lt;/span&amp;gt;] &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; { &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; completion(&amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;, .badResponse) }

            completion(token, &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)
        }
    }

    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;signup&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(credentials: AuthService.SignupCredentials, completion: @escaping&amp;lt;span class="hljs-params"&amp;gt;(AuthService.AccessToken?, ApiError?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Void&amp;lt;/span&amp;gt;) {
        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; params = [
            &amp;lt;span class="hljs-string"&amp;gt;"name"&amp;lt;/span&amp;gt;: credentials.name,
            &amp;lt;span class="hljs-string"&amp;gt;"email"&amp;lt;/span&amp;gt;: credentials.email,
            &amp;lt;span class="hljs-string"&amp;gt;"password"&amp;lt;/span&amp;gt;: credentials.password
        ]

        request(.post, url: &amp;lt;span class="hljs-string"&amp;gt;"/api/register"&amp;lt;/span&amp;gt;, params: params, auth: &amp;lt;span class="hljs-literal"&amp;gt;false&amp;lt;/span&amp;gt;) { data &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; res = data, &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; data = res[&amp;lt;span class="hljs-string"&amp;gt;"data"&amp;lt;/span&amp;gt;] &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;? [&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;:&amp;lt;span class="hljs-type"&amp;gt;AnyObject&amp;lt;/span&amp;gt;] &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; completion(&amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;, .badCredentials)
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; token = data[&amp;lt;span class="hljs-string"&amp;gt;"access_token"&amp;lt;/span&amp;gt;] &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; completion(&amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;, .badResponse)
            }

            completion(token, &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next let’s add the methods for loading users, loading posts, loading comments and adding comments to the &lt;code&gt;ApiService&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;fetchUsers&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(completion: @escaping&amp;lt;span class="hljs-params"&amp;gt;(Users?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Void&amp;lt;/span&amp;gt;) {
        request(.&amp;lt;span class="hljs-keyword"&amp;gt;get&amp;lt;/span&amp;gt;, url: &amp;lt;span class="hljs-string"&amp;gt;"/api/users"&amp;lt;/span&amp;gt;) { data &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; data = &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.responseToJsonStringData(response: data) {
                &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; obj = &amp;lt;span class="hljs-keyword"&amp;gt;try&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;JSONDecoder&amp;lt;/span&amp;gt;().decode(&amp;lt;span class="hljs-type"&amp;gt;Users&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;, from: data) {
                    &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; completion(obj)
                }
            }

            completion(&amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)
        }
    }

    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;fetchPosts&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(completion: @escaping&amp;lt;span class="hljs-params"&amp;gt;(Photos?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Void&amp;lt;/span&amp;gt;) {
        request(.&amp;lt;span class="hljs-keyword"&amp;gt;get&amp;lt;/span&amp;gt;, url: &amp;lt;span class="hljs-string"&amp;gt;"/api/photos"&amp;lt;/span&amp;gt;) { data &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; data = &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.responseToJsonStringData(response: data) {
                &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; obj = &amp;lt;span class="hljs-keyword"&amp;gt;try&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;JSONDecoder&amp;lt;/span&amp;gt;().decode(&amp;lt;span class="hljs-type"&amp;gt;Photos&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;, from: data) {
                    &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; completion(obj)
                }
            }

            completion(&amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)
        }
    }

    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;fetchComments&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(forPhoto id: Int, completion: @escaping&amp;lt;span class="hljs-params"&amp;gt;(PhotoComments?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Void&amp;lt;/span&amp;gt;) {
        request(.&amp;lt;span class="hljs-keyword"&amp;gt;get&amp;lt;/span&amp;gt;, url: &amp;lt;span class="hljs-string"&amp;gt;"/api/photos/\(id)/comments"&amp;lt;/span&amp;gt;) { data &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; data = &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.responseToJsonStringData(response: data) {
                &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; obj = &amp;lt;span class="hljs-keyword"&amp;gt;try&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;JSONDecoder&amp;lt;/span&amp;gt;().decode(&amp;lt;span class="hljs-type"&amp;gt;PhotoComments&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;, from: data) {
                    &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; completion(obj)
                }
            }

            completion(&amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)
        }
    }

    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;leaveComment&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(forId id: Int, comment: String, completion: @escaping&amp;lt;span class="hljs-params"&amp;gt;(PhotoComment?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Void&amp;lt;/span&amp;gt;) {
        request(.post, url: &amp;lt;span class="hljs-string"&amp;gt;"/api/photos/\(id)/comments"&amp;lt;/span&amp;gt;, params: [&amp;lt;span class="hljs-string"&amp;gt;"comment"&amp;lt;/span&amp;gt;: comment]) { data &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; res = data, &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; data = res[&amp;lt;span class="hljs-string"&amp;gt;"data"&amp;lt;/span&amp;gt;] &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;? [&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-type"&amp;gt;AnyObject&amp;lt;/span&amp;gt;],
                &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; json = &amp;lt;span class="hljs-keyword"&amp;gt;try&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;JSONSerialization&amp;lt;/span&amp;gt;.data(withJSONObject: data, options: []),
                &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; jsonString = &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;(data: json, encoding: .utf8),
                &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; jsonData = jsonString.data(using: .utf8),
                &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; obj = &amp;lt;span class="hljs-keyword"&amp;gt;try&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;JSONDecoder&amp;lt;/span&amp;gt;().decode(&amp;lt;span class="hljs-type"&amp;gt;PhotoComment&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;, from: jsonData) {
                    &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; completion(obj)
            }

            completion(&amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the methods above, you’ll notice we decode the JSON response from the API into the appropriate model object. This makes it easier to work with in our controllers.&lt;/p&gt;

&lt;p&gt;The next methods we will add will be to follow or unfollow a user, load settings for a user and update settings for a user. Add the methods below to the &lt;code&gt;ApiService&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;toggleFollowStatus&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(forUserId id: Int, following: Bool, completion: @escaping&amp;lt;span class="hljs-params"&amp;gt;(Bool?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Void&amp;lt;/span&amp;gt;) {
        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; status = following ? &amp;lt;span class="hljs-string"&amp;gt;"unfollow"&amp;lt;/span&amp;gt; : &amp;lt;span class="hljs-string"&amp;gt;"follow"&amp;lt;/span&amp;gt;

        request(.post, url: &amp;lt;span class="hljs-string"&amp;gt;"/api/users/\((status))"&amp;lt;/span&amp;gt;, params: [&amp;lt;span class="hljs-string"&amp;gt;"following_id"&amp;lt;/span&amp;gt;: id]) { data &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; res = data &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;? [&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;], res[&amp;lt;span class="hljs-string"&amp;gt;"status"&amp;lt;/span&amp;gt;] == &amp;lt;span class="hljs-string"&amp;gt;"success"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; completion(&amp;lt;span class="hljs-literal"&amp;gt;false&amp;lt;/span&amp;gt;)
            }

            completion(&amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;)
        }
    }

    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;loadSettings&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(completion: @escaping&amp;lt;span class="hljs-params"&amp;gt;([String: String]?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Void&amp;lt;/span&amp;gt;) {
        request(.&amp;lt;span class="hljs-keyword"&amp;gt;get&amp;lt;/span&amp;gt;, url: &amp;lt;span class="hljs-string"&amp;gt;"/api/users/settings"&amp;lt;/span&amp;gt;) { data &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; settings = data &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;? [&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;] &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; completion(&amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)
            }

            completion(settings)
        }
    }

    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;saveSettings&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(settings: [String: String], completion: @escaping&amp;lt;span class="hljs-params"&amp;gt;(Bool)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Void&amp;lt;/span&amp;gt;) {
        request(.put, url: &amp;lt;span class="hljs-string"&amp;gt;"/api/users/settings"&amp;lt;/span&amp;gt;, params: settings) { data &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; res = data &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;? [&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;], res[&amp;lt;span class="hljs-string"&amp;gt;"status"&amp;lt;/span&amp;gt;] == &amp;lt;span class="hljs-string"&amp;gt;"success"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; completion(&amp;lt;span class="hljs-literal"&amp;gt;false&amp;lt;/span&amp;gt;)
            }

            completion(&amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;)
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The next method we want to add is the &lt;code&gt;uploadImage&lt;/code&gt; method. This method is responsible for taking the selected image and caption and sending it to the API for uploading. Add the method below to the &lt;code&gt;ApiService&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;uploadImage&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; image: Data, caption: String, name: String, completion: @escaping&amp;lt;span class="hljs-params"&amp;gt;(Photo?, ApiError?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Void&amp;lt;/span&amp;gt;) {
        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; url = &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.url(appending: &amp;lt;span class="hljs-string"&amp;gt;"/api/photos"&amp;lt;/span&amp;gt;)

        &amp;lt;span class="hljs-comment"&amp;gt;// Handles multipart data&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; multipartHandler: (&amp;lt;span class="hljs-type"&amp;gt;MultipartFormData&amp;lt;/span&amp;gt;) -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Void&amp;lt;/span&amp;gt; = { multipartFormData &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
            multipartFormData.append(caption.data(using: .utf8)!, withName: &amp;lt;span class="hljs-string"&amp;gt;"caption"&amp;lt;/span&amp;gt;)
            multipartFormData.append(image, withName: &amp;lt;span class="hljs-string"&amp;gt;"image"&amp;lt;/span&amp;gt;, fileName: name, mimeType: &amp;lt;span class="hljs-string"&amp;gt;"image/jpeg"&amp;lt;/span&amp;gt;)
        }

        &amp;lt;span class="hljs-type"&amp;gt;Alamofire&amp;lt;/span&amp;gt;.upload(
            multipartFormData: multipartHandler,
            usingThreshold: &amp;lt;span class="hljs-type"&amp;gt;UInt64&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-keyword"&amp;gt;init&amp;lt;/span&amp;gt;(),
            to: url,
            method: .post,
            headers: requestHeaders(),
            encodingCompletion: { encodingResult &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; uploadedHandler: (&amp;lt;span class="hljs-type"&amp;gt;DataResponse&amp;lt;/span&amp;gt;&amp;lt;&amp;lt;span class="hljs-type"&amp;gt;Any&amp;lt;/span&amp;gt;&amp;gt;) -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Void&amp;lt;/span&amp;gt; = { response &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
                    &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; response.result.isSuccess,
                        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; resp = response.result.value &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;? [&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-type"&amp;gt;AnyObject&amp;lt;/span&amp;gt;],
                        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; data = resp[&amp;lt;span class="hljs-string"&amp;gt;"data"&amp;lt;/span&amp;gt;] &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;? [&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-type"&amp;gt;AnyObject&amp;lt;/span&amp;gt;],
                        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; json = &amp;lt;span class="hljs-keyword"&amp;gt;try&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;JSONSerialization&amp;lt;/span&amp;gt;.data(withJSONObject: data, options: []),
                        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; jsonString = &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;(data: json, encoding: .utf8),
                        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; jsonData = jsonString.data(using: .utf8),
                        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; obj = &amp;lt;span class="hljs-keyword"&amp;gt;try&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;JSONDecoder&amp;lt;/span&amp;gt;().decode(&amp;lt;span class="hljs-type"&amp;gt;Photo&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;, from: jsonData) {
                            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; completion(obj, &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)
                    }

                    completion(&amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;, .uploadError(&amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;))
                }

                &amp;lt;span class="hljs-keyword"&amp;gt;switch&amp;lt;/span&amp;gt; encodingResult {
                &amp;lt;span class="hljs-keyword"&amp;gt;case&amp;lt;/span&amp;gt; .failure(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt;): completion(&amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;, .uploadError(&amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;))
                &amp;lt;span class="hljs-keyword"&amp;gt;case&amp;lt;/span&amp;gt; .success(&amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; upload, &amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt;): upload.responseJSON(completionHandler: uploadedHandler)
                }
            }
        )
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next let’s add the class’ helper methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;url&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(appending: URLConvertible)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;URLConvertible&amp;lt;/span&amp;gt; {
        &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;"\(AppConstants.API_URL)\(appending)"&amp;lt;/span&amp;gt;
    }

    &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;requestHeaders&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(auth: Bool = &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;HTTPHeaders&amp;lt;/span&amp;gt; {
        &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; headers: &amp;lt;span class="hljs-type"&amp;gt;HTTPHeaders&amp;lt;/span&amp;gt; = [&amp;lt;span class="hljs-string"&amp;gt;"Accept"&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-string"&amp;gt;"application/json"&amp;lt;/span&amp;gt;]

        &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; auth &amp;amp;&amp;amp; &amp;lt;span class="hljs-type"&amp;gt;AuthService&amp;lt;/span&amp;gt;.shared.loggedIn() {
            headers[&amp;lt;span class="hljs-string"&amp;gt;"Authorization"&amp;lt;/span&amp;gt;] = &amp;lt;span class="hljs-string"&amp;gt;"Bearer \(AuthService.shared.getToken()!)"&amp;lt;/span&amp;gt;
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; headers
    }

    &amp;lt;span class="hljs-keyword"&amp;gt;private&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;request&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-number"&amp;gt;_&amp;lt;/span&amp;gt; method: HTTPMethod, url: URLConvertible, params: Parameters? = &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;, auth: Bool = &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;, handler: @escaping &amp;lt;span class="hljs-params"&amp;gt;([String: AnyObject]?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Void&amp;lt;/span&amp;gt;) {
        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; url = &amp;lt;span class="hljs-keyword"&amp;gt;self&amp;lt;/span&amp;gt;.url(appending: url)

        &amp;lt;span class="hljs-type"&amp;gt;Alamofire&amp;lt;/span&amp;gt;
            .request(url, method: method, parameters: params, encoding: &amp;lt;span class="hljs-type"&amp;gt;JSONEncoding&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-keyword"&amp;gt;default&amp;lt;/span&amp;gt;, headers: requestHeaders(auth: auth))
            .validate()
            .responseJSON { resp &amp;lt;span class="hljs-keyword"&amp;gt;in&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-keyword"&amp;gt;guard&amp;lt;/span&amp;gt; resp.result.isSuccess, &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; data = resp.result.value &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;? [&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-type"&amp;gt;AnyObject&amp;lt;/span&amp;gt;] &amp;lt;span class="hljs-keyword"&amp;gt;else&amp;lt;/span&amp;gt; {
                    &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; handler(&amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;)
                }

                handler(data)
            }
    } 

    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;responseToJsonStringData&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(response data: [String: AnyObject]?)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; -&amp;gt; &amp;lt;span class="hljs-type"&amp;gt;Data&amp;lt;/span&amp;gt;? {
        &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; res = data, &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; data = res[&amp;lt;span class="hljs-string"&amp;gt;"data"&amp;lt;/span&amp;gt;] &amp;lt;span class="hljs-keyword"&amp;gt;as&amp;lt;/span&amp;gt;? [[&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-type"&amp;gt;AnyObject&amp;lt;/span&amp;gt;]] {
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; json = &amp;lt;span class="hljs-keyword"&amp;gt;try&amp;lt;/span&amp;gt;? &amp;lt;span class="hljs-type"&amp;gt;JSONSerialization&amp;lt;/span&amp;gt;.data(withJSONObject: data, options: []) {
                &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; jsonString = &amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;(data: json, encoding: .utf8), &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; data = jsonString.data(using: .utf8) {
                    &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; data
                }
            }
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt;
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;url&lt;/code&gt; method takes a URL path and appends the base API URL to it. The &lt;code&gt;requestHeaders&lt;/code&gt; method attaches the appropriate headers to the request sent by Alamofire. The &lt;code&gt;request&lt;/code&gt; method is a wrapper around Alamofire that sends requests to the API for us. The &lt;code&gt;responseToJsonStringData&lt;/code&gt; converts the data from our JSON file into a JSON string which can then be decoded into one of our &lt;code&gt;Codable&lt;/code&gt; models.&lt;/p&gt;

&lt;p&gt;One final thing we want to add to the bottom of the &lt;code&gt;ApiService&lt;/code&gt; class is the &lt;code&gt;enum&lt;/code&gt; for &lt;code&gt;ApiError&lt;/code&gt;s. In the same file at the bottom, add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-class"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;enum&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;ApiError&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title"&amp;gt;Error&amp;lt;/span&amp;gt; &amp;lt;/span&amp;gt;{
        &amp;lt;span class="hljs-keyword"&amp;gt;case&amp;lt;/span&amp;gt; badResponse
        &amp;lt;span class="hljs-keyword"&amp;gt;case&amp;lt;/span&amp;gt; badCredentials
        &amp;lt;span class="hljs-keyword"&amp;gt;case&amp;lt;/span&amp;gt; uploadError([&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;: [&amp;lt;span class="hljs-type"&amp;gt;String&amp;lt;/span&amp;gt;]]?)
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That’s all for the &lt;code&gt;ApiService&lt;/code&gt; and indeed all the applications services. In the next part we will continue building our iOS application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this first part of the article, we have seen how we can create an API for our social network application using Laravel. We also integrated push notifications on the server side using Pusher Beams.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/neo/send-push-notifications-in-a-social-network-ios-app---part-2-build-the-app-3d8p"&gt;next part&lt;/a&gt;, we will build the client IOS application using Swift. We will also integrate push notifications to our social network application using Pusher Beams.&lt;/p&gt;

&lt;p&gt;The source code to the application is on &lt;a href="https://github.com/neoighodaro-articles/pusher-beams-ios-social-network"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post first appeared on the &lt;a href="https://pusher.com/tutorials/social-notifications-ios-part-1"&gt;Pusher blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>pusher</category>
      <category>ios</category>
      <category>swift</category>
      <category>node</category>
    </item>
    <item>
      <title>Build a live comments feed with Go and Vue.js</title>
      <dc:creator>Neo</dc:creator>
      <pubDate>Mon, 19 Nov 2018 17:10:45 +0000</pubDate>
      <link>https://dev.to/neo/build-a-live-comments-feed-with-go-and-vuejs-2i7n</link>
      <guid>https://dev.to/neo/build-a-live-comments-feed-with-go-and-vuejs-2i7n</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;You will need Go, and SQLite installed on your machine. Basic knowledge of Go and JavaScript will be helpful.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The internet is a breeding ground for all kinds of social activities because it expands the possibilities of communication. In order to keep web applications social and enjoyable, it is important for them to have one or more interfaces for the users to interact through. One such interface is the comment section.&lt;/p&gt;

&lt;p&gt;The comment section is where users can discuss a subject (post, video, picture) that they have access to. In the past, for a user to see a comment from another user, the user would have to refresh the browser window. However, with realtime comments now we can automatically pull in comments live. This article will cover how we can build realtime commenting using Pusher.&lt;/p&gt;

&lt;p&gt;By the end of this article, we will have built an application that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/3dhebYl8aAm4cCME4Os2ys/4d39432a0bd39156cf8556c15649a2f1/go-comments-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/3dhebYl8aAm4cCME4Os2ys/4d39432a0bd39156cf8556c15649a2f1/go-comments-demo.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;To follow along with this article, you will need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go (version &amp;gt;= 0.10.x) installed on your computer. Heres how you can &lt;a href="https://golang.org/doc/install" rel="noopener noreferrer"&gt;install Go&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;SQLite (v3.x) installed on your machine. &lt;a href="http://www.sqlitetutorial.net/download-install-sqlite/" rel="noopener noreferrer"&gt;Installation guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Basic knowledge of the Go programming language.&lt;/li&gt;
&lt;li&gt;Basic knowledge of JavaScript (ES6).&lt;/li&gt;
&lt;li&gt;Basic knowledge of Vue.js.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting a Pusher Channels application
&lt;/h2&gt;

&lt;p&gt;The first step will be to get a Pusher Channels application. We will need the application credentials for our realtime features to work.&lt;/p&gt;

&lt;p&gt;Go to the Pusher website and create an account. After creating an account, you should create a new application. Follow the application creation wizard and then you should be given your application credentials, we will use this later in the article.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/2h3e4hp58cIW6acMc8Mgy0/dfeac8f3cdffb41174e93ad567233cd0/go-comments-app-keys.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/2h3e4hp58cIW6acMc8Mgy0/dfeac8f3cdffb41174e93ad567233cd0/go-comments-app-keys.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have our application, let’s move on to the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the codebase
&lt;/h2&gt;

&lt;p&gt;Let’s start by navigating into the &lt;code&gt;src&lt;/code&gt; directory located in the &lt;code&gt;$GOPATH&lt;/code&gt;. Then we’ll create a new directory for our app there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ cd $GOPATH/src
    $ mkdir go-realtime-comments
    $ cd go-realtime-comments

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;comments.go&lt;/code&gt; file in this directory.&lt;/p&gt;

&lt;p&gt;Before we write code, we need to import a few Go packages that will help run our projects. We will install the &lt;a href="https://echo.labstack.com" rel="noopener noreferrer"&gt;Echo framework&lt;/a&gt; and the &lt;a href="https://github.com/mattn/go-sqlite3" rel="noopener noreferrer"&gt;SQLite packages&lt;/a&gt;. Run the following commands to pull in the packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ go get github.com/labstack/echo
    $ go get github.com/labstack/echo/middleware
    $ go get github.com/mattn/go-sqlite3

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ If you use Windows and you encounter the error ‘cc.exe: sorry, unimplemented: 64-bit mode not compiled in ‘, then you need a Windows gcc port, such as &lt;a href="https://sourceforge.net/projects/mingw-w64/" rel="noopener noreferrer"&gt;https://sourceforge.net/projects/mingw-w64/&lt;/a&gt;. Also see this &lt;a href="https://github.com/mattn/go-sqlite3/issues/297" rel="noopener noreferrer"&gt;GitHub issue&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With your favorite editor, open the &lt;code&gt;comments.go&lt;/code&gt; file and paste in the following lines of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;package&amp;lt;/span&amp;gt; main

    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; (
        &amp;lt;span class="hljs-comment"&amp;gt;// "database/sql"&amp;lt;/span&amp;gt;

        &amp;lt;span class="hljs-string"&amp;gt;"github.com/labstack/echo"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-string"&amp;gt;"github.com/labstack/echo/middleware"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-comment"&amp;gt;// _ "github.com/mattn/go-sqlite3"&amp;lt;/span&amp;gt;
    )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuring the database and routes
&lt;/h2&gt;

&lt;p&gt;Every Go application must have a &lt;code&gt;main&lt;/code&gt; function. This is where the execution of the application will start from, so let’s create our &lt;code&gt;main&lt;/code&gt; function:&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;comments.go&lt;/code&gt; file, add the following below the imports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;main&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {

        &amp;lt;span class="hljs-comment"&amp;gt;// Echo instance&amp;lt;/span&amp;gt;
        e := echo.New()

        &amp;lt;span class="hljs-comment"&amp;gt;// Middleware&amp;lt;/span&amp;gt;
        e.Use(middleware.Logger())
        e.Use(middleware.Recover())

        &amp;lt;span class="hljs-comment"&amp;gt;// Define the HTTP routes&amp;lt;/span&amp;gt;
        e.GET(&amp;lt;span class="hljs-string"&amp;gt;"/comments"&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(c echo.Context)&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;error&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; c.JSON(&amp;lt;span class="hljs-number"&amp;gt;200&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;"GET Comments"&amp;lt;/span&amp;gt;)
        })

        e.POST(&amp;lt;span class="hljs-string"&amp;gt;"/comment"&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(c echo.Context)&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;error&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; c.JSON(&amp;lt;span class="hljs-number"&amp;gt;200&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;"POST a new Comment"&amp;lt;/span&amp;gt;)
        })

        &amp;lt;span class="hljs-comment"&amp;gt;// Start server&amp;lt;/span&amp;gt;
        e.Logger.Fatal(e.Start(&amp;lt;span class="hljs-string"&amp;gt;":9000"&amp;lt;/span&amp;gt;))
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the main function, we have defined some basic route handler functions, these functions basically return hard coded text to browser on request. The last line will start Go’s standard HTTP server using Echo’s start method and listen for requests port 9000.&lt;/p&gt;

&lt;p&gt;We can test that the application works at this stage by running it and making some requests using &lt;a href="https://www.getpostman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is how you can run the application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ go run ./comments.go

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can send HTTP requests using Postman. Here’s a sample GET request using Postman:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/1hOAHfzR8C2SocUciKSaEi/de4455b61abfaed1da7f612a6b1c75f6/go-comments-get-example.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/1hOAHfzR8C2SocUciKSaEi/de4455b61abfaed1da7f612a6b1c75f6/go-comments-get-example.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;POST request with Postman:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/1uCCDXGXBeus4mSKgSKYoi/a66e13b4921d54b799a3ceae40c2b4d1/go-comments-post-example.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/1uCCDXGXBeus4mSKgSKYoi/a66e13b4921d54b799a3ceae40c2b4d1/go-comments-post-example.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will create a function that will initialize a database and for that we need the SQL and SQLite3 drivers. We already added them to the &lt;code&gt;import&lt;/code&gt; statement so uncomment them. We will also create a function that will migrate the database using a database schema defined inside the function.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;comments.go&lt;/code&gt; file and paste the following code before the &lt;code&gt;main&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;initDB&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(filepath &amp;lt;span class="hljs-keyword"&amp;gt;string&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt; *&amp;lt;span class="hljs-title"&amp;gt;sql&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-title"&amp;gt;DB&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
        db, err := sql.Open(&amp;lt;span class="hljs-string"&amp;gt;"sqlite3"&amp;lt;/span&amp;gt;, filepath)
        &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; err != &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-built_in"&amp;gt;panic&amp;lt;/span&amp;gt;(err)
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; db == &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-built_in"&amp;gt;panic&amp;lt;/span&amp;gt;(&amp;lt;span class="hljs-string"&amp;gt;"db nil"&amp;lt;/span&amp;gt;)
        }
        &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; db
    }

    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;migrate&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(db *sql.DB)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
        sql := &amp;lt;span class="hljs-string"&amp;gt;`
        CREATE TABLE IF NOT EXISTS comments(
                id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
                name VARCHAR NOT NULL,
                email VARCHAR NOT NULL,
                comment VARCHAR NOT NULL
        );
       `&amp;lt;/span&amp;gt;
        _, err := db.Exec(sql)
        &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; err != &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-built_in"&amp;gt;panic&amp;lt;/span&amp;gt;(err)
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next add the following code to the top of the &lt;code&gt;main&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-comment"&amp;gt;// Initialize the database&amp;lt;/span&amp;gt;
    db := initDB(&amp;lt;span class="hljs-string"&amp;gt;"storage.db"&amp;lt;/span&amp;gt;)
    migrate(db)

    &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now check that these functions are being called and the database is created during execution by running the application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    go run comments.go

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ If you were already running the Go application you would need to kill the process using ctrl+c on your keyboard and then restart it to see changes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When the application is run for the first time, a &lt;code&gt;storage.db&lt;/code&gt; file will be created in the working directory if it did not previously exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the handlers
&lt;/h2&gt;

&lt;p&gt;We have tested that our application listens on the specified port 9000 and handles the HTTP requests as we configured it to. However, the current handler functions simply return hard-coded text to the browser so let’s create new handler functions to handle responses to the routes.&lt;/p&gt;

&lt;p&gt;Create a new folder in the root directory named &lt;code&gt;handlers&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ mkdir handlers
    $ cd handlers

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next create a &lt;code&gt;handlers.go&lt;/code&gt; file and paste the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;package&amp;lt;/span&amp;gt; handlers

    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; (
        &amp;lt;span class="hljs-string"&amp;gt;"database/sql"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-string"&amp;gt;"go-realtime-comments/models"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-string"&amp;gt;"net/http"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-string"&amp;gt;"github.com/labstack/echo"&amp;lt;/span&amp;gt;
    )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to go back to the &lt;code&gt;comments.go&lt;/code&gt; file and import the handlers package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    import (
        "go-realtime-comments/handlers"

        // [...]
    )

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the same file, replace the route definitions from earlier with the ones below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-comment"&amp;gt;// Define the HTTP routes&amp;lt;/span&amp;gt;
    e.File(&amp;lt;span class="hljs-string"&amp;gt;"/"&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;"public/index.html"&amp;lt;/span&amp;gt;)
    e.GET(&amp;lt;span class="hljs-string"&amp;gt;"/comments"&amp;lt;/span&amp;gt;, handlers.GetComments(db))
    e.POST(&amp;lt;span class="hljs-string"&amp;gt;"/comment"&amp;lt;/span&amp;gt;, handlers.PushComment(db))

    &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next paste the following code in the &lt;code&gt;handlers.go&lt;/code&gt; file below the import statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;type&amp;lt;/span&amp;gt; H &amp;lt;span class="hljs-keyword"&amp;gt;map&amp;lt;/span&amp;gt;[&amp;lt;span class="hljs-keyword"&amp;gt;string&amp;lt;/span&amp;gt;]&amp;lt;span class="hljs-keyword"&amp;gt;interface&amp;lt;/span&amp;gt;{}

    &amp;lt;span class="hljs-comment"&amp;gt;//GetComments handles the HTTP request that hits the /comments endpoint&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;GetComments&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(db *sql.DB)&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;echo&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-title"&amp;gt;HandlerFunc&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
        &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(c echo.Context)&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;error&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; c.JSON(http.StatusOK, models.GetComments(db))
        }
    }

    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;PushComment&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(db *sql.DB)&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;echo&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-title"&amp;gt;HandlerFunc&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
        &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(c echo.Context)&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;error&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; comment models.Comment

            c.Bind(&amp;amp;comment)

            id, err := models.PushComment(db, comment.Name, comment.Email, comment.Comment)
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; err == &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; c.JSON(http.StatusCreated, H{
                    &amp;lt;span class="hljs-string"&amp;gt;"created"&amp;lt;/span&amp;gt;: id,
                })
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; err
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;GetComments&lt;/code&gt; function fetches and returns comments from the database while the &lt;code&gt;PushComment&lt;/code&gt; saves comments to the database and returns a response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the models
&lt;/h2&gt;

&lt;p&gt;To create the model package, we need to create a new folder in the root directory of our application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ mkdir models
    $ cd models

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next create a &lt;code&gt;models.go&lt;/code&gt; file and paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;package&amp;lt;/span&amp;gt; models

    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; (
        &amp;lt;span class="hljs-string"&amp;gt;"database/sql"&amp;lt;/span&amp;gt;
        _ &amp;lt;span class="hljs-string"&amp;gt;"github.com/mattn/go-sqlite3"&amp;lt;/span&amp;gt;
    )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s create a Comment &lt;code&gt;type&lt;/code&gt;, which is a struct with four fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ID&lt;/code&gt; - the ID of the comment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Name&lt;/code&gt; - the username of the user who made the comment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Email&lt;/code&gt; - the email of the user who made the comment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Comment&lt;/code&gt; - the comment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Go, we can add metadata to variables by putting them within backticks. We can use this to define what each field should look like when converted to &lt;code&gt;JSON&lt;/code&gt;. This will also help the &lt;code&gt;c.Bind&lt;/code&gt; function know how to map &lt;code&gt;JSON&lt;/code&gt; data when registering a new comment.&lt;/p&gt;

&lt;p&gt;Let’s define the structs for &lt;code&gt;Comment&lt;/code&gt; and &lt;code&gt;CommentCollection&lt;/code&gt;. In the &lt;code&gt;models.go&lt;/code&gt; file paste in the following below the imports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;type&amp;lt;/span&amp;gt; Comment &amp;lt;span class="hljs-keyword"&amp;gt;struct&amp;lt;/span&amp;gt; {
        ID      &amp;lt;span class="hljs-keyword"&amp;gt;int&amp;lt;/span&amp;gt;    &amp;lt;span class="hljs-string"&amp;gt;`json:"id"`&amp;lt;/span&amp;gt;
        Name    &amp;lt;span class="hljs-keyword"&amp;gt;string&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;`json:"name"`&amp;lt;/span&amp;gt;
        Email   &amp;lt;span class="hljs-keyword"&amp;gt;string&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;`json:"email"`&amp;lt;/span&amp;gt;
        Comment &amp;lt;span class="hljs-keyword"&amp;gt;string&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;`json:"comment"`&amp;lt;/span&amp;gt;
    }

    &amp;lt;span class="hljs-keyword"&amp;gt;type&amp;lt;/span&amp;gt; CommentCollection &amp;lt;span class="hljs-keyword"&amp;gt;struct&amp;lt;/span&amp;gt; {
        Comments []Comment &amp;lt;span class="hljs-string"&amp;gt;`json:"items"`&amp;lt;/span&amp;gt;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, paste in the following code after the structs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;GetComments&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(db *sql.DB)&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;CommentCollection&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
        sql := &amp;lt;span class="hljs-string"&amp;gt;"SELECT * FROM comments"&amp;lt;/span&amp;gt;
        rows, err := db.Query(sql)

        &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; err != &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-built_in"&amp;gt;panic&amp;lt;/span&amp;gt;(err)
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;defer&amp;lt;/span&amp;gt; rows.Close()

        result := CommentCollection{}

        &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt; rows.Next() {

            comment := Comment{}
            err2 := rows.Scan(&amp;amp;comment.ID, &amp;amp;comment.Name, &amp;amp;comment.Email, &amp;amp;comment.Comment)
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; err2 != &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-built_in"&amp;gt;panic&amp;lt;/span&amp;gt;(err2)
            }

            result.Comments = &amp;lt;span class="hljs-built_in"&amp;gt;append&amp;lt;/span&amp;gt;(result.Comments, comment)
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; result
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;GetComments&lt;/code&gt; function is responsible for retrieving all the available comments from the database and returning them as an instance of the &lt;code&gt;CommentCollection&lt;/code&gt; that we defined.&lt;/p&gt;

&lt;p&gt;Next, paste in the following code below the one above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;PushComment&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(db *sql.DB, name &amp;lt;span class="hljs-keyword"&amp;gt;string&amp;lt;/span&amp;gt;, email &amp;lt;span class="hljs-keyword"&amp;gt;string&amp;lt;/span&amp;gt;, comment &amp;lt;span class="hljs-keyword"&amp;gt;string&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-keyword"&amp;gt;int64&amp;lt;/span&amp;gt;, error)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
        sql := &amp;lt;span class="hljs-string"&amp;gt;"INSERT INTO comments(name, email, comment) VALUES(?, ?, ?)"&amp;lt;/span&amp;gt;
        stmt, err := db.Prepare(sql)
        &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; err != &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-built_in"&amp;gt;panic&amp;lt;/span&amp;gt;(err)
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;defer&amp;lt;/span&amp;gt; stmt.Close()

        result, err2 := stmt.Exec(name, email, comment)
        &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; err2 != &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-built_in"&amp;gt;panic&amp;lt;/span&amp;gt;(err2)
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; result.LastInsertId()
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;PushComments&lt;/code&gt; function adds a new comment to the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the frontend
&lt;/h2&gt;

&lt;p&gt;Next, create a &lt;code&gt;public&lt;/code&gt; folder in our application’s root directory and create an &lt;code&gt;index.html&lt;/code&gt; file inside it.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;index.html&lt;/code&gt; file and paste in this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;html&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;lang&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"en"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;head&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;meta&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;charset&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"UTF-8"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;meta&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;name&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"viewport"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;content&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"width=device-width, initial-scale=1.0"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;meta&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;http-equiv&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"X-UA-Compatible"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;content&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"ie=edge"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;link&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;rel&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"stylesheet"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;href&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;integrity&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;crossorigin&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"anonymous"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;title&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;Realtime comments&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;title&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;script&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;src&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"https://unpkg.com/axios/dist/axios.min.js"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="undefined"&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;script&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;script&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;src&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="undefined"&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;script&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;style&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="css"&amp;gt;
          @&amp;lt;span class="hljs-keyword"&amp;gt;media&amp;lt;/span&amp;gt; (min-width: &amp;lt;span class="hljs-number"&amp;gt;48em&amp;lt;/span&amp;gt;) {
            &amp;lt;span class="hljs-selector-tag"&amp;gt;html&amp;lt;/span&amp;gt; {
              &amp;lt;span class="hljs-attribute"&amp;gt;font-size&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;18px&amp;lt;/span&amp;gt;;
            }
          }
          &amp;lt;span class="hljs-selector-tag"&amp;gt;body&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-attribute"&amp;gt;font-family&amp;lt;/span&amp;gt;: Georgia, &amp;lt;span class="hljs-string"&amp;gt;"Times New Roman"&amp;lt;/span&amp;gt;, Times, serif;
            &amp;lt;span class="hljs-attribute"&amp;gt;color&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;#555&amp;lt;/span&amp;gt;;
          }
          &amp;lt;span class="hljs-selector-tag"&amp;gt;h1&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-selector-class"&amp;gt;.h1&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-selector-tag"&amp;gt;h2&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-selector-class"&amp;gt;.h2&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-selector-tag"&amp;gt;h3&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-selector-class"&amp;gt;.h3&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-selector-tag"&amp;gt;h4&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-selector-class"&amp;gt;.h4&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-selector-tag"&amp;gt;h5&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-selector-class"&amp;gt;.h5&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-selector-tag"&amp;gt;h6&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-selector-class"&amp;gt;.h6&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-attribute"&amp;gt;font-family&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-string"&amp;gt;"Helvetica Neue"&amp;lt;/span&amp;gt;, Helvetica, Arial, sans-serif;
            &amp;lt;span class="hljs-attribute"&amp;gt;font-weight&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;400&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;color&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;#333&amp;lt;/span&amp;gt;;
          }
          &amp;lt;span class="hljs-selector-class"&amp;gt;.blog-masthead&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-attribute"&amp;gt;margin-bottom&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;3rem&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;background-color&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;#428bca&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;box-shadow&amp;lt;/span&amp;gt;: inset &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt; -.&amp;lt;span class="hljs-number"&amp;gt;1rem&amp;lt;/span&amp;gt; .&amp;lt;span class="hljs-number"&amp;gt;25rem&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-built_in"&amp;gt;rgba&amp;lt;/span&amp;gt;(0,0,0,.1);
          }
          &amp;lt;span class="hljs-selector-class"&amp;gt;.nav-link&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-attribute"&amp;gt;position&amp;lt;/span&amp;gt;: relative;
            &amp;lt;span class="hljs-attribute"&amp;gt;padding&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;1rem&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;font-weight&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;500&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;color&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;#cdddeb&amp;lt;/span&amp;gt;;
          }
          &amp;lt;span class="hljs-selector-class"&amp;gt;.nav-link&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-selector-pseudo"&amp;gt;:hover&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-selector-class"&amp;gt;.nav-link&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-selector-pseudo"&amp;gt;:focus&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-attribute"&amp;gt;color&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;#fff&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;background-color&amp;lt;/span&amp;gt;: transparent;
          }
          &amp;lt;span class="hljs-selector-class"&amp;gt;.nav-link&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-selector-class"&amp;gt;.active&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-attribute"&amp;gt;color&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;#fff&amp;lt;/span&amp;gt;;
          }
          &amp;lt;span class="hljs-selector-class"&amp;gt;.nav-link&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-selector-class"&amp;gt;.active&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-selector-pseudo"&amp;gt;::after&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-attribute"&amp;gt;position&amp;lt;/span&amp;gt;: absolute;
            &amp;lt;span class="hljs-attribute"&amp;gt;bottom&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;left&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;50%&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;width&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;height&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;margin-left&amp;lt;/span&amp;gt;: -.&amp;lt;span class="hljs-number"&amp;gt;3rem&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;vertical-align&amp;lt;/span&amp;gt;: middle;
            &amp;lt;span class="hljs-attribute"&amp;gt;content&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-string"&amp;gt;""&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;border-right&amp;lt;/span&amp;gt;: .&amp;lt;span class="hljs-number"&amp;gt;3rem&amp;lt;/span&amp;gt; solid transparent;
            &amp;lt;span class="hljs-attribute"&amp;gt;border-bottom&amp;lt;/span&amp;gt;: .&amp;lt;span class="hljs-number"&amp;gt;3rem&amp;lt;/span&amp;gt; solid;
            &amp;lt;span class="hljs-attribute"&amp;gt;border-left&amp;lt;/span&amp;gt;: .&amp;lt;span class="hljs-number"&amp;gt;3rem&amp;lt;/span&amp;gt; solid transparent;
          }
          @&amp;lt;span class="hljs-keyword"&amp;gt;media&amp;lt;/span&amp;gt; (min-width: &amp;lt;span class="hljs-number"&amp;gt;40em&amp;lt;/span&amp;gt;) {
            &amp;lt;span class="hljs-selector-class"&amp;gt;.blog-title&amp;lt;/span&amp;gt; {
              &amp;lt;span class="hljs-attribute"&amp;gt;font-size&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;3.5rem&amp;lt;/span&amp;gt;;
            }
          }
          &amp;lt;span class="hljs-selector-class"&amp;gt;.sidebar-module&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-attribute"&amp;gt;padding&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;1rem&amp;lt;/span&amp;gt;;
          }
          &amp;lt;span class="hljs-selector-class"&amp;gt;.sidebar-module-inset&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-attribute"&amp;gt;padding&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;1rem&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;background-color&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;#f5f5f5&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;border-radius&amp;lt;/span&amp;gt;: .&amp;lt;span class="hljs-number"&amp;gt;25rem&amp;lt;/span&amp;gt;;
          }
          &amp;lt;span class="hljs-selector-class"&amp;gt;.sidebar-module-inset&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-selector-tag"&amp;gt;p&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-selector-pseudo"&amp;gt;:last-child&amp;lt;/span&amp;gt;,
          &amp;lt;span class="hljs-selector-class"&amp;gt;.sidebar-module-inset&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-selector-tag"&amp;gt;ul&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-selector-pseudo"&amp;gt;:last-child&amp;lt;/span&amp;gt;,
          &amp;lt;span class="hljs-selector-class"&amp;gt;.sidebar-module-inset&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-selector-tag"&amp;gt;ol&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-selector-pseudo"&amp;gt;:last-child&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-attribute"&amp;gt;margin-bottom&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;;
          }
          &amp;lt;span class="hljs-selector-class"&amp;gt;.blog-post&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-attribute"&amp;gt;margin-bottom&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;4rem&amp;lt;/span&amp;gt;;
          }
          &amp;lt;span class="hljs-selector-class"&amp;gt;.blog-post-title&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-attribute"&amp;gt;margin-bottom&amp;lt;/span&amp;gt;: .&amp;lt;span class="hljs-number"&amp;gt;25rem&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;font-size&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;2.5rem&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;text-align&amp;lt;/span&amp;gt;: center;
          }
          &amp;lt;span class="hljs-selector-class"&amp;gt;.blog-post-meta&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-attribute"&amp;gt;margin-bottom&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;1.25rem&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;color&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;#999&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;text-align&amp;lt;/span&amp;gt;: center;
          }
          &amp;lt;span class="hljs-selector-class"&amp;gt;.blog-footer&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-attribute"&amp;gt;padding&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;2.5rem&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;color&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;#999&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;text-align&amp;lt;/span&amp;gt;: center;
            &amp;lt;span class="hljs-attribute"&amp;gt;background-color&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;#f9f9f9&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;border-top&amp;lt;/span&amp;gt;: .&amp;lt;span class="hljs-number"&amp;gt;05rem&amp;lt;/span&amp;gt; solid &amp;lt;span class="hljs-number"&amp;gt;#e5e5e5&amp;lt;/span&amp;gt;;
          }
          &amp;lt;span class="hljs-selector-class"&amp;gt;.blog-footer&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-selector-tag"&amp;gt;p&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-selector-pseudo"&amp;gt;:last-child&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-attribute"&amp;gt;margin-bottom&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;;
          }
          &amp;lt;span class="hljs-selector-tag"&amp;gt;input&amp;lt;/span&amp;gt;{
              &amp;lt;span class="hljs-attribute"&amp;gt;width&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;45%&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-meta"&amp;gt;!important&amp;lt;/span&amp;gt;;
              &amp;lt;span class="hljs-attribute"&amp;gt;display&amp;lt;/span&amp;gt;: inline-block &amp;lt;span class="hljs-meta"&amp;gt;!important&amp;lt;/span&amp;gt;;
          }
          &amp;lt;span class="hljs-selector-tag"&amp;gt;textarea&amp;lt;/span&amp;gt; {
              &amp;lt;span class="hljs-attribute"&amp;gt;width&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;90%&amp;lt;/span&amp;gt;;
              &amp;lt;span class="hljs-attribute"&amp;gt;height&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;150px&amp;lt;/span&amp;gt;;
              &amp;lt;span class="hljs-attribute"&amp;gt;padding&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;12px&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-number"&amp;gt;20px&amp;lt;/span&amp;gt;;
              &amp;lt;span class="hljs-attribute"&amp;gt;box-sizing&amp;lt;/span&amp;gt;: border-box;
              &amp;lt;span class="hljs-attribute"&amp;gt;border&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;2px&amp;lt;/span&amp;gt; solid &amp;lt;span class="hljs-number"&amp;gt;#ccc&amp;lt;/span&amp;gt;;
              &amp;lt;span class="hljs-attribute"&amp;gt;border-radius&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;4px&amp;lt;/span&amp;gt;;
              &amp;lt;span class="hljs-attribute"&amp;gt;background-color&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;#f8f8f8&amp;lt;/span&amp;gt;;
              &amp;lt;span class="hljs-attribute"&amp;gt;resize&amp;lt;/span&amp;gt;: none;
          }
          &amp;lt;span class="hljs-selector-tag"&amp;gt;textarea&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-selector-pseudo"&amp;gt;:focus&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-selector-tag"&amp;gt;input&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-selector-pseudo"&amp;gt;:focus&amp;lt;/span&amp;gt;{
              &amp;lt;span class="hljs-attribute"&amp;gt;outline&amp;lt;/span&amp;gt;: none &amp;lt;span class="hljs-meta"&amp;gt;!important&amp;lt;/span&amp;gt;;
          }
          &amp;lt;span class="hljs-selector-id"&amp;gt;#comment-section&amp;lt;/span&amp;gt;{
            &amp;lt;span class="hljs-attribute"&amp;gt;background&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-built_in"&amp;gt;rgb&amp;lt;/span&amp;gt;(178, 191, 214); 
            &amp;lt;span class="hljs-attribute"&amp;gt;padding&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;0.5em&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-number"&amp;gt;2em&amp;lt;/span&amp;gt;; &amp;lt;span class="hljs-attribute"&amp;gt;width&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;90%&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;margin&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;10px&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;;
            &amp;lt;span class="hljs-attribute"&amp;gt;border-radius&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;15px&amp;lt;/span&amp;gt;;
          }
          &amp;lt;span class="hljs-selector-id"&amp;gt;#comment-section&amp;lt;/span&amp;gt; &amp;gt; &amp;lt;span class="hljs-selector-tag"&amp;gt;div&amp;lt;/span&amp;gt; &amp;gt; &amp;lt;span class="hljs-selector-tag"&amp;gt;p&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-attribute"&amp;gt;color&amp;lt;/span&amp;gt;: black;
            &amp;lt;span class="hljs-attribute"&amp;gt;display&amp;lt;/span&amp;gt;:inline;
          }
          &amp;lt;span class="hljs-selector-tag"&amp;gt;img&amp;lt;/span&amp;gt;{
          &amp;lt;span class="hljs-attribute"&amp;gt;border-radius&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;50%&amp;lt;/span&amp;gt;;
          &amp;lt;span class="hljs-attribute"&amp;gt;float&amp;lt;/span&amp;gt;: left;
          }
        &amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;style&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;head&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;body&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"app"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;header&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"blog-masthead"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
              &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"container"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;nav&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"nav"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                  &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;a&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"nav-link active"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;href&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"#"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;Home&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;a&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;nav&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
              &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;header&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;

          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;main&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;role&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"main"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"container"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;

            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"row"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;

              &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"col-sm-12 blog-main"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;

                &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"blog-post"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                  &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;h2&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"blog-post-title"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;Realtime Comments With Pusher&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;h2&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                  &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;p&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"blog-post-meta"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;January 1, 2018 by &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;a&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;href&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"#"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;Jordan&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;a&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;p&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                  &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;p&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;This blog post shows a few different types of content that's supported and styled with Bootstrap. Basic typography, images, and code are all supported.This blog post shows a few different types of content that's supported and styled with Bootstrap. Basic typography, images, and code are all supported
                  &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;p&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                  &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"comment-section"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;form&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"form-signin"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;h5&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"comment"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;Comment&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;h5&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;input&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;type&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"username"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;ref&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"username"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"form-control"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;placeholder&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"John Doe"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;required&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;autofocus&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;input&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;type&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"email"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;ref&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"email"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"form-control"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;placeholder&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"Johndoe@gmail.com"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;required&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;textarea&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;ref&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"comment"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;textarea&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;button&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"btn btn-lg btn-primary"&amp;lt;/span&amp;gt; @&amp;lt;span class="hljs-attr"&amp;gt;click.prevent&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"sendComment"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;type&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"submit"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;Comment&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;button&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;form&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;br&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"comment-section"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;v-for&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"comment in comments"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
              &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;img&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;src&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"http://merritos.com/img/team/maleTeam.jpg"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;width&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"65px"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;height&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"65px"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
               &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;p&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;{{comment.name}} &amp;amp;nbsp;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt; {{&amp;lt;span class="hljs-attr"&amp;gt;comment.email&amp;lt;/span&amp;gt;}} &amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;p&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
               &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;hr&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;p&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;style&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"color:black"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;{{comment.comment}}&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;p&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                  &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
              &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;

            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;

          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;main&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;

          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;footer&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"blog-footer"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;p&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;a&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;href&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"#"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;Back to top&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;a&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;p&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
          &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;footer&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;

        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;body&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;html&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now in the same file, paste the following code before the closing &lt;code&gt;body&lt;/code&gt; tag of the HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;script&amp;gt;
      &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; app = &amp;lt;span class="hljs-keyword"&amp;gt;new&amp;lt;/span&amp;gt; Vue({
        &amp;lt;span class="hljs-attr"&amp;gt;el&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-string"&amp;gt;'#app'&amp;lt;/span&amp;gt;,
        &amp;lt;span class="hljs-attr"&amp;gt;data&amp;lt;/span&amp;gt;: {
          &amp;lt;span class="hljs-attr"&amp;gt;comments&amp;lt;/span&amp;gt; : []
        },
        &amp;lt;span class="hljs-attr"&amp;gt;created&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt;(&amp;lt;span class="hljs-params"&amp;gt;&amp;lt;/span&amp;gt;) &amp;lt;/span&amp;gt;{
          axios.get(&amp;lt;span class="hljs-string"&amp;gt;'/comments'&amp;lt;/span&amp;gt;).then(&amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;response&amp;lt;/span&amp;gt; =&amp;gt;&amp;lt;/span&amp;gt; { 
            &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.comments = response.data.items ? response.data.items : [] 
          })
        },
        &amp;lt;span class="hljs-attr"&amp;gt;methods&amp;lt;/span&amp;gt;: {
            &amp;lt;span class="hljs-attr"&amp;gt;sendComment&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt;(&amp;lt;span class="hljs-params"&amp;gt;index&amp;lt;/span&amp;gt;) &amp;lt;/span&amp;gt;{
              &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; comment = {
                &amp;lt;span class="hljs-attr"&amp;gt;name&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.$refs.username.value,
                &amp;lt;span class="hljs-attr"&amp;gt;email&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.$refs.email.value,
                &amp;lt;span class="hljs-attr"&amp;gt;comment&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.$refs.comment.value
              }

              axios.post(&amp;lt;span class="hljs-string"&amp;gt;'/comment'&amp;lt;/span&amp;gt;, comment).then(&amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;response&amp;lt;/span&amp;gt; =&amp;gt;&amp;lt;/span&amp;gt; { 
                &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.$refs.username.value = &amp;lt;span class="hljs-string"&amp;gt;''&amp;lt;/span&amp;gt;,
                &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.$refs.email.value = &amp;lt;span class="hljs-string"&amp;gt;''&amp;lt;/span&amp;gt;,
                &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.$refs.comment.value = &amp;lt;span class="hljs-string"&amp;gt;''&amp;lt;/span&amp;gt;
              })
            }
        }
      })
    &amp;lt;&amp;lt;span class="hljs-regexp"&amp;gt;/script&amp;gt;&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above we have the Vue.js code for our application and this is a summary of what it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We instantiate a comments array that will hold all the available comments.&lt;/li&gt;
&lt;li&gt;In the &lt;code&gt;created()&lt;/code&gt; method, we use &lt;a href="https://www.npmjs.com/package/axios" rel="noopener noreferrer"&gt;Axios&lt;/a&gt; to pull in all the comments available from the API and store it in the &lt;code&gt;comments&lt;/code&gt; array.&lt;/li&gt;
&lt;li&gt;In the &lt;code&gt;sendComment&lt;/code&gt; method, we send a request to the API to create a new &lt;code&gt;comment&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can build our application at this stage and visit &lt;a href="http://localhost:9000" rel="noopener noreferrer"&gt;http://localhost:9000&lt;/a&gt;, we should see this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ go run comments.go

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our application should display like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/2m7H3gsZCMIkEKGueSEuK/d014e440249b413da2ed8f509d91eae1/go-comments-homepage.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/2m7H3gsZCMIkEKGueSEuK/d014e440249b413da2ed8f509d91eae1/go-comments-homepage.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Making comments display in realtime
&lt;/h3&gt;

&lt;p&gt;The next thing we need to do is make sure the comments are displayed in realtime. To do this, we need to trigger an event every time a new comment is added. We will do this in the backend using the &lt;a href="https://github.com/pusher/pusher-http-go" rel="noopener noreferrer"&gt;Pusher Go library&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To pull in the Pusher Go library run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ go get github.com/pusher/pusher-http-go

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next let’s import the library. In our &lt;code&gt;models.go&lt;/code&gt; file do the following in the imports statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;package&amp;lt;/span&amp;gt; models

    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; (
        &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;

        pusher &amp;lt;span class="hljs-string"&amp;gt;"github.com/pusher/pusher-http-go"&amp;lt;/span&amp;gt;
    )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the same file, before the &lt;code&gt;type&lt;/code&gt; definition, paste in the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;

    &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; client = pusher.Client{
        AppId:   &amp;lt;span class="hljs-string"&amp;gt;"PUSHER_APP_ID"&amp;lt;/span&amp;gt;,
        Key:     &amp;lt;span class="hljs-string"&amp;gt;"PUSHER_APP_KEY"&amp;lt;/span&amp;gt;,
        Secret:  &amp;lt;span class="hljs-string"&amp;gt;"PUSHER_APP_SECRET"&amp;lt;/span&amp;gt;,
        Cluster: &amp;lt;span class="hljs-string"&amp;gt;"PUSHER_APP_CLUSTER"&amp;lt;/span&amp;gt;,
        Secure:  &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;,
    }

    &amp;lt;span class="hljs-comment"&amp;gt;// [...]&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we have initialized the Pusher client using the credentials from our earlier created app.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Replace &lt;code&gt;PUSHER_APP_*&lt;/code&gt; keys with your Pusher app credentials.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next, let’s trigger an event every time a comment is saved to the database. Replace the &lt;code&gt;PushComment&lt;/code&gt; function with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;PushComment&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(db *sql.DB, name &amp;lt;span class="hljs-keyword"&amp;gt;string&amp;lt;/span&amp;gt;, email &amp;lt;span class="hljs-keyword"&amp;gt;string&amp;lt;/span&amp;gt;, comment &amp;lt;span class="hljs-keyword"&amp;gt;string&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-params"&amp;gt;(&amp;lt;span class="hljs-keyword"&amp;gt;int64&amp;lt;/span&amp;gt;, error)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
        sql := &amp;lt;span class="hljs-string"&amp;gt;"INSERT INTO comments(name, email, comment) VALUES(?, ?, ?)"&amp;lt;/span&amp;gt;
        stmt, err := db.Prepare(sql)
        &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; err != &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-built_in"&amp;gt;panic&amp;lt;/span&amp;gt;(err)
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;defer&amp;lt;/span&amp;gt; stmt.Close()

        result, err2 := stmt.Exec(name, email, comment)
        &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; err2 != &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-built_in"&amp;gt;panic&amp;lt;/span&amp;gt;(err2)
        }

        newComment := Comment{
            Name:    name,
            Email:   email,
            Comment: comment,
        }

        client.Trigger(&amp;lt;span class="hljs-string"&amp;gt;"comment-channel"&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;"new-comment"&amp;lt;/span&amp;gt;, newComment)
        &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; result.LastInsertId()
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this newer version of the function we create a &lt;code&gt;newComment&lt;/code&gt; object that holds information for the last comment that was saved to the database. Whenever a new comment is created, we will send it to the Pusher channel &lt;code&gt;comment-channel&lt;/code&gt; to be triggered on the event &lt;code&gt;new-comment&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Displaying data in realtime on the client&lt;/strong&gt;To receive comments we have to register the &lt;a href="https://github.com/pusher/pusher-js" rel="noopener noreferrer"&gt;Pusher JavaScript Client&lt;/a&gt; in our frontend code. Add this line of code inside the head tag of our HTML in the index.html file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;script src="https://js.pusher.com/4.1/pusher.min.js"&amp;gt;&amp;lt;/script&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we will register a Pusher instance in the &lt;code&gt;created()&lt;/code&gt; life cycle hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    created: function() {

        &amp;lt;span class="hljs-keyword"&amp;gt;const&amp;lt;/span&amp;gt; pusher = &amp;lt;span class="hljs-built_in"&amp;gt;new&amp;lt;/span&amp;gt; Pusher(&amp;lt;span class="hljs-string"&amp;gt;'PUSHER_APP_KEY'&amp;lt;/span&amp;gt;, {
            cluster: &amp;lt;span class="hljs-string"&amp;gt;'PUSHER_APP_CLUSTER'&amp;lt;/span&amp;gt;,
            encrypted: &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;
        });

        &amp;lt;span class="hljs-keyword"&amp;gt;const&amp;lt;/span&amp;gt; channel = pusher.subscribe(&amp;lt;span class="hljs-string"&amp;gt;'comment-channel'&amp;lt;/span&amp;gt;);

        channel.bind(&amp;lt;span class="hljs-string"&amp;gt;'new-comment'&amp;lt;/span&amp;gt;, data =&amp;gt; {
          this.comments.push(data)
        });

        &amp;lt;span class="hljs-comment"&amp;gt;// [...]    &amp;lt;/span&amp;gt;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ Replace the &lt;code&gt;PUSHER_APP_*&lt;/code&gt; keys with the credentials for your Pusher application.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the code above, we are creating a Pusher instance and then subscribing to a channel. In that channel we are listening for the &lt;code&gt;new-comment&lt;/code&gt; event.&lt;/p&gt;

&lt;p&gt;Now we can run our application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ go run comments.go

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can point a web browser to this address &lt;a href="http://localhost:9000" rel="noopener noreferrer"&gt;http://localhost:9000&lt;/a&gt; and we should see the application in action:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/3dhebYl8aAm4cCME4Os2ys/4d39432a0bd39156cf8556c15649a2f1/go-comments-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/3dhebYl8aAm4cCME4Os2ys/4d39432a0bd39156cf8556c15649a2f1/go-comments-demo.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we looked at how to build a realtime comment system using Go, Vue.js, and Pusher Channels. The source code to the application is available on &lt;a href="https://github.com/neoighodaro/realtime-comments-go-pusher" rel="noopener noreferrer"&gt;GitHub.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post first appeared on the &lt;a href="https://pusher.com/tutorials/live-comments-go-vuejs" rel="noopener noreferrer"&gt;Pusher blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>pusher</category>
      <category>vue</category>
      <category>go</category>
    </item>
    <item>
      <title>Build a photo feed with Go and Vue.js</title>
      <dc:creator>Neo</dc:creator>
      <pubDate>Mon, 19 Nov 2018 17:04:25 +0000</pubDate>
      <link>https://dev.to/neo/build-a-photo-feed-with-go-and-vuejs-33b3</link>
      <guid>https://dev.to/neo/build-a-photo-feed-with-go-and-vuejs-33b3</guid>
      <description>&lt;p&gt;You will need Go and SQLite installed on your machine, as well as basic knowledge of Go and JavaScript.&lt;/p&gt;

&lt;p&gt;Many social media based applications allow users to upload photos and these photos are usually displayed in a timeline for their followers and others to see. In the past, you would have had to refresh your feed manually to see new photos uploaded to the timeline. However, with modern web technologies, you can see the updates in realtime without having to refresh the page manually.&lt;/p&gt;

&lt;p&gt;In this article, we will consider how you can build a realtime photo feed using Pusher Channels, GO and a little Vue.js. &lt;a href="https://pusher.com" rel="noopener noreferrer"&gt;Pusher Channels&lt;/a&gt; helps you “easily build scalable in-app notifications, chat, realtime graphs, geotracking and more in your web &amp;amp; mobile apps with our hosted pub/sub messaging API.”&lt;/p&gt;

&lt;p&gt;This is a preview of what we will be building:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/5vVy2T7aY8qoOiCuA8oGQK/abdfe10038a2935fa4a894296db6630d/go-photo-feed-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/5vVy2T7aY8qoOiCuA8oGQK/abdfe10038a2935fa4a894296db6630d/go-photo-feed-demo.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we start building our application, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic knowledge of the &lt;a href="https://golang.org/" rel="noopener noreferrer"&gt;Go&lt;/a&gt; programming language.&lt;/li&gt;
&lt;li&gt;Basic JavaScript (Vue.js) knowledge.&lt;/li&gt;
&lt;li&gt;Go (version &amp;gt;= 0.10.x) installed on your machine. Check out the &lt;a href="https://golang.org/doc/install" rel="noopener noreferrer"&gt;installation guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;SQLite (version &amp;gt;= 3.x) installed on your machine. Check out an &lt;a href="http://www.sqlitetutorial.net/download-install-sqlite/" rel="noopener noreferrer"&gt;installation guide&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting a Pusher Channels application
&lt;/h2&gt;

&lt;p&gt;The first step will be to get a Pusher Channels application. We will need the application credentials for our realtime features to work.&lt;/p&gt;

&lt;p&gt;Go to the Pusher website and create an account. After creating an account, you should create a new application. Follow the application creation wizard and then you should be given your application credentials, we will use this later in the article.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/5mwD9r7FFmWugK6AyY08Ga/fdff5e30849724654dd10c6a5f3de29a/go-photo-feed-app-keys.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/5mwD9r7FFmWugK6AyY08Ga/fdff5e30849724654dd10c6a5f3de29a/go-photo-feed-app-keys.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have our application, let’s move on to the next step&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating our Go application
&lt;/h2&gt;

&lt;p&gt;The next thing we want to do is create the Go application. In your terminal, &lt;code&gt;cd&lt;/code&gt; to your &lt;code&gt;$GOPATH&lt;/code&gt; and create a new directory there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ cd $GOPATH/src
    $ mkdir gofoto
    $ cd gofoto

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 It is recommended that you place the source code for your project in the &lt;code&gt;src&lt;/code&gt; subdirectory (e.g., &lt;code&gt;$GOPATH/src/your_project&lt;/code&gt; or &lt;code&gt;$GOPATH/src/github.com/your_github_username/your_project&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next, we will create some directories to organize our application a little:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ mkdir database
    $ mkdir public
    $ mkdir public/uploads

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a &lt;code&gt;database&lt;/code&gt; and &lt;code&gt;public&lt;/code&gt; directory, and also an &lt;code&gt;uploads&lt;/code&gt; directory inside the public directory. We will store our database file inside the &lt;code&gt;database&lt;/code&gt; directory, we will keep our public files: HTML and images, inside the &lt;code&gt;public&lt;/code&gt; and &lt;code&gt;uploads&lt;/code&gt; directory. Create a new &lt;code&gt;index.html&lt;/code&gt; file in the &lt;code&gt;public&lt;/code&gt; directory that was created.&lt;/p&gt;

&lt;p&gt;Now let’s create our first (and only) Go file for this article. We will try to keep everything simple by placing all our source code in a single file. Create a &lt;code&gt;main.go&lt;/code&gt; file in the project root.&lt;/p&gt;

&lt;p&gt;In the file paste the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;package&amp;lt;/span&amp;gt; main

    &amp;lt;span class="hljs-keyword"&amp;gt;import&amp;lt;/span&amp;gt; (
        &amp;lt;span class="hljs-string"&amp;gt;"database/sql"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-string"&amp;gt;"io"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-string"&amp;gt;"net/http"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-string"&amp;gt;"os"&amp;lt;/span&amp;gt;

        &amp;lt;span class="hljs-string"&amp;gt;"github.com/labstack/echo"&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-string"&amp;gt;"github.com/labstack/echo/middleware"&amp;lt;/span&amp;gt;
        _ &amp;lt;span class="hljs-string"&amp;gt;"github.com/mattn/go-sqlite3"&amp;lt;/span&amp;gt;
        pusher &amp;lt;span class="hljs-string"&amp;gt;"github.com/pusher/pusher-http-go"&amp;lt;/span&amp;gt;
    )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above we have imported some packages we will be needing to work on our photo feed. We need the &lt;code&gt;database/sql&lt;/code&gt; to run SQL queries, the &lt;code&gt;io&lt;/code&gt; and &lt;code&gt;os&lt;/code&gt; package for our file uploading process, and the &lt;code&gt;net/http&lt;/code&gt; for our HTTP status codes.&lt;/p&gt;

&lt;p&gt;We have some other external packages we imported. The &lt;code&gt;labstack/echo&lt;/code&gt; package is the &lt;a href="https://github.com/labstack/echo" rel="noopener noreferrer"&gt;Echo framework&lt;/a&gt; that we will be using. We also have the &lt;code&gt;mattn/go-sqlite3&lt;/code&gt; package which is for SQLite. Finally, we imported the &lt;code&gt;pusher/pusher-http-go&lt;/code&gt; package which we will use to trigger events to Pusher Channels.&lt;/p&gt;

&lt;h3&gt;
  
  
  Importing external Go packages
&lt;/h3&gt;

&lt;p&gt;Before we continue, let’s pull in these packages using our terminal. Run the following commands below to pull the packages in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ go get github.com/labstack/echo
    $ go get github.com/labstack/echo/middleware
    $ go get github.com/mattn/go-sqlite3
    $ go get github.com/pusher/pusher-http-go

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note that the commands above will not return any confirmation output when it finishes installing the packages. If you want to confirm the packages were indeed installed you can just check the &lt;code&gt;$GOPATH/src/github.com&lt;/code&gt; directory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that we have pulled in our packages, let’s create the &lt;code&gt;main&lt;/code&gt; function. This is the function that will be the entry point of our application. In this function, we will set up our applications database, middleware, and routes.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;main,go&lt;/code&gt; file and paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;main&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
        db := initialiseDatabase(&amp;lt;span class="hljs-string"&amp;gt;"database/database.sqlite"&amp;lt;/span&amp;gt;)
        migrateDatabase(db)

        e := echo.New()

        e.Use(middleware.Logger())
        e.Use(middleware.Recover())

        e.File(&amp;lt;span class="hljs-string"&amp;gt;"/"&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;"public/index.html"&amp;lt;/span&amp;gt;)
        e.GET(&amp;lt;span class="hljs-string"&amp;gt;"/photos"&amp;lt;/span&amp;gt;, getPhotos(db))
        e.POST(&amp;lt;span class="hljs-string"&amp;gt;"/photos"&amp;lt;/span&amp;gt;, uploadPhoto(db))
        e.Static(&amp;lt;span class="hljs-string"&amp;gt;"/uploads"&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;"public/uploads"&amp;lt;/span&amp;gt;)

        e.Logger.Fatal(e.Start(&amp;lt;span class="hljs-string"&amp;gt;":9000"&amp;lt;/span&amp;gt;))
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code above, we instantiated our database using the file path to the database file. This will create the SQLite file if it did not already exist. We then run the &lt;code&gt;migrateDatabase&lt;/code&gt; function which migrates the database.&lt;/p&gt;

&lt;p&gt;Next, we instantiate Echo and then register some middlewares. The &lt;a href="https://echo.labstack.com/middleware/logger" rel="noopener noreferrer"&gt;logger middleware&lt;/a&gt; is helpful for logging information about the HTTP request while the &lt;a href="https://echo.labstack.com/middleware/recover" rel="noopener noreferrer"&gt;recover middleware&lt;/a&gt; “recovers from panics anywhere in the chain, prints stack trace and handles the control to the centralized &lt;a href="https://echo.labstack.com/guide/customization#http-error-handler" rel="noopener noreferrer"&gt;HTTPErrorHandler&lt;/a&gt;.”&lt;/p&gt;

&lt;p&gt;We then set up some routes to handle our requests. The first handler is the &lt;code&gt;File&lt;/code&gt; handler. We use this to serve the &lt;code&gt;index.html&lt;/code&gt; file. This will be the entry point to the application from the frontend. We also have the &lt;code&gt;/photos&lt;/code&gt; route which accepts a &lt;code&gt;POST&lt;/code&gt; and &lt;code&gt;GET&lt;/code&gt; request. We need these routes to act like API endpoints that are used for uploading and displaying the photos. The final handler is &lt;code&gt;Static&lt;/code&gt;. We use this to return static files that are stored in the &lt;code&gt;/uploads&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;We finally use &lt;code&gt;e.Start&lt;/code&gt; to start our Go web server running on port 9000. The port is not set in stone and you can choose any available and unused port you feel like.&lt;/p&gt;

&lt;p&gt;At this point, we have not created most of the functions we referenced in the &lt;code&gt;main&lt;/code&gt; function so let’s do so now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating our database management functions
&lt;/h3&gt;

&lt;p&gt;In the &lt;code&gt;main&lt;/code&gt; function we referenced an &lt;code&gt;initialiseDatabase&lt;/code&gt; and &lt;code&gt;migrateDatabase&lt;/code&gt; function. Let’s create them now. In the &lt;code&gt;main.go&lt;/code&gt; file, paste the following functions above the &lt;code&gt;main&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;initialiseDatabase&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(filepath &amp;lt;span class="hljs-keyword"&amp;gt;string&amp;lt;/span&amp;gt;)&amp;lt;/span&amp;gt; *&amp;lt;span class="hljs-title"&amp;gt;sql&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-title"&amp;gt;DB&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
        db, err := sql.Open(&amp;lt;span class="hljs-string"&amp;gt;"sqlite3"&amp;lt;/span&amp;gt;, filepath)
        &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; err != &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; || db == &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-built_in"&amp;gt;panic&amp;lt;/span&amp;gt;(&amp;lt;span class="hljs-string"&amp;gt;"Error connecting to database"&amp;lt;/span&amp;gt;)
        }

        &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; db
    }

    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;migrateDatabase&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(db *sql.DB)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
        sql := &amp;lt;span class="hljs-string"&amp;gt;`
            CREATE TABLE IF NOT EXISTS photos(
                    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
                    src VARCHAR NOT NULL
            );
       `&amp;lt;/span&amp;gt;

        _, err := db.Exec(sql)
        &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; err != &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; {
            &amp;lt;span class="hljs-built_in"&amp;gt;panic&amp;lt;/span&amp;gt;(err)
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;initialiseDatabase&lt;/code&gt; function, we create an instance of the SQLite database using the database file and return that instance. In the &lt;code&gt;migrateDatabase&lt;/code&gt; function, we use the instance of the database returned in the previous function to execute the migration SQL.&lt;/p&gt;

&lt;p&gt;Let’s create the data structure for our photo and photo collection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating our data structures
&lt;/h3&gt;

&lt;p&gt;The next thing we will do is create the data structure for our object types. We will create a &lt;code&gt;Photo&lt;/code&gt; structure and a &lt;code&gt;PhotoCollection&lt;/code&gt; structure. The &lt;code&gt;Photo&lt;/code&gt; struct will define how a typical photo will be represented while the &lt;code&gt;PhotoCollection&lt;/code&gt; will define how a collection of photos will be represented.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;main.go&lt;/code&gt; file and paste the following code above the &lt;code&gt;initialiseDatabase&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;type&amp;lt;/span&amp;gt; Photo &amp;lt;span class="hljs-keyword"&amp;gt;struct&amp;lt;/span&amp;gt; {
        ID  &amp;lt;span class="hljs-keyword"&amp;gt;int64&amp;lt;/span&amp;gt;  &amp;lt;span class="hljs-string"&amp;gt;`json:"id"`&amp;lt;/span&amp;gt;
        Src &amp;lt;span class="hljs-keyword"&amp;gt;string&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-string"&amp;gt;`json:"src"`&amp;lt;/span&amp;gt;
    }

    &amp;lt;span class="hljs-keyword"&amp;gt;type&amp;lt;/span&amp;gt; PhotoCollection &amp;lt;span class="hljs-keyword"&amp;gt;struct&amp;lt;/span&amp;gt; {
        Photos []Photo &amp;lt;span class="hljs-string"&amp;gt;`json:"items"`&amp;lt;/span&amp;gt;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating our route handler functions
&lt;/h3&gt;

&lt;p&gt;Next let’s create the functions for our routes. Open the &lt;code&gt;main.go&lt;/code&gt; file and paste the following file inside it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;getPhotos&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(db *sql.DB)&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;echo&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-title"&amp;gt;HandlerFunc&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
        &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(c echo.Context)&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;error&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            rows, err := db.Query(&amp;lt;span class="hljs-string"&amp;gt;"SELECT * FROM photos"&amp;lt;/span&amp;gt;)
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; err != &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-built_in"&amp;gt;panic&amp;lt;/span&amp;gt;(err)
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;defer&amp;lt;/span&amp;gt; rows.Close()

            result := PhotoCollection{}

            &amp;lt;span class="hljs-keyword"&amp;gt;for&amp;lt;/span&amp;gt; rows.Next() {
                photo := Photo{}

                err2 := rows.Scan(&amp;amp;photo.ID, &amp;amp;photo.Src)
                &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; err2 != &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; {
                    &amp;lt;span class="hljs-built_in"&amp;gt;panic&amp;lt;/span&amp;gt;(err2)
                }

                result.Photos = &amp;lt;span class="hljs-built_in"&amp;gt;append&amp;lt;/span&amp;gt;(result.Photos, photo)
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; c.JSON(http.StatusOK, result)
        }
    }

    &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;uploadPhoto&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(db *sql.DB)&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;echo&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-title"&amp;gt;HandlerFunc&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
        &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;func&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;(c echo.Context)&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-title"&amp;gt;error&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; {
            file, err := c.FormFile(&amp;lt;span class="hljs-string"&amp;gt;"file"&amp;lt;/span&amp;gt;)
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; err != &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; err
            }

            src, err := file.Open()
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; err != &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; err
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;defer&amp;lt;/span&amp;gt; src.Close()

            filePath := &amp;lt;span class="hljs-string"&amp;gt;"./public/uploads/"&amp;lt;/span&amp;gt; + file.Filename
            fileSrc := &amp;lt;span class="hljs-string"&amp;gt;"http://127.0.0.1:9000/uploads/"&amp;lt;/span&amp;gt; + file.Filename

            dst, err := os.Create(filePath)
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; err != &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-built_in"&amp;gt;panic&amp;lt;/span&amp;gt;(err)
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;defer&amp;lt;/span&amp;gt; dst.Close()

            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; _, err = io.Copy(dst, src); err != &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-built_in"&amp;gt;panic&amp;lt;/span&amp;gt;(err)
            }

            stmt, err := db.Prepare(&amp;lt;span class="hljs-string"&amp;gt;"INSERT INTO photos (src) VALUES(?)"&amp;lt;/span&amp;gt;)
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; err != &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-built_in"&amp;gt;panic&amp;lt;/span&amp;gt;(err)
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;defer&amp;lt;/span&amp;gt; stmt.Close()

            result, err := stmt.Exec(fileSrc)
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; err != &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-built_in"&amp;gt;panic&amp;lt;/span&amp;gt;(err)
            }

            insertedId, err := result.LastInsertId()
            &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; err != &amp;lt;span class="hljs-literal"&amp;gt;nil&amp;lt;/span&amp;gt; {
                &amp;lt;span class="hljs-built_in"&amp;gt;panic&amp;lt;/span&amp;gt;(err)
            }

            photo := Photo{
                Src: fileSrc,
                ID:  insertedId,
            }

            &amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; c.JSON(http.StatusOK, photo)
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;getPhotos&lt;/code&gt; method, we are simply running the query to fetch all the photos from the database and returning them as a JSON response to the client. In the &lt;code&gt;uploadPhoto&lt;/code&gt; method we first get the file to be uploaded then upload them to the server and then we run the query to insert a new record in the &lt;code&gt;photos&lt;/code&gt; table with the newly uploaded photo. We also return a JSON response from that function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding realtime support to our Go application
&lt;/h3&gt;

&lt;p&gt;The next thing we want to do is trigger an event when a new photo is uploaded to the server. For this, we will be using the &lt;a href="https://github.com/pusher/pusher-http-go" rel="noopener noreferrer"&gt;Pusher Go HTTP library&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;main.go&lt;/code&gt; file paste the following above the type definitions for the &lt;code&gt;Photo&lt;/code&gt; and &lt;code&gt;PhotoCollection&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-keyword"&amp;gt;var&amp;lt;/span&amp;gt; client = pusher.Client{
        AppId:   &amp;lt;span class="hljs-string"&amp;gt;"PUSHER_APP_ID"&amp;lt;/span&amp;gt;,
        Key:     &amp;lt;span class="hljs-string"&amp;gt;"PUSHER_APP_KEY"&amp;lt;/span&amp;gt;,
        Secret:  &amp;lt;span class="hljs-string"&amp;gt;"PUSHER_APP_SECRET"&amp;lt;/span&amp;gt;,
        Cluster: &amp;lt;span class="hljs-string"&amp;gt;"PUSHER_APP_CLUSTER"&amp;lt;/span&amp;gt;,
        Secure:  &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;,
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a new Pusher client instance. We can then use this instance to trigger notifications to different channels we want. Remember to replace the &lt;code&gt;PUSHER_APP_*&lt;/code&gt; keys with the keys provided when you created your Pusher application earlier.&lt;/p&gt;

&lt;p&gt;Next, go to the &lt;code&gt;uploadPhoto&lt;/code&gt; function in the &lt;code&gt;main.go&lt;/code&gt; file and right before the &lt;code&gt;return&lt;/code&gt; statement at the bottom of the function, paste the following code:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;client.Trigger(&amp;lt;span class="hljs-string"&amp;gt;"photo-stream"&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-string"&amp;gt;"new-photo"&amp;lt;/span&amp;gt;, photo)&lt;/code&gt;This is the code that triggers a new event when a new photo is uploaded to our application.&lt;/p&gt;

&lt;p&gt;That will be all for our Go application. At this point, you can build your application and compile it into a binary using the &lt;code&gt;go build&lt;/code&gt; command. However, for this tutorial we will just run the binary temporarily:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ go run main.go

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/3DPeaIV05GMOGMSUouAaO/ce811f6f30da4b86c8c659f8637fdc3e/go-photo-feed-go-run.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/3DPeaIV05GMOGMSUouAaO/ce811f6f30da4b86c8c659f8637fdc3e/go-photo-feed-go-run.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Building our frontend
&lt;/h2&gt;

&lt;p&gt;The next thing we want to do is build out our frontend. We will be using the &lt;a href="https://vuejs.org/" rel="noopener noreferrer"&gt;Vue.js framework&lt;/a&gt; and the &lt;a href="https://github.com/axios/axios" rel="noopener noreferrer"&gt;Axios library&lt;/a&gt; to send requests.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;index.html&lt;/code&gt; file and in there paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;span class="hljs-meta"&amp;gt;&amp;lt;!doctype html&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;html&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;lang&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"en"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;head&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;meta&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;charset&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"utf-8"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;meta&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;name&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"viewport"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;content&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"width=device-width, initial-scale=1, shrink-to-fit=no"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;link&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;rel&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"stylesheet"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;href&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;title&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;Photo Feed&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;title&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;style&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;type&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"text/css"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="css"&amp;gt;
            &amp;lt;span class="hljs-selector-id"&amp;gt;#photoFile&amp;lt;/span&amp;gt; { &amp;lt;span class="hljs-attribute"&amp;gt;display&amp;lt;/span&amp;gt;: none; }
            &amp;lt;span class="hljs-selector-id"&amp;gt;#app&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-selector-tag"&amp;gt;img&amp;lt;/span&amp;gt; { &amp;lt;span class="hljs-attribute"&amp;gt;max-width&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;100%&amp;lt;/span&amp;gt;; }
            &amp;lt;span class="hljs-selector-class"&amp;gt;.image-row&amp;lt;/span&amp;gt; { &amp;lt;span class="hljs-attribute"&amp;gt;margin&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;20px&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;; }
            &amp;lt;span class="hljs-selector-class"&amp;gt;.image-row&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-selector-class"&amp;gt;.thumbnail&amp;lt;/span&amp;gt; { &amp;lt;span class="hljs-attribute"&amp;gt;padding&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;2px&amp;lt;/span&amp;gt;; &amp;lt;span class="hljs-attribute"&amp;gt;border&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-number"&amp;gt;1px&amp;lt;/span&amp;gt; solid &amp;lt;span class="hljs-number"&amp;gt;#d9d9d9&amp;lt;/span&amp;gt;; }
        &amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;style&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;head&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;body&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"app"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;

            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;nav&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"navbar navbar-expand-lg navbar-light bg-light"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;a&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"navbar-brand"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;href&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"#"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;GoFoto&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;a&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;ul&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"navbar-nav mr-auto"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;li&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"nav-item active"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;a&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"nav-link"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;v-on:click&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"filePicker"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;href&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"#"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;Upload&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;a&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;input&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;type&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"file"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"photoFile"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;ref&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"myFiles"&amp;lt;/span&amp;gt; @&amp;lt;span class="hljs-attr"&amp;gt;change&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"upload"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;name&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"file"&amp;lt;/span&amp;gt; /&amp;gt;&amp;lt;/span&amp;gt;
                        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;li&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;ul&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;nav&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;

            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"container"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"row justify-content-md-center"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;id&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"loading"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;v-if&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"loading"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"col-xs-12"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                        Loading photos...
                    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"row justify-content-md-center image-row"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;v-for&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"photo in photos"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"col col-lg-4 col-md-6 col-xs-12"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;img&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;class&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"thumbnail"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;:src&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"photo.src"&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;alt&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;""&amp;lt;/span&amp;gt; /&amp;gt;&amp;lt;/span&amp;gt;
                    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
                &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;

        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;div&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;script&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;src&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"//js.pusher.com/4.0/pusher.min.js"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="undefined"&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;script&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;script&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;src&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"https://unpkg.com/axios/dist/axios.min.js"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="undefined"&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;script&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;&amp;lt;span class="hljs-name"&amp;gt;script&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-attr"&amp;gt;src&amp;lt;/span&amp;gt;=&amp;lt;span class="hljs-string"&amp;gt;"https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="undefined"&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;script&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;body&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;html&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the HTML file above we have defined the design for our photostream. We are using Bootstrap 4 and we included the CSS in the HTML above. We are also using the Axios library, Pusher library, and Vue framework. We included the links to the scripts at the bottom of the HTML document.&lt;/p&gt;

&lt;p&gt;Next let’s add the Vue.js code. In the HTML file, add the following code right before the closing &lt;code&gt;body&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;script type=&amp;lt;span class="hljs-string"&amp;gt;"text/javascript"&amp;lt;/span&amp;gt;&amp;gt;
        &amp;lt;span class="hljs-keyword"&amp;gt;new&amp;lt;/span&amp;gt; Vue({
            &amp;lt;span class="hljs-attr"&amp;gt;el&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-string"&amp;gt;'#app'&amp;lt;/span&amp;gt;,
            &amp;lt;span class="hljs-attr"&amp;gt;data&amp;lt;/span&amp;gt;: {
                &amp;lt;span class="hljs-attr"&amp;gt;photos&amp;lt;/span&amp;gt;: [],
                &amp;lt;span class="hljs-attr"&amp;gt;loading&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;,
            },
            mounted() {
                &amp;lt;span class="hljs-keyword"&amp;gt;const&amp;lt;/span&amp;gt; pusher = &amp;lt;span class="hljs-keyword"&amp;gt;new&amp;lt;/span&amp;gt; Pusher(&amp;lt;span class="hljs-string"&amp;gt;'PUSHER_APP_KEY'&amp;lt;/span&amp;gt;, {
                    &amp;lt;span class="hljs-attr"&amp;gt;cluster&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-string"&amp;gt;'PUSHER_APP_CLUSTER'&amp;lt;/span&amp;gt;,
                    &amp;lt;span class="hljs-attr"&amp;gt;encrypted&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;
                });

                &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; channel = pusher.subscribe(&amp;lt;span class="hljs-string"&amp;gt;'photo-stream'&amp;lt;/span&amp;gt;)

                channel.bind(&amp;lt;span class="hljs-string"&amp;gt;'new-photo'&amp;lt;/span&amp;gt;, data =&amp;gt; &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.photos.unshift(data));

                axios.get(&amp;lt;span class="hljs-string"&amp;gt;'/photos'&amp;lt;/span&amp;gt;).then(&amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;res&amp;lt;/span&amp;gt; =&amp;gt;&amp;lt;/span&amp;gt; {
                    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.loading = &amp;lt;span class="hljs-literal"&amp;gt;false&amp;lt;/span&amp;gt;
                    &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.photos = res.data.items ? res.data.items : []
                })
            },
            &amp;lt;span class="hljs-attr"&amp;gt;methods&amp;lt;/span&amp;gt;: {
                &amp;lt;span class="hljs-attr"&amp;gt;filePicker&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-params"&amp;gt;&amp;lt;/span&amp;gt;) &amp;lt;/span&amp;gt;{
                    &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; elem = &amp;lt;span class="hljs-built_in"&amp;gt;document&amp;lt;/span&amp;gt;.getElementById(&amp;lt;span class="hljs-string"&amp;gt;'photoFile'&amp;lt;/span&amp;gt;);

                    &amp;lt;span class="hljs-keyword"&amp;gt;if&amp;lt;/span&amp;gt; (elem &amp;amp;&amp;amp; &amp;lt;span class="hljs-built_in"&amp;gt;document&amp;lt;/span&amp;gt;.createEvent) {
                        &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; evt = &amp;lt;span class="hljs-built_in"&amp;gt;document&amp;lt;/span&amp;gt;.createEvent(&amp;lt;span class="hljs-string"&amp;gt;"MouseEvents"&amp;lt;/span&amp;gt;);
                        evt.initEvent(&amp;lt;span class="hljs-string"&amp;gt;"click"&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-literal"&amp;gt;true&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-literal"&amp;gt;false&amp;lt;/span&amp;gt;);
                        elem.dispatchEvent(evt);
                    }
                },
                &amp;lt;span class="hljs-attr"&amp;gt;upload&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-keyword"&amp;gt;function&amp;lt;/span&amp;gt; (&amp;lt;span class="hljs-params"&amp;gt;&amp;lt;/span&amp;gt;) &amp;lt;/span&amp;gt;{
                    &amp;lt;span class="hljs-keyword"&amp;gt;let&amp;lt;/span&amp;gt; data = &amp;lt;span class="hljs-keyword"&amp;gt;new&amp;lt;/span&amp;gt; FormData();
                    data.append(&amp;lt;span class="hljs-string"&amp;gt;'file'&amp;lt;/span&amp;gt;, &amp;lt;span class="hljs-keyword"&amp;gt;this&amp;lt;/span&amp;gt;.$refs.myFiles.files[&amp;lt;span class="hljs-number"&amp;gt;0&amp;lt;/span&amp;gt;]);

                    axios.post(&amp;lt;span class="hljs-string"&amp;gt;'/photos'&amp;lt;/span&amp;gt;, data).then(&amp;lt;span class="hljs-function"&amp;gt;&amp;lt;span class="hljs-params"&amp;gt;res&amp;lt;/span&amp;gt; =&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-built_in"&amp;gt;console&amp;lt;/span&amp;gt;.log(res))
                }
            }
        });
    &amp;lt;span class="xml"&amp;gt;&amp;lt;span class="hljs-tag"&amp;gt;&amp;lt;/&amp;lt;span class="hljs-name"&amp;gt;script&amp;lt;/span&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above we created a Vue instance and stored the properties &lt;code&gt;photos&lt;/code&gt; and &lt;code&gt;loading&lt;/code&gt;. The &lt;code&gt;photos&lt;/code&gt; property stores the photo list and the &lt;code&gt;loading&lt;/code&gt; just holds a boolean that indicates if the photos are loading or not.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;mounted&lt;/code&gt; method we create an instance of our Pusher library. We then listen on the &lt;code&gt;photo-stream&lt;/code&gt; channel for the &lt;code&gt;new-photo&lt;/code&gt; event. When the event is triggered we append the new photo from the event to the &lt;code&gt;photos&lt;/code&gt; list. We also send a GET request to &lt;code&gt;/photos&lt;/code&gt; to fetch all the photos from the API. Replace the &lt;code&gt;PUSHER_APP_*&lt;/code&gt; keys with the one from your Pusher dashboard.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;methods&lt;/code&gt; property, we added a few methods. The &lt;code&gt;filePicker&lt;/code&gt; is triggered when the ‘Upload’ button is pressed on the UI. It triggers a file picker that allows the user to upload photos. The &lt;code&gt;upload&lt;/code&gt; method takes the uploaded file and sends a POST request with the file to the API for processing.&lt;/p&gt;

&lt;p&gt;That’s all for the frontend, you can save the file and head over to your web browser. Visit &lt;a href="http://127.0.0.1:9000" rel="noopener noreferrer"&gt;http://127.0.0.1:9000&lt;/a&gt; to see your application in action.&lt;/p&gt;

&lt;p&gt;Here’s how it will look again:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/1es3ne0caaid/5vVy2T7aY8qoOiCuA8oGQK/abdfe10038a2935fa4a894296db6630d/go-photo-feed-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/1es3ne0caaid/5vVy2T7aY8qoOiCuA8oGQK/abdfe10038a2935fa4a894296db6630d/go-photo-feed-demo.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we have been able to demonstrate how you can use Pusher Channels in your Go application to provide realtime features for your application. As seen from the code samples above, it is very easy to get started with Pusher Channels. Check the &lt;a href="https://pusher.com/docs" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; to see other ways you can use Pusher Channels to provide realtime features to your users.&lt;/p&gt;

&lt;p&gt;The source code for this application is available on &lt;a href="https://github.com/neoighodaro/realtime-photofeed-pusher-go" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post first appeared on the &lt;a href="https://pusher.com/tutorials/photo-feed-go-vuejs" rel="noopener noreferrer"&gt;Pusher blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>pusher</category>
      <category>go</category>
      <category>vue</category>
      <category>realtime</category>
    </item>
    <item>
      <title>CREATE A GO APPLICATION WITH ONLINE PRESENCE</title>
      <dc:creator>Neo</dc:creator>
      <pubDate>Tue, 18 Sep 2018 13:16:36 +0000</pubDate>
      <link>https://dev.to/neo/create-a-go-application-with-online-presence-4696</link>
      <guid>https://dev.to/neo/create-a-go-application-with-online-presence-4696</guid>
      <description>&lt;p&gt;&lt;em&gt;You will need Go 0.10+ installed on your machine.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When building applications that allow multiple users to interact with one another, it is essential to display their online presence so that each user gets an idea of how many other users are online.&lt;/p&gt;

&lt;p&gt;In this article, we will build a live streaming application that displays the online presence of the users currently streaming a video. We will use &lt;a href="https://golang.org/doc/install" rel="noopener noreferrer"&gt;Go&lt;/a&gt;, JavaScript (Vue) and &lt;a href="http://pusher.com/" rel="noopener noreferrer"&gt;Pusher&lt;/a&gt; for the development.&lt;/p&gt;

&lt;p&gt;Here’s a demo of the final application:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flcurf2cplvqulqo59tsp.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flcurf2cplvqulqo59tsp.gif" width="800" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The source code for this tutorial is available on GitHub.&lt;/p&gt;
&lt;h2&gt;
  
  
  PREREQUISITES
&lt;/h2&gt;

&lt;p&gt;To follow along with this article, you will need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A code editor like Visual Studio Code.&lt;/li&gt;
&lt;li&gt;Basic knowledge of the Go programming language.&lt;/li&gt;
&lt;li&gt;Go (version &amp;gt;= 0.10.x) installed on your computer. &lt;a href="https://golang.org/doc/install" rel="noopener noreferrer"&gt;Installation guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Basic knowledge of JavaScript (Vue).&lt;/li&gt;
&lt;li&gt;A Pusher application. Create one &lt;a href="https://dashboard.pusher.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.
Once you have all the above requirements, we can proceed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  BUILDING THE BACKEND SERVER
&lt;/h2&gt;

&lt;p&gt;We will build the backend server in Go. Create a new project directory in the src directory that is located in the &lt;code&gt;$GOPATH&lt;/code&gt;, let’s call this directory &lt;code&gt;go-pusher-presence-app&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ cd $GOPATH/src
    $ mkdir go-pusher-presence-app
    $ cd go-pusher-presence-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create a new Go file and call it &lt;code&gt;presence.go&lt;/code&gt;, this file will be where our entire backend server logic will be. Now, let’s pull in the official Go Pusher package with this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   $ go get github.com/pusher/pusher-http-go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the &lt;code&gt;presence.go&lt;/code&gt; file and paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // File: ./presence.go
    package main

    import (
        "encoding/json"
        "fmt"
        "io/ioutil"
        "log"
        "net/http"
        pusher "github.com/pusher/pusher-http-go"
    )

    var client = pusher.Client{
        AppId:   "PUSHER_APP_ID",
        Key:     "PUSHER_APP_KEY",
        Secret:  "PUSHER_APP_SECRET",
        Cluster: "PUSHER_APP_CLUSTER",
        Secure:  true,
    }

    type user struct {
        Username  string `json:"username" xml:"username" form:"username" query:"username"`
        Email string `json:"email" xml:"email" form:"email" query:"email"`
    }

    var loggedInUser user

    func main() {
        // Define our routes
        http.Handle("/", http.FileServer(http.Dir("./static")))
        http.HandleFunc("/isLoggedIn", isUserLoggedIn)
        http.HandleFunc("/new/user", NewUser)
        http.HandleFunc("/pusher/auth", pusherAuth)

        // Start executing the application on port 8090
        log.Fatal(http.ListenAndServe(":8090", nil))
    }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Replace the PUSHER_APP_* keys with the keys on your Pusher dashboard.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s a breakdown of what we’ve done in the code above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We imported all the packages that are required for the application to work, including Pusher.&lt;/li&gt;
&lt;li&gt;We instantiated the Pusher client that we will use to authenticate users from the client-side.&lt;/li&gt;
&lt;li&gt;We defined a user struct and gave it two the properties — username and email — so that Go knows how to handle incoming payloads and correctly bind it to a user instance.&lt;/li&gt;
&lt;li&gt;We created a global instance of the user struct so that we can use it to store a user’s name and email. This instance is going to somewhat serve the purpose of a session on a server, we will check that it is set before allowing a user to access the dashboard of this application.
In the main function, we registered four endpoints:&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;/ - loads all the static files from the static directory.&lt;/li&gt;
&lt;li&gt;/isLoggedIn - checks if a user is logged in or not and returns a fitting message.&lt;/li&gt;
&lt;li&gt;/new/user - allows a new user to connect and initializes the global user instance.&lt;/li&gt;
&lt;li&gt;/pusher/auth — authorizes users from the client-side.
In the same file, above the &lt;code&gt;main&lt;/code&gt; function, add the code for the handler function of the &lt;code&gt;/isLoggedIn&lt;/code&gt; endpoint:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  // File: ./presence.go

    // [...]

    func isUserLoggedIn(rw http.ResponseWriter, req *http.Request){
        if loggedInUser.Username != "" &amp;amp;&amp;amp; loggedInUser.Email != "" {
            json.NewEncoder(rw).Encode(loggedInUser)
        } else {
            json.NewEncoder(rw).Encode("false")
        }
    }

    // [...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the function above, let’s add the handler function for the &lt;code&gt;/new/user&lt;/code&gt; endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // File: ./presence.go

    // [...]

    func NewUser(rw http.ResponseWriter, req *http.Request) {
        body, err := ioutil.ReadAll(req.Body)
        if err != nil {
            panic(err)
        }
        err = json.Unmarshal(body, &amp;amp;loggedInUser)
        if err != nil {
            panic(err)
        }
        json.NewEncoder(rw).Encode(loggedInUser)
    }

    // [...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above, we receive a new user's details in a &lt;code&gt;POST&lt;/code&gt; request and bind it to an instance of the user struct. We further use this user instance to check if a user is logged in or not&lt;/p&gt;

&lt;p&gt;Lastly, after the function above, let’s add the code for the &lt;code&gt;/pusher/auth&lt;/code&gt; endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  // File: ./presence.go

    // [...]

    // -------------------------------------------------------
    // Here, we authorize users so that they can subscribe to 
    // the presence channel
    // -------------------------------------------------------

    func pusherAuth(res http.ResponseWriter, req *http.Request) {
        params, _ := ioutil.ReadAll(req.Body)

        data := pusher.MemberData{
            UserId: loggedInUser.Username,
            UserInfo: map[string]string{
                "email": loggedInUser.Email,
            },
        }

        response, err := client.AuthenticatePresenceChannel(params, data)
        if err != nil {
            panic(err)
        }

        fmt.Fprintf(res, string(response))
    }

    // [...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To ensure that every connected user has a unique presence, we used the properties of the global &lt;code&gt;loggedInUser&lt;/code&gt; variable in setting the pusher.MemberData instance.&lt;/p&gt;

&lt;p&gt;The syntax for authenticating a &lt;code&gt;Pusher&lt;/code&gt; presence channel is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    client.AuthenticatePresenceChannel(params, presenceData)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  BUILDING THE FRONTEND
&lt;/h2&gt;

&lt;p&gt;Next, in the root of the project, create a &lt;code&gt;static&lt;/code&gt; folder. Create two files the directory named &lt;code&gt;index.html&lt;/code&gt; and &lt;code&gt;dashboard.html&lt;/code&gt;. In the &lt;code&gt;index.html&lt;/code&gt; file, we will write the HTML code that allows users to connect to the live streaming application using their name and email.&lt;/p&gt;

&lt;h2&gt;
  
  
  SETTING UP THE CONNECTION PAGE
&lt;/h2&gt;

&lt;p&gt;Open the &lt;code&gt;index.html&lt;/code&gt; file and update it with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;!-- File: ./static/index.html --&amp;gt;
    &amp;lt;!DOCTYPE html&amp;gt;
    &amp;lt;html lang="en"&amp;gt;
        &amp;lt;head&amp;gt;
            &amp;lt;meta charset="utf-8"&amp;gt;
            &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"&amp;gt;
            &amp;lt;title&amp;gt;Live streamer&amp;lt;/title&amp;gt;
            &amp;lt;link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"&amp;gt;
            &amp;lt;style&amp;gt;
                  :root {
                    --input-padding-x: .75rem;
                    --input-padding-y: .75rem;
                  }
                  html,
                  body, body &amp;gt; div {
                    height: 100%;
                  }
                  body &amp;gt; div {
                    display: -ms-flexbox;
                    display: flex;
                    -ms-flex-align: center;
                    align-items: center;
                    padding-top: 40px;
                    padding-bottom: 40px;
                    background-color: #f5f5f5;
                  }
                  .form-signin {
                    width: 100%;
                    max-width: 420px;
                    padding: 15px;
                    margin: auto;
                  }
                  .form-label-group {
                    position: relative;
                    margin-bottom: 1rem;
                  }
                  .form-label-group &amp;gt; input,
                  .form-label-group &amp;gt; label {
                    padding: var(--input-padding-y) var(--input-padding-x);
                  }
                  .form-label-group &amp;gt; label {
                    position: absolute;
                    top: 0;
                    left: 0;
                    display: block;
                    width: 100%;
                    margin-bottom: 0; /* Override default `&amp;lt;label&amp;gt;` margin */
                    line-height: 1.5;
                    color: #495057;
                    cursor: text; /* Match the input under the label */
                    border: 1px solid transparent;
                    border-radius: .25rem;
                    transition: all .1s ease-in-out;
                  }
                  .form-label-group input::-webkit-input-placeholder {
                    color: transparent;
                  }
                  .form-label-group input:-ms-input-placeholder {
                    color: transparent;
                  }
                  .form-label-group input::-ms-input-placeholder {
                    color: transparent;
                  }
                  .form-label-group input::-moz-placeholder {
                    color: transparent;
                  }
                  .form-label-group input::placeholder {
                    color: transparent;
                  }
                  .form-label-group input:not(:placeholder-shown) {
                    padding-top: calc(var(--input-padding-y) + var(--input-padding-y) * (2 / 3));
                    padding-bottom: calc(var(--input-padding-y) / 3);
                  }
                  .form-label-group input:not(:placeholder-shown) ~ label {
                    padding-top: calc(var(--input-padding-y) / 3);
                    padding-bottom: calc(var(--input-padding-y) / 3);
                    font-size: 12px;
                    color: #777;
                  }
            &amp;lt;/style&amp;gt;
          &amp;lt;/head&amp;gt;

          &amp;lt;body&amp;gt;
            &amp;lt;div id="app"&amp;gt;
              &amp;lt;form class="form-signin"&amp;gt;
                &amp;lt;div class="text-center mb-4"&amp;gt;
                  &amp;lt;img class="mb-4" src="https://www.onlinelogomaker.com/blog/wp-content/uploads/2017/07/Fotolia_117855281_Subscription_Monthly_M.jpg" alt="" width="72" height="72"&amp;gt;
                  &amp;lt;h1 class="h3 mb-3 font-weight-normal"&amp;gt;Live streamer&amp;lt;/h1&amp;gt;
                  &amp;lt;p&amp;gt;STREAM YOUR FAVOURITE VIDEOS FOR FREE&amp;lt;/p&amp;gt;
                &amp;lt;/div&amp;gt;
                &amp;lt;div class="form-label-group"&amp;gt;
                    &amp;lt;input type="name" id="inputUsername" ref="username" class="form-control" placeholder="Username" required="" autofocus=""&amp;gt;
                      &amp;lt;label for="inputUsername"&amp;gt;Username&amp;lt;/label&amp;gt;
                  &amp;lt;/div&amp;gt;

                &amp;lt;div class="form-label-group"&amp;gt;
                  &amp;lt;input type="email" id="inputEmail" ref="email" class="form-control" placeholder="Email address" autofocus="" required&amp;gt;
                    &amp;lt;label for="inputEmail"&amp;gt;Email address&amp;lt;/label&amp;gt;
                &amp;lt;/div&amp;gt;

                &amp;lt;button class="btn btn-lg btn-primary btn-block" type="submit" @click.prevent="login"&amp;gt;Connect&amp;lt;/button&amp;gt;
                &amp;lt;p class="mt-5 mb-3 text-muted text-center"&amp;gt;© 2017-2018&amp;lt;/p&amp;gt;
              &amp;lt;/form&amp;gt;
              &amp;lt;/div&amp;gt;

              &amp;lt;script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On line 106, we added Vue using a CDN. Let’s add the Vue script for the page.&lt;/p&gt;

&lt;p&gt;Before the closing &lt;code&gt;body&lt;/code&gt; tag add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
      var app = new Vue({
        el: '#app',
        methods: {
          login: function () {
            let username = this.$refs.username.value
            let email = this.$refs.email.value

            fetch('new/user', {
              method: 'POST',
              headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
              },
              body: JSON.stringify({username, email})
            })
            .then(res =&amp;gt; res.json())
            .then(data =&amp;gt; window.location.replace('/dashboard.html'))
          }
        }
      })
    &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script above submits user data to the backend Go server and navigates the browser’s location to the dashboard’s URL.&lt;/p&gt;

&lt;p&gt;Next, let’s build the dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  SETTING UP THE DASHBOARD
&lt;/h2&gt;

&lt;p&gt;Open the &lt;code&gt;dashboard.html&lt;/code&gt; file and update it with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;!-- File: ./static/dashboard.html --&amp;gt;
    &amp;lt;!DOCTYPE html&amp;gt;
    &amp;lt;html lang="en"&amp;gt;
      &amp;lt;head&amp;gt;
        &amp;lt;meta charset="utf-8"&amp;gt;
        &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"&amp;gt;
        &amp;lt;link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"&amp;gt;
        &amp;lt;title&amp;gt;Live streamer | Dashboard&amp;lt;/title&amp;gt;
      &amp;lt;/head&amp;gt;
      &amp;lt;body&amp;gt;
        &amp;lt;div id="app"&amp;gt;
          &amp;lt;div class="container-fluid row shadow p-1 mb-3"&amp;gt;
            &amp;lt;div class="col-3"&amp;gt;
              &amp;lt;img class="ml-3" src="https://www.onlinelogomaker.com/blog/wp-content/uploads/2017/07/Fotolia_117855281_Subscription_Monthly_M.jpg" height="72px" width="72px"/&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div class="col-6 ml-auto mt-3"&amp;gt;
              &amp;lt;div class="input-group"&amp;gt;
                &amp;lt;input type="text" class="form-control" aria-label="Text input with dropdown button"&amp;gt;
                &amp;lt;div class="input-group-append"&amp;gt;
                  &amp;lt;button class="btn btn-outline-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"&amp;gt;Search&amp;lt;/button&amp;gt;
                &amp;lt;/div&amp;gt;
              &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div class="col-3 float-right"&amp;gt;
              &amp;lt;img src="https://www.seoclerk.com/pics/319222-1IvI0s1421931178.png"  height="72px" width="72px" class="rounded-circle border"/&amp;gt;
              &amp;lt;p class="mr-auto mt-3 d-inline"&amp;gt; {{ username }} &amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
          &amp;lt;div class="container-fluid"&amp;gt;
            &amp;lt;div class="row"&amp;gt;
              &amp;lt;div class="col-8"&amp;gt;
                &amp;lt;div class="embed-responsive embed-responsive-16by9"&amp;gt;
                  &amp;lt;iframe width="854" height="480" class="embed-responsive-item" src="https://www.youtube.com/embed/VYOjWnS4cMY" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;
                &amp;lt;/div&amp;gt;
                &amp;lt;div class="text-center mt-3 p-3 text-muted font-weight-bold border"&amp;gt;
                  {{ member }} person(s) is/are currently viewing this video 
                  &amp;lt;hr&amp;gt;
                  &amp;lt;li class="m-auto text-success" v-for="member in connectedMembers"&amp;gt;
                    {{ member }}
                  &amp;lt;/li&amp;gt;
                &amp;lt;/div&amp;gt;
              &amp;lt;/div&amp;gt;
              &amp;lt;div class="col-4 border text-justify" style="background: #e0e0e0; height: 30em; overflow-y: scroll; position: relative;"&amp;gt;
                &amp;lt;div class="border invisible h-50 w-75 text-center" ref="added" style="font-size: 2rem; position: absolute; right: 0; background: #48cbe0"&amp;gt;{{ addedMember }} just started watching.&amp;lt;/div&amp;gt;
                &amp;lt;div class="border invisible h-50 w-75 text-center" ref="removed" style="font-size: 2rem; position: absolute; right: 0; background: #ff8325"&amp;gt;{{ removedMember }} just stopped watching.&amp;lt;/div&amp;gt;
                &amp;lt;div class="h-75 text-center"&amp;gt;
                  &amp;lt;h2 class="text-center my-3"&amp;gt; Lyrics &amp;lt;/h2&amp;gt;
                  &amp;lt;p class="w-75 m-auto" style="font-size: 1.5rem"&amp;gt;
                    We just wanna party&amp;lt;br&amp;gt;
                    Party just for you&amp;lt;br&amp;gt;
                    We just want the money&amp;lt;br&amp;gt;
                    Money just for you&amp;lt;br&amp;gt;
                    I know you wanna party&amp;lt;br&amp;gt;
                    Party just for me&amp;lt;br&amp;gt;
                    Girl, you got me dancin' (yeah, girl, you got me dancin')&amp;lt;br&amp;gt;
                    Dance and shake the frame&amp;lt;br&amp;gt;
                    We just wanna party (yeah)&amp;lt;br&amp;gt;
                    Party just for you (yeah)&amp;lt;br&amp;gt;
                    We just want the money (yeah)&amp;lt;br&amp;gt;
                    Money just for you (you)&amp;lt;br&amp;gt;
                    I know you wanna party (yeah)&amp;lt;br&amp;gt;
                    Party just for me (yeah)&amp;lt;br&amp;gt;
                    Girl, you got me dancin' (yeah, girl, you got me dancin')&amp;lt;br&amp;gt;
                    Dance and shake the frame (you)&amp;lt;br&amp;gt;
                    This is America&amp;lt;br&amp;gt;
                    Don't catch you slippin' up&amp;lt;br&amp;gt;
                    Don't catch you slippin' up&amp;lt;br&amp;gt;
                    Look what I'm whippin' up&amp;lt;br&amp;gt;
                    This is America (woo)&amp;lt;br&amp;gt;
                    Don't catch you slippin' up&amp;lt;br&amp;gt;
                    Don't catch you slippin' up&amp;lt;br&amp;gt;
                    Look what I'm whippin' up&amp;lt;br&amp;gt;
                  &amp;lt;/p&amp;gt;
                &amp;lt;/div&amp;gt;
              &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;script src="https://js.pusher.com/4.2/pusher.min.js"&amp;gt;&amp;lt;/script&amp;gt;
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;⚠️ Video is an embed from YouTube and may not play depending on your region.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;On line 80 we imported the JavaScript Pusher library so let’s add some code to utilize it. Before the closing body tag, add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;script&amp;gt;
    var app = new Vue({
        el: '#app',
        data: {
            username: '',
            member: 0,
            addedMember: '',
            removedMember: '',
            connectedMembers: []
        },

        created() {
            fetch('/isLoggedIn', {
                method: 'GET',
                headers: {
                    'Accept': 'application/json',
                    'Content-Type': 'application/json'
                }
            })
            .then(res =&amp;gt; res.json())
            .then(data =&amp;gt; {
                if (data != 'false') {
                    this.username = data.username
                } else {
                    window.location.replace('/')
                }
            })

            this.subscribe()
        },

        methods: {
            subscribe: function () {
                const pusher = new Pusher('PUSHER_APP_KEY', {
                    authEndpoint: '/pusher/auth',
                    cluster: 'PUSHER_APP_CLUSTER',
                    encrypted: true
                });

                let channel = pusher.subscribe('presence-channel')

                channel.bind('pusher:subscription_succeeded', data =&amp;gt; {
                    this.member = data.count
                    data.each(member =&amp;gt; this.connectedMembers.push(member.id))
                })

                // Display a notification when a member comes online
                channel.bind('pusher:member_added', data =&amp;gt; {
                    this.member++
                    this.connectedMembers.push(data.id)
                    this.addedMember = data.id

                    this.$refs.added.classList.add('visible')
                    this.$refs.added.classList.remove('invisible')

                    window.setTimeout(() =&amp;gt; {
                        this.$refs.added.classList.remove('visible');
                        this.$refs.added.classList.add('invisible');
                    }, 3000)
                });

                // Display a notification when a member goes offline
                channel.bind('pusher:member_removed', data =&amp;gt; {
                    this.member--
                    let index = this.connectedMembers.indexOf(data.id)

                    if (index &amp;gt; -1) {
                        this.connectedMembers.splice(index, 1)
                    }

                    this.removedMember = data.id
                    this.$refs.removed.classList.add('visible')
                    this.$refs.removed.classList.remove('invisible')

                    window.setTimeout(() =&amp;gt; {
                        this.$refs.removed.classList.remove('visible')
                        this.$refs.removed.classList.add('invisible')
                    }, 3000)
                })
            }
        }
    })
    &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will need Go 0.10+ installed on your machine.&lt;br&gt;
When building applications that allow multiple users to interact with one another, it is essential to display their online presence so that each user gets an idea of how many other users are online.&lt;/p&gt;

&lt;p&gt;In this article, we will build a live streaming application that displays the online presence of the users currently streaming a video. We will use Go, JavaScript (Vue) and Pusher for the development.&lt;/p&gt;

&lt;p&gt;Here’s a demo of the final application:&lt;/p&gt;

&lt;p&gt;go-online-presence-demo&lt;/p&gt;

&lt;p&gt;The source code for this tutorial is available on GitHub.&lt;/p&gt;
&lt;h2&gt;
  
  
  PREREQUISITES
&lt;/h2&gt;

&lt;p&gt;To follow along with this article, you will need the following:&lt;/p&gt;

&lt;p&gt;A code editor like Visual Studio Code.&lt;br&gt;
Basic knowledge of the Go programming language.&lt;br&gt;
Go (version &amp;gt;= 0.10.x) installed on your computer. Installation guide.&lt;br&gt;
Basic knowledge of JavaScript (Vue).&lt;br&gt;
A Pusher application. Create one here.&lt;br&gt;
Once you have all the above requirements, we can proceed.&lt;/p&gt;
&lt;h2&gt;
  
  
  BUILDING THE BACKEND SERVER
&lt;/h2&gt;

&lt;p&gt;We will build the backend server in Go. Create a new project directory in the src directory that is located in the $GOPATH, let’s call this directory go-pusher-presence-app.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd $GOPATH/src
$ mkdir go-pusher-presence-app
$ cd go-pusher-presence-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Next, create a new Go file and call it presence.go, this file will be where our entire backend server logic will be. Now, let’s pull in the official Go Pusher package with this command:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ go get github.com/pusher/pusher-http-go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Open the presence.go file and paste the following code:&lt;br&gt;
    // File: ./presence.go&lt;br&gt;
    package main&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    pusher "github.com/pusher/pusher-http-go"
)

var client = pusher.Client{
    AppId:   "PUSHER_APP_ID",
    Key:     "PUSHER_APP_KEY",
    Secret:  "PUSHER_APP_SECRET",
    Cluster: "PUSHER_APP_CLUSTER",
    Secure:  true,
}

type user struct {
    Username  string `json:"username" xml:"username" form:"username" query:"username"`
    Email string `json:"email" xml:"email" form:"email" query:"email"`
}

var loggedInUser user

func main() {
    // Define our routes
    http.Handle("/", http.FileServer(http.Dir("./static")))
    http.HandleFunc("/isLoggedIn", isUserLoggedIn)
    http.HandleFunc("/new/user", NewUser)
    http.HandleFunc("/pusher/auth", pusherAuth)

    // Start executing the application on port 8090
    log.Fatal(http.ListenAndServe(":8090", nil))
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Replace the PUSHER_APP_* keys with the keys on your Pusher dashboard.&lt;/p&gt;

&lt;p&gt;Here’s a breakdown of what we’ve done in the code above:&lt;/p&gt;

&lt;p&gt;We imported all the packages that are required for the application to work, including Pusher.&lt;br&gt;
We instantiated the Pusher client that we will use to authenticate users from the client-side.&lt;br&gt;
We defined a user struct and gave it two the properties — username and email — so that Go knows how to handle incoming payloads and correctly bind it to a user instance.&lt;br&gt;
We created a global instance of the user struct so that we can use it to store a user’s name and email. This instance is going to somewhat serve the purpose of a session on a server, we will check that it is set before allowing a user to access the dashboard of this application.&lt;br&gt;
In the main function, we registered four endpoints:&lt;/p&gt;

&lt;p&gt;/ - loads all the static files from the static directory.&lt;br&gt;
/isLoggedIn - checks if a user is logged in or not and returns a fitting message.&lt;br&gt;
/new/user - allows a new user to connect and initializes the global user instance.&lt;br&gt;
/pusher/auth — authorizes users from the client-side.&lt;br&gt;
In the same file, above the main function, add the code for the handler function of the /isLoggedIn endpoint:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// File: ./presence.go

// [...]

func isUserLoggedIn(rw http.ResponseWriter, req *http.Request){
    if loggedInUser.Username != "" &amp;amp;&amp;amp; loggedInUser.Email != "" {
        json.NewEncoder(rw).Encode(loggedInUser)
    } else {
        json.NewEncoder(rw).Encode("false")
    }
}

// [...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;After the function above, let’s add the handler function for the /new/user endpoint:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// File: ./presence.go

// [...]

func NewUser(rw http.ResponseWriter, req *http.Request) {
    body, err := ioutil.ReadAll(req.Body)
    if err != nil {
        panic(err)
    }
    err = json.Unmarshal(body, &amp;amp;loggedInUser)
    if err != nil {
        panic(err)
    }
    json.NewEncoder(rw).Encode(loggedInUser)
}

// [...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Above, we receive a new user's details in a POST request and bind it to an instance of the user struct. We further use this user instance to check if a user is logged in or not&lt;/p&gt;

&lt;p&gt;Lastly, after the function above, let’s add the code for the /pusher/auth endpoint:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// File: ./presence.go

// [...]

// -------------------------------------------------------
// Here, we authorize users so that they can subscribe to 
// the presence channel
// -------------------------------------------------------

func pusherAuth(res http.ResponseWriter, req *http.Request) {
    params, _ := ioutil.ReadAll(req.Body)

    data := pusher.MemberData{
        UserId: loggedInUser.Username,
        UserInfo: map[string]string{
            "email": loggedInUser.Email,
        },
    }

    response, err := client.AuthenticatePresenceChannel(params, data)
    if err != nil {
        panic(err)
    }

    fmt.Fprintf(res, string(response))
}

// [...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;To ensure that every connected user has a unique presence, we used the properties of the global loggedInUser variable in setting the pusher.MemberData instance.&lt;/p&gt;

&lt;p&gt;The syntax for authenticating a Pusher presence channel is:&lt;br&gt;
    client.AuthenticatePresenceChannel(params, presenceData)&lt;/p&gt;
&lt;h2&gt;
  
  
  BUILDING THE FRONTEND
&lt;/h2&gt;

&lt;p&gt;Next, in the root of the project, create a static folder. Create two files the directory named index.html and dashboard.html. In the index.html file, we will write the HTML code that allows users to connect to the live streaming application using their name and email.&lt;/p&gt;
&lt;h2&gt;
  
  
  SETTING UP THE CONNECTION PAGE
&lt;/h2&gt;

&lt;p&gt;Open the index.html file and update it with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- File: ./static/index.html --&amp;gt;
    &amp;lt;!DOCTYPE html&amp;gt;
    &amp;lt;html lang="en"&amp;gt;
        &amp;lt;head&amp;gt;
            &amp;lt;meta charset="utf-8"&amp;gt;
            &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"&amp;gt;
            &amp;lt;title&amp;gt;Live streamer&amp;lt;/title&amp;gt;
            &amp;lt;link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"&amp;gt;
            &amp;lt;style&amp;gt;
                  :root {
                    --input-padding-x: .75rem;
                    --input-padding-y: .75rem;
                  }
                  html,
                  body, body &amp;gt; div {
                    height: 100%;
                  }
                  body &amp;gt; div {
                    display: -ms-flexbox;
                    display: flex;
                    -ms-flex-align: center;
                    align-items: center;
                    padding-top: 40px;
                    padding-bottom: 40px;
                    background-color: #f5f5f5;
                  }
                  .form-signin {
                    width: 100%;
                    max-width: 420px;
                    padding: 15px;
                    margin: auto;
                  }
                  .form-label-group {
                    position: relative;
                    margin-bottom: 1rem;
                  }
                  .form-label-group &amp;gt; input,
                  .form-label-group &amp;gt; label {
                    padding: var(--input-padding-y) var(--input-padding-x);
                  }
                  .form-label-group &amp;gt; label {
                    position: absolute;
                    top: 0;
                    left: 0;
                    display: block;
                    width: 100%;
                    margin-bottom: 0; /* Override default `&amp;lt;label&amp;gt;` margin */
                    line-height: 1.5;
                    color: #495057;
                    cursor: text; /* Match the input under the label */
                    border: 1px solid transparent;
                    border-radius: .25rem;
                    transition: all .1s ease-in-out;
                  }
                  .form-label-group input::-webkit-input-placeholder {
                    color: transparent;
                  }
                  .form-label-group input:-ms-input-placeholder {
                    color: transparent;
                  }
                  .form-label-group input::-ms-input-placeholder {
                    color: transparent;
                  }
                  .form-label-group input::-moz-placeholder {
                    color: transparent;
                  }
                  .form-label-group input::placeholder {
                    color: transparent;
                  }
                  .form-label-group input:not(:placeholder-shown) {
                    padding-top: calc(var(--input-padding-y) + var(--input-padding-y) * (2 / 3));
                    padding-bottom: calc(var(--input-padding-y) / 3);
                  }
                  .form-label-group input:not(:placeholder-shown) ~ label {
                    padding-top: calc(var(--input-padding-y) / 3);
                    padding-bottom: calc(var(--input-padding-y) / 3);
                    font-size: 12px;
                    color: #777;
                  }
            &amp;lt;/style&amp;gt;
          &amp;lt;/head&amp;gt;

          &amp;lt;body&amp;gt;
            &amp;lt;div id="app"&amp;gt;
              &amp;lt;form class="form-signin"&amp;gt;
                &amp;lt;div class="text-center mb-4"&amp;gt;
                  &amp;lt;img class="mb-4" src="https://www.onlinelogomaker.com/blog/wp-content/uploads/2017/07/Fotolia_117855281_Subscription_Monthly_M.jpg" alt="" width="72" height="72"&amp;gt;
                  &amp;lt;h1 class="h3 mb-3 font-weight-normal"&amp;gt;Live streamer&amp;lt;/h1&amp;gt;
                  &amp;lt;p&amp;gt;STREAM YOUR FAVOURITE VIDEOS FOR FREE&amp;lt;/p&amp;gt;
                &amp;lt;/div&amp;gt;
                &amp;lt;div class="form-label-group"&amp;gt;
                    &amp;lt;input type="name" id="inputUsername" ref="username" class="form-control" placeholder="Username" required="" autofocus=""&amp;gt;
                      &amp;lt;label for="inputUsername"&amp;gt;Username&amp;lt;/label&amp;gt;
                  &amp;lt;/div&amp;gt;

                &amp;lt;div class="form-label-group"&amp;gt;
                  &amp;lt;input type="email" id="inputEmail" ref="email" class="form-control" placeholder="Email address" autofocus="" required&amp;gt;
                    &amp;lt;label for="inputEmail"&amp;gt;Email address&amp;lt;/label&amp;gt;
                &amp;lt;/div&amp;gt;

                &amp;lt;button class="btn btn-lg btn-primary btn-block" type="submit" @click.prevent="login"&amp;gt;Connect&amp;lt;/button&amp;gt;
                &amp;lt;p class="mt-5 mb-3 text-muted text-center"&amp;gt;© 2017-2018&amp;lt;/p&amp;gt;
              &amp;lt;/form&amp;gt;
              &amp;lt;/div&amp;gt;

              &amp;lt;script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On line 106, we added Vue using a CDN. Let’s add the Vue script for the page.&lt;/p&gt;

&lt;p&gt;Before the closing body tag add the following code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  var app = new Vue({
    el: '#app',
    methods: {
      login: function () {
        let username = this.$refs.username.value
        let email = this.$refs.email.value

        fetch('new/user', {
          method: 'POST',
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({username, email})
        })
        .then(res =&amp;gt; res.json())
        .then(data =&amp;gt; window.location.replace('/dashboard.html'))
      }
    }
  })
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This script above submits user data to the backend Go server and navigates the browser’s location to the dashboard’s URL.&lt;/p&gt;

&lt;p&gt;Next, let’s build the dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  SETTING UP THE DASHBOARD
&lt;/h2&gt;

&lt;p&gt;Open the dashboard.html file and update it with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;!-- File: ./static/dashboard.html --&amp;gt;
    &amp;lt;!DOCTYPE html&amp;gt;
    &amp;lt;html lang="en"&amp;gt;
      &amp;lt;head&amp;gt;
        &amp;lt;meta charset="utf-8"&amp;gt;
        &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"&amp;gt;
        &amp;lt;link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"&amp;gt;
        &amp;lt;title&amp;gt;Live streamer | Dashboard&amp;lt;/title&amp;gt;
      &amp;lt;/head&amp;gt;
      &amp;lt;body&amp;gt;
        &amp;lt;div id="app"&amp;gt;
          &amp;lt;div class="container-fluid row shadow p-1 mb-3"&amp;gt;
            &amp;lt;div class="col-3"&amp;gt;
              &amp;lt;img class="ml-3" src="https://www.onlinelogomaker.com/blog/wp-content/uploads/2017/07/Fotolia_117855281_Subscription_Monthly_M.jpg" height="72px" width="72px"/&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div class="col-6 ml-auto mt-3"&amp;gt;
              &amp;lt;div class="input-group"&amp;gt;
                &amp;lt;input type="text" class="form-control" aria-label="Text input with dropdown button"&amp;gt;
                &amp;lt;div class="input-group-append"&amp;gt;
                  &amp;lt;button class="btn btn-outline-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"&amp;gt;Search&amp;lt;/button&amp;gt;
                &amp;lt;/div&amp;gt;
              &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div class="col-3 float-right"&amp;gt;
              &amp;lt;img src="https://www.seoclerk.com/pics/319222-1IvI0s1421931178.png"  height="72px" width="72px" class="rounded-circle border"/&amp;gt;
              &amp;lt;p class="mr-auto mt-3 d-inline"&amp;gt; {{ username }} &amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
          &amp;lt;div class="container-fluid"&amp;gt;
            &amp;lt;div class="row"&amp;gt;
              &amp;lt;div class="col-8"&amp;gt;
                &amp;lt;div class="embed-responsive embed-responsive-16by9"&amp;gt;
                  &amp;lt;iframe width="854" height="480" class="embed-responsive-item" src="https://www.youtube.com/embed/VYOjWnS4cMY" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;
                &amp;lt;/div&amp;gt;
                &amp;lt;div class="text-center mt-3 p-3 text-muted font-weight-bold border"&amp;gt;
                  {{ member }} person(s) is/are currently viewing this video 
                  &amp;lt;hr&amp;gt;
                  &amp;lt;li class="m-auto text-success" v-for="member in connectedMembers"&amp;gt;
                    {{ member }}
                  &amp;lt;/li&amp;gt;
                &amp;lt;/div&amp;gt;
              &amp;lt;/div&amp;gt;
              &amp;lt;div class="col-4 border text-justify" style="background: #e0e0e0; height: 30em; overflow-y: scroll; position: relative;"&amp;gt;
                &amp;lt;div class="border invisible h-50 w-75 text-center" ref="added" style="font-size: 2rem; position: absolute; right: 0; background: #48cbe0"&amp;gt;{{ addedMember }} just started watching.&amp;lt;/div&amp;gt;
                &amp;lt;div class="border invisible h-50 w-75 text-center" ref="removed" style="font-size: 2rem; position: absolute; right: 0; background: #ff8325"&amp;gt;{{ removedMember }} just stopped watching.&amp;lt;/div&amp;gt;
                &amp;lt;div class="h-75 text-center"&amp;gt;
                  &amp;lt;h2 class="text-center my-3"&amp;gt; Lyrics &amp;lt;/h2&amp;gt;
                  &amp;lt;p class="w-75 m-auto" style="font-size: 1.5rem"&amp;gt;
                    We just wanna party&amp;lt;br&amp;gt;
                    Party just for you&amp;lt;br&amp;gt;
                    We just want the money&amp;lt;br&amp;gt;
                    Money just for you&amp;lt;br&amp;gt;
                    I know you wanna party&amp;lt;br&amp;gt;
                    Party just for me&amp;lt;br&amp;gt;
                    Girl, you got me dancin' (yeah, girl, you got me dancin')&amp;lt;br&amp;gt;
                    Dance and shake the frame&amp;lt;br&amp;gt;
                    We just wanna party (yeah)&amp;lt;br&amp;gt;
                    Party just for you (yeah)&amp;lt;br&amp;gt;
                    We just want the money (yeah)&amp;lt;br&amp;gt;
                    Money just for you (you)&amp;lt;br&amp;gt;
                    I know you wanna party (yeah)&amp;lt;br&amp;gt;
                    Party just for me (yeah)&amp;lt;br&amp;gt;
                    Girl, you got me dancin' (yeah, girl, you got me dancin')&amp;lt;br&amp;gt;
                    Dance and shake the frame (you)&amp;lt;br&amp;gt;
                    This is America&amp;lt;br&amp;gt;
                    Don't catch you slippin' up&amp;lt;br&amp;gt;
                    Don't catch you slippin' up&amp;lt;br&amp;gt;
                    Look what I'm whippin' up&amp;lt;br&amp;gt;
                    This is America (woo)&amp;lt;br&amp;gt;
                    Don't catch you slippin' up&amp;lt;br&amp;gt;
                    Don't catch you slippin' up&amp;lt;br&amp;gt;
                    Look what I'm whippin' up&amp;lt;br&amp;gt;
                  &amp;lt;/p&amp;gt;
                &amp;lt;/div&amp;gt;
              &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;script src="https://js.pusher.com/4.2/pusher.min.js"&amp;gt;&amp;lt;/script&amp;gt;
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⚠️ Video is an embed from YouTube and may not play depending on your region.&lt;/p&gt;

&lt;p&gt;On line 80 we imported the JavaScript Pusher library so let’s add some code to utilize it. Before the closing body tag, add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;script&amp;gt;
    var app = new Vue({
        el: '#app',
        data: {
            username: '',
            member: 0,
            addedMember: '',
            removedMember: '',
            connectedMembers: []
        },

        created() {
            fetch('/isLoggedIn', {
                method: 'GET',
                headers: {
                    'Accept': 'application/json',
                    'Content-Type': 'application/json'
                }
            })
            .then(res =&amp;gt; res.json())
            .then(data =&amp;gt; {
                if (data != 'false') {
                    this.username = data.username
                } else {
                    window.location.replace('/')
                }
            })

            this.subscribe()
        },

        methods: {
            subscribe: function () {
                const pusher = new Pusher('PUSHER_APP_KEY', {
                    authEndpoint: '/pusher/auth',
                    cluster: 'PUSHER_APP_CLUSTER',
                    encrypted: true
                });

                let channel = pusher.subscribe('presence-channel')

                channel.bind('pusher:subscription_succeeded', data =&amp;gt; {
                    this.member = data.count
                    data.each(member =&amp;gt; this.connectedMembers.push(member.id))
                })

                // Display a notification when a member comes online
                channel.bind('pusher:member_added', data =&amp;gt; {
                    this.member++
                    this.connectedMembers.push(data.id)
                    this.addedMember = data.id

                    this.$refs.added.classList.add('visible')
                    this.$refs.added.classList.remove('invisible')

                    window.setTimeout(() =&amp;gt; {
                        this.$refs.added.classList.remove('visible');
                        this.$refs.added.classList.add('invisible');
                    }, 3000)
                });

                // Display a notification when a member goes offline
                channel.bind('pusher:member_removed', data =&amp;gt; {
                    this.member--
                    let index = this.connectedMembers.indexOf(data.id)

                    if (index &amp;gt; -1) {
                        this.connectedMembers.splice(index, 1)
                    }

                    this.removedMember = data.id
                    this.$refs.removed.classList.add('visible')
                    this.$refs.removed.classList.remove('invisible')

                    window.setTimeout(() =&amp;gt; {
                        this.$refs.removed.classList.remove('visible')
                        this.$refs.removed.classList.add('invisible')
                    }, 3000)
                })
            }
        }
    })
    &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the snippet above, we created some Vue data variables to display reactive updates on different parts of the DOM. We also registered a &lt;code&gt;created()&lt;/code&gt; lifecycle hook that checks if a user is connected on the backend server and eligible to view the dashboard before calling the &lt;code&gt;subscribe()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;subscribe()&lt;/code&gt; method first configures a Pusher instance using the keys provided on the dashboard then subscribes to a presence channel. Next, it binds to several events that are available on the returned object of a presence channel subscription.&lt;/p&gt;

&lt;p&gt;In the callback function of these bindings, we are able to update the state of the data variables, this is how we display the visual updates on user presence in this application.&lt;/p&gt;

&lt;h2&gt;
  
  
  TESTING THE APPLICATION
&lt;/h2&gt;

&lt;p&gt;We can test the application by compiling down the Go source code and running it with this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ go run presence.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application will be available for testing on this address &lt;a href="http://127.0.0.1:8090/" rel="noopener noreferrer"&gt;http://127.0.0.1:8090&lt;/a&gt;, here’s a display of how the application should look:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffdj06z1x1zt3qbvgzbzj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffdj06z1x1zt3qbvgzbzj.gif" width="800" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  CONCLUSION
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we have learned how to leverage the Pusher SDK in creating a live streaming application powered by a Go backend server.&lt;/p&gt;

&lt;p&gt;The source code for this tutorial is available on [GitHub(&lt;a href="https://github.com/neoighodaro/go-pusher-presence-app" rel="noopener noreferrer"&gt;https://github.com/neoighodaro/go-pusher-presence-app&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;This was first published on &lt;a href="https://pusher.com/tutorials/go-vue-online-presence" rel="noopener noreferrer"&gt;pusher&lt;/a&gt; site.&lt;/p&gt;

</description>
      <category>go</category>
      <category>youtube</category>
      <category>app</category>
    </item>
    <item>
      <title>Build a cryptocurrency alert app using Kotlin and Go: Part 2 — The backend</title>
      <dc:creator>Neo</dc:creator>
      <pubDate>Wed, 05 Sep 2018 06:46:00 +0000</pubDate>
      <link>https://dev.to/neo/build-a-cryptocurrency-alert-app-using-kotlin-and-go-part-2--the-backend-1hhn</link>
      <guid>https://dev.to/neo/build-a-cryptocurrency-alert-app-using-kotlin-and-go-part-2--the-backend-1hhn</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;You will need Android Studio 3+ and Go 1.10.2+ installed on your machine. You should have some familiarity with Android development and the Kotlin language.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In &lt;a href="https://dev.to/neo/build-a-cryptocurrency-alert-app-using-kotlin-and-go-part-1the-frontend-2h9d"&gt;the first part&lt;/a&gt; of this article, we started building our service by creating our Android application. The application, however, requires a backend to work properly. So in this part, we will be creating the backend of the application.&lt;/p&gt;

&lt;p&gt;We will be using Go to build the backend of the application. The framework in Go we will be using is &lt;a href="https://echo.labstack.com/" rel="noopener noreferrer"&gt;Echo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As a recap, here is a screen recording of what we will have built when we are done:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flajznltmchj7lezykleo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flajznltmchj7lezykleo.gif" width="1024" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;To follow along, you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To have completed &lt;a href="https://pusher.com/tutorials/cryptocurrency-kotlin-go-part-1" rel="noopener noreferrer"&gt;part one&lt;/a&gt; of the article.&lt;/li&gt;
&lt;li&gt;Android Studio installed on your machine (v3.x or later). &lt;a href="https://developer.android.com/studio/index.html" rel="noopener noreferrer"&gt;Download here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Go version 1.10.2 or later &lt;a href="https://golang.org/doc/install#install" rel="noopener noreferrer"&gt;installed&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.sqlitetutorial.net/download-install-sqlite/" rel="noopener noreferrer"&gt;SQLite installed&lt;/a&gt; on your machine.&lt;/li&gt;
&lt;li&gt;Basic knowledge on using the Android Studio IDE.&lt;/li&gt;
&lt;li&gt;Basic knowledge of Kotlin programming language. See the &lt;a href="https://kotlinlang.org/docs/reference/" rel="noopener noreferrer"&gt;official docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Basic knowledge of Go and the &lt;a href="https://echo.labstack.com/" rel="noopener noreferrer"&gt;Echo framework&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Building our Go API
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Setting up
&lt;/h4&gt;

&lt;p&gt;To get started, create a new project directory for your application. We will create one called &lt;code&gt;backend&lt;/code&gt;. It is recommended that you create this in your &lt;code&gt;$GOPATH&lt;/code&gt; however, it is not a requirement.&lt;/p&gt;

&lt;p&gt;In the project directory, create three new directories:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; database&lt;/li&gt;
&lt;li&gt; notification&lt;/li&gt;
&lt;li&gt; routes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the &lt;code&gt;database&lt;/code&gt; directory, create a new directory called &lt;code&gt;model&lt;/code&gt;. In this &lt;code&gt;database&lt;/code&gt; directory, we will store all things related to the database including the SQLite database file, the &lt;code&gt;model&lt;/code&gt;, and &lt;code&gt;database&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;notification&lt;/code&gt; directory, we will have a package that will contain everything needed to send push notifications to the devices.&lt;/p&gt;

&lt;p&gt;Finally, in the &lt;code&gt;routes&lt;/code&gt; directory, we will have the &lt;code&gt;routes&lt;/code&gt; package where we have the logic for each HTTP request.&lt;/p&gt;

&lt;p&gt;Now let’s start building the application.&lt;/p&gt;

&lt;p&gt;Create a new &lt;code&gt;main.go&lt;/code&gt; file in the root of the project. In this file, we will be adding the core of the project. We will be setting up the routing, middleware, and database.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;main.go&lt;/code&gt; file, paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// File: ./main.go
package main

import (
    "./database"
    "./routes"
    "github.com/labstack/echo"
    "github.com/labstack/echo/middleware"
)

func main() {
    db := database.Initialize("./database/db.sqlite")
    database.Migrate(db)
    e := echo.New()
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())
    e.GET("/fetch-values", routes.GetPrices())
    e.POST("/btc-pref", routes.SaveDeviceSettings(db))
    e.POST("/eth-pref", routes.SaveDeviceSettings(db))
    e.GET("/simulate", routes.SimulatePriceChanges(db))
    e.Start(":9000")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code above, we first imported some packages that the Go script will need to work. Then we instantiate the database using the &lt;code&gt;database&lt;/code&gt; subpackage that we imported. Next, we run the migration on the &lt;code&gt;db&lt;/code&gt; instance. This will create the database table the application needs to run if it does not already exist.&lt;/p&gt;

&lt;p&gt;Next, we create a new Echo instance &lt;code&gt;e&lt;/code&gt;. We then use the instance to register the &lt;a href="https://echo.labstack.com/middleware/logger" rel="noopener noreferrer"&gt;Logger&lt;/a&gt; middleware and the &lt;a href="https://echo.labstack.com/middleware/recover" rel="noopener noreferrer"&gt;Recover&lt;/a&gt; middleware.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Logger middleware logs the information about each HTTP request.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Recover middleware recovers from panics anywhere in the chain, prints stack trace and handles the control to the centralize *&lt;a href="https://echo.labstack.com/guide/customization#http-error-handler" rel="noopener noreferrer"&gt;HTTPErrorHandler&lt;/a&gt;&lt;/em&gt;.*&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We then register our routes and map a handler to them using the &lt;code&gt;routes&lt;/code&gt; package we imported. The routes are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;code&gt;GET /fetch-values&lt;/code&gt; - fetches the current prices of all the supported currencies
and returns a JSON response.&lt;/li&gt;
&lt;li&gt; &lt;code&gt;POST /btc-pref&lt;/code&gt; - stores the minimum and maximum price BTC has to exceed for a device before receiving a notification and returns a JSON response.&lt;/li&gt;
&lt;li&gt; &lt;code&gt;POST /eth-pref&lt;/code&gt; - stores the minimum and maximum price ETH has to exceed for a device before receiving a notification and returns a JSON response.&lt;/li&gt;
&lt;li&gt; &lt;code&gt;GET /simulate&lt;/code&gt; - simulates prices changes in the supported currencies.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After the routes, we start the server on port 9000.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;You can choose a different port if 9000 is in use, just remember to also change it in your *&lt;code&gt;MainActivity.kt&lt;/code&gt;*file.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that we have the &lt;code&gt;main.go&lt;/code&gt; file, let’s pull in all the imports the script needs. Open your terminal and run the following commands:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ go get github.com/labstack/echo
$ go get github.com/labstack/echo/middleware
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will pull in Echo package and the Echo Middleware package. For the other two packages, &lt;code&gt;database&lt;/code&gt; and &lt;code&gt;routes&lt;/code&gt;, we will create those manually. Let’s do that now.&lt;/p&gt;

&lt;h4&gt;
  
  
  Creating internal Go packages
&lt;/h4&gt;

&lt;p&gt;As mentioned earlier, we are going to create some internal packages to make the application a lot more modular so let’s start with the &lt;code&gt;database&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;database&lt;/code&gt; directory, create a new &lt;code&gt;init.go&lt;/code&gt; file and paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// File: ./database/init.go
package database

import (
    "database/sql"
    _ "github.com/mattn/go-sqlite3"
)

// Initialize initialises the database
func Initialize(filepath string) *sql.DB {
    db, err := sql.Open("sqlite3", filepath)
    if err != nil || db == nil {
        panic("Error connecting to database")
    }
    return db
}

// Migrate migrates the database
func Migrate(db *sql.DB) {
    sql := `
        CREATE TABLE IF NOT EXISTS devices(
                id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
                uuid VARCHAR NOT NULL,
                btc_min INTEGER,
                btc_max INTEGER,
                eth_min INTEGER,
                eth_max INTEGER
        );
    `
    _, err := db.Exec(sql)
    if err != nil {
        panic(err)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the file above, we first import two packages, the &lt;code&gt;database/sql&lt;/code&gt;, which is inbuilt, and the &lt;a href="http://github.com/mattn/go-sqlite3" rel="noopener noreferrer"&gt;mattn/go-sqlite3&lt;/a&gt;package, which is an sqlite3 driver for Go using &lt;code&gt;database/sql&lt;/code&gt;. To pull that in open the terminal and run the command below:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ go get github.com/mattn/go-sqlite3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Next, we created a function called &lt;code&gt;Initialize&lt;/code&gt; and in this function, we initialize our SQLite database. This will create a new database file if it does not exist, or use an existing one.&lt;/p&gt;

&lt;p&gt;We also have a &lt;code&gt;Migrate&lt;/code&gt; function where we specify the SQL query to be run when the application is initialized. As seen from the query, we create the table &lt;code&gt;devices&lt;/code&gt; only if it does not already exist.&lt;/p&gt;

&lt;p&gt;That’s all for the &lt;code&gt;init.go&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Create a new &lt;code&gt;routes.go&lt;/code&gt; file in the &lt;code&gt;routes&lt;/code&gt; directory and paste the following code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// File: ./routes/routes.go
package routes

import (
    "database/sql"
    "errors"
    "net/http"
    "strconv"
    "../database/model"
    "github.com/labstack/echo"
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now let’s start defining the route handlers as used in the &lt;code&gt;main.go&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;First, we will add the &lt;code&gt;GetPrices&lt;/code&gt; function. In the same file paste the following code at the bottom:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// GetPrices returns the coin prices
func GetPrices() echo.HandlerFunc {
    return func(c echo.Context) error {
        prices, err := model.GetCoinPrices(true)
        if err != nil {
            return c.JSON(http.StatusBadGateway, err)
        }
        return c.JSON(http.StatusOK, prices)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The function above is straightforward. We just get the prices from the &lt;code&gt;model.GetCoinPrices&lt;/code&gt; function and return them as a JSON response.&lt;/p&gt;

&lt;p&gt;Note that we passed a boolean to the &lt;code&gt;GetCoinPrices&lt;/code&gt; function. This boolean is to mark whether to simulate the prices or fetch from the API directly. Since we are testing, we want to simulate the prices so it changes often.&lt;/p&gt;

&lt;p&gt;The next function to add to the &lt;code&gt;routes.go&lt;/code&gt; file is the &lt;code&gt;SaveDeviceSettings&lt;/code&gt; function. In the same file, paste the following code to the bottom of the file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var postedSettings map[string]string

func formValue(c echo.Context, key string) (string, error) {
    if postedSettings == nil {
        if err := c.Bind(&amp;amp;postedSettings); err != nil {
            return "", err
        }
    }

    return postedSettings[key], nil
}

func getCoinValueFromRequest(key string, c echo.Context) (int64, error) {
    value, _ := formValue(c, key)

    if value != "" {
        setting, err := strconv.ParseInt(value, 10, 64)
        if err == nil {
            return setting, nil
        }
    }

    return 0, errors.New("Invalid or empty key for: " + key)
}

// SaveDeviceSettings saves the device settings
func SaveDeviceSettings(db *sql.DB) echo.HandlerFunc {
    return func(c echo.Context) error {
        uuid, _ := formValue(c, "uuid")        
        field := make(map[string]int64)

        if btcmin, err := getCoinValueFromRequest("minBTC", c); err == nil {
            field["btc_min"] = btcmin
        }

        if btcmax, err := getCoinValueFromRequest("maxBTC", c); err == nil {
            field["btc_max"] = btcmax
        }

        if ethmin, err := getCoinValueFromRequest("minETH", c); err == nil {
            field["eth_min"] = ethmin
        }

        if ethmax, err := getCoinValueFromRequest("maxETH", c); err == nil {
            field["eth_max"] = ethmax
        }

        defer func() { postedSettings = nil }()

        device, err := model.SaveSettings(db, uuid, field)
        if err != nil {
            return c.JSON(http.StatusBadRequest, err)
        }

        return c.JSON(http.StatusOK, device)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the code above, we have three functions. The first two are helper functions. We need them to get the posted form values from the request.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;SaveDeviceSettings&lt;/code&gt; function, we get the &lt;code&gt;uuid&lt;/code&gt; for the device, and conditionally get the minimum and maximum values for the coin. We save the values to the database using the &lt;code&gt;model.SaveSettings&lt;/code&gt; function and return a JSON response.&lt;/p&gt;

&lt;p&gt;The final function to add will be the &lt;code&gt;Simulate&lt;/code&gt; function. Add the following code to the bottom of the file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SimulatePriceChanges simulates the prices changes
func SimulatePriceChanges(db *sql.DB) echo.HandlerFunc {
    return func(c echo.Context) error {
        prices, err := model.GetCoinPrices(true)
        if err != nil {
            panic(err)
        }

        devices, err := model.NotifyDevicesOfPriceChange(db, prices)
        if err != nil {
            panic(err)
        }

        resp := map[string]interface{}{
            "prices":  prices,
            "devices": devices,
            "status":  "success",
        }

        return c.JSON(http.StatusOK, resp)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the function above, we fetch the prices for the coins, we then send that to the &lt;code&gt;model.NotifyDevicesOfPriceChange&lt;/code&gt; function, which finds devices with matching criteria and sends them a push notification. We then return a JSON response of the &lt;code&gt;prices&lt;/code&gt;, &lt;code&gt;devices&lt;/code&gt; and &lt;code&gt;status&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That’s all for the routes.&lt;/p&gt;

&lt;p&gt;Lastly, let’s define the model. Create a new &lt;code&gt;models.go&lt;/code&gt; file in the &lt;code&gt;database/model&lt;/code&gt; directory and paste the following code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// File: ./database/model/models.go
package model

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "math/big"
    "math/rand"
    "net/http"
    "time"
    "errors"
    "../../notification"
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Next, let’s define the structs for our object resources. In the same file, paste the following to the bottom:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// CoinPrice represents a single coin resource
type CoinPrice map[string]interface{}

// Device represents a single device resource
type Device struct {
    ID     int64  `json:"id"`
    UUID   string `json:"uuid"`
    BTCMin int64  `json:"btc_min"`
    BTCMax int64  `json:"btc_max"`
    ETHMin int64  `json:"eth_min"`
    ETHMax int64  `json:"eth_max"`
}

// Devices represents a collection of Devices
type Devices struct {
    Devices []Device `json:"items"`
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Above, we have the &lt;code&gt;CoinPrice&lt;/code&gt; map. This will be used to handle the response from the API we will be using for our application. When a response from the API is gotten, we bind it to the &lt;code&gt;CoinPrice&lt;/code&gt; map.&lt;/p&gt;

&lt;p&gt;The next one is the &lt;code&gt;Device&lt;/code&gt; struct. This represents the device resource. It matches the SQL schema of the table we created earlier in the article. When we want to create a new device resource to store in the database or retrieve one, we will use the &lt;code&gt;Device&lt;/code&gt; struct.&lt;/p&gt;

&lt;p&gt;Finally, we have the &lt;code&gt;Devices&lt;/code&gt; struct which is simply a collection of multiple &lt;code&gt;Device&lt;/code&gt; structs. We use this if we want to return a collection of &lt;code&gt;Device&lt;/code&gt;s.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Go does not allow underscores in the struct names, so we will use the *&lt;code&gt;json:&lt;/code&gt;`&lt;/em&gt;"&lt;em&gt;&lt;code&gt;key_name&lt;/code&gt;&lt;/em&gt;"` format to automatically convert to and from properties with the keys specified.*&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s start defining our model functions.&lt;/p&gt;

&lt;p&gt;In the same file, paste the following code to the bottom of the page:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// CreateSettings creates a new device and saves it to the db
func CreateSettings(db *sql.DB, uuid string) (Device, error) {
    device := Device{UUID: uuid, BTCMin: 0, BTCMax: 0, ETHMin: 0, ETHMax: 0}
    stmt, err := db.Prepare("INSERT INTO devices (uuid, btc_min, btc_max, eth_min, eth_max) VALUES (?, ?, ?, ?, ?)")
    if err != nil {
        return device, err
    }

    res, err := stmt.Exec(device.UUID, device.BTCMin, device.BTCMax, device.ETHMin, device.ETHMax)
    if err != nil {
        return device, err
    }

    lastID, err := res.LastInsertId()
    if err != nil {
        return device, err
    }

    device.ID = lastID
    return device, nil
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The function above is used to create settings for a new device. In the function, a new device is created using the &lt;code&gt;Device&lt;/code&gt; struct. We then write the SQL query we want to use to create a new device.&lt;/p&gt;

&lt;p&gt;We run &lt;code&gt;Exec&lt;/code&gt; on the SQL query to execute the query. If there’s no error, we get the last inserted ID from the query and assign that to the &lt;code&gt;Device&lt;/code&gt; struct we created earlier. We then return the created &lt;code&gt;Device&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s add the next function. In the same file, paste the following code to the bottom:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// GetSettings fetches the settings for a single user from the db
func GetSettings(db *sql.DB, uuid string) (Device, error) {
    device := Device{}
    if len(uuid) &amp;lt;= 0 {
        return device, errors.New("Invalid device UUID")
    }

    err := db.QueryRow("SELECT * FROM devices WHERE uuid=?", uuid).Scan(
        &amp;amp;device.ID,
        &amp;amp;device.UUID,
        &amp;amp;device.BTCMin,
        &amp;amp;device.BTCMax,
        &amp;amp;device.ETHMin,
        &amp;amp;device.ETHMax)
    if err != nil {
        return CreateSettings(db, uuid)
    }

    return device, nil
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the &lt;code&gt;GetSettings&lt;/code&gt; function above, we create an empty &lt;code&gt;Device&lt;/code&gt; struct. We run the query to fetch a device from the &lt;code&gt;devices&lt;/code&gt; table that matches the &lt;code&gt;uuid&lt;/code&gt;. We then use the &lt;code&gt;Scan&lt;/code&gt; method of the database package to save the row values to the &lt;code&gt;Device&lt;/code&gt; Instance.&lt;/p&gt;

&lt;p&gt;If no device is found, a new one is created using the &lt;code&gt;CreateSettings&lt;/code&gt; function we created earlier, else the device found is returned.&lt;/p&gt;

&lt;p&gt;Let’s add the next function. In the same file, paste the following code to the bottom:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SaveSettings saves the devices settings
func SaveSettings(db *sql.DB, uuid string, field map[string]int64) (Device, error) {
    device, err := GetSettings(db, uuid)
    if err != nil {
        return Device{}, err
    }

    if btcmin, isset := field["btc_min"]; isset {
        device.BTCMin = btcmin
    }

    if btcmax, isset := field["btc_max"]; isset {
        device.BTCMax = btcmax
    }

    if ethmin, isset := field["eth_min"]; isset {
        device.ETHMin = ethmin
    }

    if ethmax, isset := field["eth_max"]; isset {
        device.ETHMax = ethmax
    }

    stmt, err := db.Prepare("UPDATE devices SET btc_min = ?, btc_max = ?, eth_min = ?, eth_max = ? WHERE uuid = ?")
    if err != nil {
        return Device{}, err
    }

    _, err = stmt.Exec(device.BTCMin, device.BTCMax, device.ETHMin, device.ETHMax, device.UUID)
    if err != nil {
        return Device{}, err
    }
    return device, nil
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the &lt;code&gt;SaveSettings&lt;/code&gt; function above, we get the existing settings using the &lt;code&gt;GetSettings&lt;/code&gt; function and then we conditionally update the existing value. We then write an SQL query to update the database with the new values. After this, we return the &lt;code&gt;Device&lt;/code&gt; struct.&lt;/p&gt;

&lt;p&gt;Let’s add the next function. In the same file, paste the following code to the bottom:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// GetCoinPrices gets the current coin prices
func GetCoinPrices(simulate bool) (CoinPrice, error) {
    coinPrice := make(CoinPrice)
    currencies := [2]string{"ETH", "BTC"}

    for _, curr := range currencies {
        if simulate == true {
            min := 1000.0
            max := 15000.0
            price, _ := big.NewFloat(min + rand.Float64()*(max-min)).SetPrec(8).Float64()
            coinPrice[curr] = map[string]interface{}{"USD": price}
            continue
        }

        url := fmt.Sprintf("https://min-api.cryptocompare.com/data/pricehistorical?fsym=%s&amp;amp;tsyms=USD&amp;amp;ts=%d", curr, time.Now().Unix())
        res, err := http.Get(url)
        if err != nil {
            return coinPrice, err
        }

        defer res.Body.Close()

        body, err := ioutil.ReadAll(res.Body)
        if err != nil {
            return coinPrice, err
        }

        var f interface{}

        err = json.Unmarshal([]byte(body), &amp;amp;f)
        if err != nil {
            return coinPrice, err
        }

        priceMap := f.(map[string]interface{})[curr]

        for _, price := range priceMap.(map[string]interface{}) {
            coinPrice[curr] = map[string]interface{}{"USD": price.(float64)}
        }
    }
    return coinPrice, nil
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the function above, we create a new instance of &lt;code&gt;coinPrice&lt;/code&gt; and then we create an array of the two currencies we want to fetch, ETH and BTC. We then loop through the currencies and if &lt;code&gt;simulate&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;, we just return the simulated prices for the coins. If it’s &lt;code&gt;false&lt;/code&gt;, then for each of the currencies we do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetch the price for the currency from the API.&lt;/li&gt;
&lt;li&gt;Add the price of the currency to the &lt;code&gt;coinPrice&lt;/code&gt; map.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After we are done, we return the prices.&lt;/p&gt;

&lt;p&gt;The next and final function we want to add is the &lt;code&gt;NotifyDevicesOfPriceChange&lt;/code&gt; function. This is responsible for getting devices that match the minimum and maximum threshold and sending push notifications to them.&lt;/p&gt;

&lt;p&gt;In the same file, paste the following code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func minMaxQuery(curr string) string {
    return `(` + curr + `_min &amp;gt; 0 AND ` + curr + `_min &amp;gt; ?) OR (` + curr + `_max &amp;gt; 0 AND ` + curr + `_max &amp;lt; ?)`
}

// NotifyDevicesOfPriceChange returns the devices that are within the range
func NotifyDevicesOfPriceChange(db *sql.DB, prices CoinPrice) (Devices, error) {
    devices := Devices{}
    for currency, price := range prices {
        pricing := price.(map[string]interface{})
        rows, err := db.Query("SELECT * FROM devices WHERE "+minMaxQuery(currency), pricing["USD"], pricing["USD"])
        if err != nil {
            return devices, err
        }

        defer rows.Close()

        for rows.Next() {
            device := Device{}
            err = rows.Scan(&amp;amp;device.ID, &amp;amp;device.UUID, &amp;amp;device.BTCMin, &amp;amp;device.BTCMax, &amp;amp;device.ETHMin, &amp;amp;device.ETHMax)
            if err != nil {
                return devices, err
            }
            devices.Devices = append(devices.Devices, device)
            notification.SendNotification(currency, pricing["USD"].(float64), device.UUID)
        }
    }

    return devices, nil
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the code above we have two functions, the first is &lt;code&gt;minMaxQuery&lt;/code&gt; which is a helper function that helps us generate the SQL query for the min and max of a currency.&lt;/p&gt;

&lt;p&gt;The second function is the &lt;code&gt;NotifyDevicesOfPriceChange&lt;/code&gt; function. In here we loop through the currency prices and for each of the price we check the database for devices that match the minimum and maximum prices.&lt;/p&gt;

&lt;p&gt;When we have the devices, we loop through them and send a push notification using the &lt;code&gt;notification.SendNotification&lt;/code&gt; method. We then return the devices we sent the notification to.&lt;/p&gt;

&lt;p&gt;That’s all for the model package. We have one last package to add and that’s the &lt;code&gt;notification&lt;/code&gt; package. We used it in the code above to send push notification so let’s define it.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;notifications&lt;/code&gt; directory, create a &lt;code&gt;push.go&lt;/code&gt; file and paste the following code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// File: ./notification/push.go
package notification

import (
    "fmt"
    "strconv"
    "github.com/pusher/push-notifications-go"
)

const (
    instanceID = "PUSHER_BEAMS_INSTANCE_ID"
    secretKey  = "PUSHER_BEAMS_SECRET_KEY"
)

// SendNotification sends push notification to devices
func SendNotification(currency string, price float64, uuid string) error {
    notifications, err := pushnotifications.New(instanceID, secretKey)
    if err != nil {
        return err
    }

    publishRequest := map[string]interface{}{
        "fcm": map[string]interface{}{
            "notification": map[string]interface{}{
                "title": currency + " Price Change",
                "body":  fmt.Sprintf("The price of %s has changed to $%s", currency, strconv.FormatFloat(price, 'f', 2, 64)),
            },
        },
    }

    interest := fmt.Sprintf("%s_%s_changed", uuid, currency)

    _, err = notifications.Publish([]string{interest}, publishRequest)
    if err != nil {
        return err
    }

    return nil
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Replace the &lt;code&gt;PUSHER_BEAMS_*&lt;/code&gt; key with the credentials in your Pusher dashboard.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the code above, we have the &lt;code&gt;SendNotification&lt;/code&gt; function. In there we instantiate a new Pusher Beams instance using the &lt;code&gt;InstanceID&lt;/code&gt; and &lt;code&gt;secretKey&lt;/code&gt; defined above the function. &lt;/p&gt;

&lt;p&gt;We then create a &lt;code&gt;publishRequest&lt;/code&gt; variable which contains the Android notification payload. This payload is what we will send to the Pusher Beams backend and will contain everything needed to send the notification to the Android device.&lt;/p&gt;

&lt;p&gt;Next, we create an &lt;code&gt;interest&lt;/code&gt; variable which will be the interest we want to push the notification to. The format of the interest will match the one we subscribed to in part one of this tutorial. Next, we call the &lt;code&gt;Publish&lt;/code&gt; function of the Pusher Beams package to send the notification to the device.&lt;/p&gt;

&lt;p&gt;One final thing we need to do is pull the Pusher Beams package into our &lt;code&gt;$GOPATH&lt;/code&gt;. Open your terminal and run the following command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ go get github.com/pusher/push-notifications-go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;When the command has executed successfully, we can now run the application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running the application
&lt;/h3&gt;

&lt;p&gt;Now that we have finished building the application, we need to run both the backend and the Android application.&lt;/p&gt;

&lt;p&gt;Open your terminal and execute the following command from the root of the project to run the Go application:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ go run main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This should start the server on port 9000.&lt;/p&gt;

&lt;p&gt;Next, go to Android Studio and launch your Android project. At this point, you can now see the application. You can go ahead to set the minimum and maximum limits for both the BTC and ETH currency.&lt;/p&gt;

&lt;p&gt;Now minimize the application in your simulator and open the notification center. Visit the URL &lt;a href="http://localhost:9000/simulate" rel="noopener noreferrer"&gt;http://localhost:9000/simulate&lt;/a&gt; to simulate the currency changes. You should see the notifications come into the device as shown below:&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In this article, we have been able to see how you can create a cryptocurrency watcher application for Android using Pusher Beams and Go. This tutorial is available for iOS also &lt;a href="https://pusher.com/tutorials/cryptocurrency-tracking-swift-laravel-part-1" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The source code to the application built in this article is available on &lt;a href="https://github.com/neoighodaro/kotlin-cryptocurrency-watcher-with-push-notification" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post first appeared on the &lt;a href="https://pushcomer./tutorials/cryptocurrency-kotlin-go-part-2" rel="noopener noreferrer"&gt;Pusher Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cryptocurrency</category>
      <category>kotlin</category>
      <category>go</category>
      <category>pusher</category>
    </item>
    <item>
      <title>Build a cryptocurrency alert app using Kotlin and Go: Part 1 — The frontend</title>
      <dc:creator>Neo</dc:creator>
      <pubDate>Wed, 05 Sep 2018 06:25:21 +0000</pubDate>
      <link>https://dev.to/neo/build-a-cryptocurrency-alert-app-using-kotlin-and-go-part-1the-frontend-2h9d</link>
      <guid>https://dev.to/neo/build-a-cryptocurrency-alert-app-using-kotlin-and-go-part-1the-frontend-2h9d</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;You will need Android Studio 3+ and Go 1.10.2+ installed on your machine. You should have some familiarity with Android development and the Kotlin language.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cryptocurrency is one of the hot topics today and as a result of this, many people have purchased many cryptocurrencies. However, the market is unpredictable and changes very often, so people tend to keep an eye on the changes in the price of their asset.&lt;/p&gt;

&lt;p&gt;In this post, we will create an app that watches for changes in the value of cryptocurrencies in realtime and notifies the user when the changes occur. We will focus on two very popular cryptocurrencies — Bitcoin and Ethereum. When we are done, your phone will receive a push notification when the value of Bitcoin and Ethereum either exceeds or goes below a value you specify in the settings of the app.&lt;/p&gt;

&lt;p&gt;Here is a screen recording of what we will build:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnf0ml6zmsdzihfyqh0ov.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnf0ml6zmsdzihfyqh0ov.gif" width="1024" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;To follow along, you need the following installed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Android Studio installed on your machine (v3.x or later). &lt;a href="https://developer.android.com/studio/index.html" rel="noopener noreferrer"&gt;Download
here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Go version 1.10.2 or later &lt;a href="https://golang.org/doc/install#install" rel="noopener noreferrer"&gt;installed&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.sqlitetutorial.net/download-install-sqlite/" rel="noopener noreferrer"&gt;SQLite installed&lt;/a&gt; on
your machine.&lt;/li&gt;
&lt;li&gt;Basic knowledge on using the Android Studio IDE.&lt;/li&gt;
&lt;li&gt;Basic knowledge of Kotlin programming language. See the &lt;a href="https://kotlinlang.org/docs/reference/" rel="noopener noreferrer"&gt;official
docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Basic knowledge of Go and the &lt;a href="https://echo.labstack.com/" rel="noopener noreferrer"&gt;Echo framework&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Building our Android application
&lt;/h3&gt;

&lt;p&gt;First, launch Android Studio and create a new application. Enter the name of your application, for example, &lt;strong&gt;CryptoAlert&lt;/strong&gt; and then enter the package name.Make sure the &lt;strong&gt;Enable Kotlin Support&lt;/strong&gt; checkbox is selected. Choose the minimum SDK, click &lt;strong&gt;Next,&lt;/strong&gt; choose an &lt;strong&gt;Empty Activity&lt;/strong&gt; template, stick with the MainActivity naming scheme and then click &lt;strong&gt;Finish&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Creating a Pusher Beams application
&lt;/h4&gt;

&lt;p&gt;Since Pusher Beams relies on Firebase, we need an FCM key and a &lt;code&gt;google-services.json&lt;/code&gt; file. Go to your &lt;a href="https://console.firebase.google.com/u/0/" rel="noopener noreferrer"&gt;Firebase console&lt;/a&gt; and click the &lt;strong&gt;Add project&lt;/strong&gt; card to initialize the app creation wizard.&lt;/p&gt;

&lt;p&gt;Add the name of the project, for example, &lt;code&gt;crypto-&lt;/code&gt;&lt;code&gt;alert&lt;/code&gt;, read and accept the terms of conditions. After this, you will be directed to the project overview screen. Choose the &lt;strong&gt;Add Firebase to your Android app&lt;/strong&gt; option.&lt;/p&gt;

&lt;p&gt;The next screen will require the package name of your app. You can find your app’s package name in your app-module &lt;code&gt;build.gradle&lt;/code&gt; file. Look out for the &lt;code&gt;applicationId&lt;/code&gt; value. Enter the package name and click &lt;strong&gt;Next&lt;/strong&gt;. You will be prompted to download a &lt;code&gt;google-services.json&lt;/code&gt; file. Download the file and skip the rest of the process. Add the downloaded file to the app folder of your project - &lt;code&gt;name-of-project/app&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To get the FCM key, go to your project settings on Firebase, under the Cloud messaging tab you should see the server key.&lt;/p&gt;

&lt;p&gt;Next, login to the new &lt;a href="https://dash.pusher.com/" rel="noopener noreferrer"&gt;Pusher dashboard&lt;/a&gt;. You should sign up if you don’t have an account yet.&lt;/p&gt;

&lt;p&gt;Open your Pusher Beams dashboard and create a new Pusher Beams application.&lt;/p&gt;

&lt;p&gt;After creating your instance, you will be presented with a quick-start guide. Select the &lt;strong&gt;Android quick-start&lt;/strong&gt;. After you add the FCM key, you can exit the quick-start guide.&lt;/p&gt;

&lt;h4&gt;
  
  
  Adding functionalities to our application
&lt;/h4&gt;

&lt;p&gt;For our app to work, we need to pull in a couple of dependencies. To do this,&lt;br&gt;
add the following to the project &lt;code&gt;build-gradle&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// File: ./build.gradle
buildscript {
    // [...]
    dependencies {
        // [...]
        classpath 'com.google.gms:google-services:4.0.0'
    }
}
// [...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, add the following to the app module &lt;code&gt;build.gradle&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// File: ./app/build.gradle
dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.4.0'
    implementation 'com.squareup.retrofit2:converter-scalars:2.4.0'
    implementation 'com.google.firebase:firebase-messaging:17.1.0'
    implementation 'com.pusher:push-notifications-android:0.10.0'
    [...]
}
// Add this line to the end of the file
apply plugin: 'com.google.gms.google-services'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above we included &lt;a href="https://github.com/square/retrofit" rel="noopener noreferrer"&gt;Retrofit&lt;/a&gt; — a package for making network calls, and then the Pusher Beams package for sending push notifications. The additional Google services are dependencies for the Pusher Beams package. Sync your gradle files to make the libraries available for use.&lt;/p&gt;

&lt;p&gt;Next, create a new interface named &lt;code&gt;ApiService&lt;/code&gt; and paste the code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// File: ./app/src/main/java/{package-name}/ApiService.kt
import okhttp3.RequestBody
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST

interface ApiService {
    @POST("/btc-pref")
    fun saveBTCLimit(@Body body: RequestBody): Call&amp;lt;String&amp;gt;

    @POST("/eth-pref")
    fun saveETHLimit(@Body body: RequestBody): Call&amp;lt;String&amp;gt;

    @GET("/fetch-values")
    fun getValues():Call&amp;lt;String&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file is used to by Retrofit to know the endpoints to be accessed. The first endpoint &lt;code&gt;/btc-pref&lt;/code&gt; is used to set the Bitcoin limits. The next endpoint &lt;code&gt;/eth-pref&lt;/code&gt; is used to save the Ethereum limits. The last endpoint &lt;code&gt;/fetch-values&lt;/code&gt; is used to get the current values of the cryptocurrencies.&lt;/p&gt;

&lt;p&gt;To make use of network services in your application, add the internet permission in your &lt;code&gt;AndroidManifest.xml&lt;/code&gt;file like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.cryptoalat"&amp;gt;
    &amp;lt;uses-permission android:name="android.permission.INTERNET"/&amp;gt;

    [...]

&amp;lt;/manifest&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we will manage notifications in our app. Create a new service named&lt;br&gt;
&lt;code&gt;NotificationsMessagingService&lt;/code&gt;and paste this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// File: ./app/src/main/java/{package-name}/NotificationsMessagingService.kt
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Intent
import android.os.Build
import android.support.v4.app.NotificationCompat
import android.support.v4.app.NotificationManagerCompat
import com.google.firebase.messaging.RemoteMessage
import com.pusher.pushnotifications.fcm.MessagingService

class NotificationsMessagingService : MessagingService() {
    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        val notificationId = 10
        val channelId  = "crypto_channel"
        lateinit var channel: NotificationChannel
        val intent = Intent(this, MainActivity::class.java)
        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        val pendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
        val mBuilder = NotificationCompat.Builder(this, channelId)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle(remoteMessage.notification!!.title!!)
                .setContentText(remoteMessage.notification!!.body!!)
                .setContentIntent(pendingIntent)
                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                .setAutoCancel(true)

        if (Build.VERSION.SDK_INT &amp;gt;= Build.VERSION_CODES.O) {
            val notificationManager = applicationContext.getSystemService(NotificationManager::class.java)
            val name = getString(R.string.channel_name)
            val description = getString(R.string.channel_description)
            val importance = NotificationManager.IMPORTANCE_DEFAULT
            channel = NotificationChannel("crypto_channel", name, importance)
            channel.description = description
            notificationManager!!.createNotificationChannel(channel)
            notificationManager.notify(notificationId, mBuilder.build())
        } else {
            val notificationManager =  NotificationManagerCompat.from(this)
            notificationManager.notify(notificationId, mBuilder.build())
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because there are major changes to push notifications in Android O, we checked for the Android version before handling the notification. If we are using Android O or newer, we have to create a notification channel that will be used to categorize the type of notification we are sending. This is particularly useful for apps that send different types of notifications.&lt;/p&gt;

&lt;p&gt;We also made use of some files stored in the &lt;code&gt;strings.xml&lt;/code&gt; file to describe the notifications channel description and channel name. Add these to the &lt;code&gt;strings.xml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- File: /app/src/main/res/values/strings.xml --&amp;gt;
&amp;lt;string name="channel_name"&amp;gt;Crypto&amp;lt;/string&amp;gt;
&amp;lt;string name="channel_description"&amp;gt;To receive updates about changes in cryptocurrency value&amp;lt;/string&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register the service in the &lt;code&gt;AndroidManifest.xml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;application
    &amp;gt;
    [...]
    &amp;lt;service android:name=".NotificationsMessagingService"&amp;gt;
        &amp;lt;intent-filter android:priority="1"&amp;gt;
            &amp;lt;action android:name="com.google.firebase.MESSAGING_EVENT" /&amp;gt;
        &amp;lt;/intent-filter&amp;gt;
    &amp;lt;/service&amp;gt;
&amp;lt;/application&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let’s prepare our layouts. First, we will design the activity’s layout. When creating your app, the &lt;code&gt;activity_main.xml&lt;/code&gt; file should already be present in the layout folder. Open it and replace with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- File: ./app/src/main/res/layout/activity_main.xml --&amp;gt;
&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"&amp;gt;
    &amp;lt;TextView
        android:id="@+id/bitcoinValue"
        android:padding="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:text="1 BTC"
        android:textSize="16sp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" /&amp;gt;
    &amp;lt;TextView
        android:id="@+id/etherumValue"
        android:padding="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:layout_marginStart="8dp"
        android:text="1 ETH"
        android:textSize="16sp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/bitcoinValue"/&amp;gt;
&amp;lt;/android.support.constraint.ConstraintLayout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The layout contains two &lt;code&gt;TextView&lt;/code&gt;s to show prices for Bitcoin and Ethereum. We also made these &lt;code&gt;TextView&lt;/code&gt;s clickable so we can set limits to get notifications when the limits are surpassed.&lt;/p&gt;

&lt;p&gt;Next, we will design the layout of our alert dialog. Create a new layout file named &lt;code&gt;alert_layout&lt;/code&gt; and paste this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- File: ./app/src/main/res/layout/alert_layout.xml --&amp;gt;
&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:padding="20dp"
    android:layout_height="match_parent"&amp;gt;
    &amp;lt;EditText
        android:id="@+id/minimumValue"
        android:background="@drawable/text_background"
        android:hint="Minimum value"
        android:paddingStart="10dp"
        android:paddingEnd="10dp"
        android:inputType="number"
        android:layout_width="match_parent"
        android:layout_height="60dp" /&amp;gt;
    &amp;lt;EditText
        android:layout_marginTop="10dp"
        android:background="@drawable/text_background"
        android:hint="Maximum value"
        android:inputType="number"
        android:id="@+id/maximumValue"
        android:layout_width="match_parent"
        android:paddingStart="10dp"
        android:paddingEnd="10dp"
        android:layout_height="60dp" /&amp;gt;
    &amp;lt;Button
        android:id="@+id/save"
        android:layout_marginTop="10dp"
        android:layout_gravity="center"
        android:text="SAVE"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" /&amp;gt;
&amp;lt;/LinearLayout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will be the layout showed by the dialog. It contains two text fields and a &lt;strong&gt;save&lt;/strong&gt; button and we used a custom designed background for the &lt;code&gt;TextView&lt;/code&gt;s. Create a new drawable file named &lt;code&gt;text_background&lt;/code&gt; and paste this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- File: /app/src/main/res/drawable/text_background.xml --&amp;gt;
&amp;lt;shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" &amp;gt;
    &amp;lt;solid android:color="@android:color/white" /&amp;gt;
    &amp;lt;stroke android:width="1dip" android:color="@android:color/darker_gray"/&amp;gt;
&amp;lt;/shape&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will move to the &lt;code&gt;MainActivity&lt;/code&gt; to finish up our app’s functionalities. Open your &lt;code&gt;MainActivity&lt;/code&gt; and replace the contents with the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// File: ./app/src/main/java/{package-name}/MainActivity.Kt
import android.os.Bundle
import okhttp3.MediaType
import okhttp3.RequestBody
import org.json.JSONObject
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import android.support.v7.app.AlertDialog
import android.support.v7.app.AppCompatActivity
import android.util.Log
import android.view.LayoutInflater
import android.widget.Button
import android.widget.EditText
import com.pusher.pushnotifications.PushNotifications
import kotlinx.android.synthetic.main.activity_main.*
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.scalars.ScalarsConverterFactory

class MainActivity : AppCompatActivity() {

    private var prefs: Prefs? = null
    private val retrofit: ApiService by lazy {
        val httpClient = OkHttpClient.Builder()
        val builder = Retrofit.Builder()
                .baseUrl("http://10.0.2.2:9000/")
                .addConverterFactory(ScalarsConverterFactory.create())
        val retrofit = builder
                .client(httpClient.build())
                .build()

        retrofit.create(ApiService::class.java)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        fetchCurrentPrice()
        setupPushNotifications()
        setupClickListeners()
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;URL&lt;/code&gt; used above, &lt;code&gt;http://10.0.2.2:9000/&lt;/code&gt;, is the URL the Android emulator recognizes as localhost.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Above, we created a &lt;code&gt;retrofit&lt;/code&gt; object to be used for network calls. After setting up the &lt;code&gt;retrofit&lt;/code&gt; object, we add the layout in the &lt;code&gt;onCreate&lt;/code&gt; method and call three other functions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;fetchCurrentPrice&lt;/code&gt; - This function will get the current price of Bitcoin and Ethereum from our server. Create a new function within the class and set it up like so:&lt;/p&gt;

&lt;p&gt;// File: /app/src/main/java/{package-name}/MainActivity.kt&lt;br&gt;
private fun fetchCurrentPrice() {&lt;br&gt;
    retrofit.getValues().enqueue(object: Callback {&lt;br&gt;
        override fun onResponse(call: Call?, response: Response?) {&lt;br&gt;
            val jsonObject = JSONObject(response!!.body())&lt;br&gt;
            bitcoinValue.text = "1 BTC = $"+ jsonObject.getJSONObject("BTC").getString("USD")&lt;br&gt;
            etherumValue.text = "1 ETH = $"+ jsonObject.getJSONObject("ETH").getString("USD")&lt;br&gt;
        }&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    override fun onFailure(call: Call&amp;lt;String&amp;gt;?, t: Throwable?) {
        Log.e("MainActivity",t!!.localizedMessage)
    }
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Above, a network call is made to get the current Bitcoin and Ethereum prices in USD. When the response is received, we parse the JSON data and display it on the screen by setting the texts of the text views in the layout. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;setupPushNotifications&lt;/code&gt; - This function is where we start listening to the interest of our choice to receive notifications. The interest name is in this format &lt;strong&gt;{device_uuid}_{currency}_changed&lt;/strong&gt;. We register two interests, one for each currency. Open the &lt;code&gt;MainActivity&lt;/code&gt; class and add the following method:&lt;/p&gt;

&lt;p&gt;// File: /app/src/main/java/{package-name}/MainActivity.Kt&lt;br&gt;
private fun setupPushNotifications() {&lt;br&gt;
    PushNotifications.start(applicationContext, "PUSHER_BEAMS_INSTANCE_ID")&lt;br&gt;
    val fmt = "%s_%s_changed"&lt;br&gt;
    PushNotifications.subscribe(java.lang.String.format(fmt, deviceUuid(), "BTC"))&lt;br&gt;
    PushNotifications.subscribe(java.lang.String.format(fmt, deviceUuid(), "ETH"))&lt;br&gt;
}&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Replace &lt;code&gt;PUSHER_BEAMS_INSTANCE_ID&lt;/code&gt; with the instance ID found on your Pusher Beams dashboard.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;setupClickListeners&lt;/code&gt; - In this function, we will set up click listeners to the text views in our layout. In the same &lt;code&gt;MainActivity&lt;/code&gt; class, add the following method:&lt;/p&gt;

&lt;p&gt;// File: /app/src/main/java/{package-name}/MainActivity.Kt&lt;br&gt;
private fun setupClickListeners() {&lt;br&gt;
    bitcoinValue.setOnClickListener {&lt;br&gt;
        createDialog("BTC")&lt;br&gt;
    }&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;etherumValue.setOnClickListener {
    createDialog("ETH")
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When any of the text views is clicked, we call the &lt;code&gt;createDialog&lt;/code&gt; method which then opens up a layout for the user to input the limit.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;MainActivity&lt;/code&gt; class, add the method &lt;code&gt;createDialog&lt;/code&gt; and as seen below:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// File: /app/src/main/java/{package-name}/MainActivity.Kt
private fun createDialog(source:String){
  val builder: AlertDialog.Builder = AlertDialog.Builder(this)
  val view = LayoutInflater.from(this).inflate(R.layout.alert_layout,null)

  builder.setTitle("Set limits")
      .setMessage("")
      .setView(view)

  val dialog = builder.create()
  val minEditText: EditText = view.findViewById(R.id.minimumValue)
  val maxEditText: EditText = view.findViewById(R.id.maximumValue)

  view.findViewById&amp;lt;Button&amp;gt;(R.id.save).setOnClickListener {
    if (source == "BTC"){
      saveBTCPref(minEditText.text.toString(), maxEditText.text.toString())
    } else {
      saveETHPref(minEditText.text.toString(), maxEditText.text.toString())
    }

    dialog.dismiss()
  }
  dialog.show()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This dialog gets the minimum and maximum values and sends it to the backend server. This is done so that when the cryptocurrency’s price changes, we’ll get a push notification if it is within the limits set. &lt;/p&gt;

&lt;p&gt;In the function above, we called two new methods. Add the two methods in the &lt;code&gt;MainActivity&lt;/code&gt; class as seen below:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// File: /app/src/main/java/{package-name}/MainActivity.Kt
private fun saveBTCPref(min:String, max:String){
    val jsonObject = JSONObject()
    jsonObject.put("minBTC", min)
    jsonObject.put("maxBTC", max)
    jsonObject.put("uuid", deviceUuid())

    val body = RequestBody.create(
            MediaType.parse("application/json"),
            jsonObject.toString()
    )

    retrofit.saveBTCLimit(body).enqueue(object: Callback&amp;lt;String&amp;gt; {
        override fun onResponse(call: Call&amp;lt;String&amp;gt;?, response: Response&amp;lt;String&amp;gt;?) {}
        override fun onFailure(call: Call&amp;lt;String&amp;gt;?, t: Throwable?) {}
    })
}

private fun saveETHPref(min:String, max:String){
    val jsonObject = JSONObject()
    jsonObject.put("minETH",min)
    jsonObject.put("maxETH",max)
    jsonObject.put("uuid", deviceUuid())

    val body = RequestBody.create(
            MediaType.parse("application/json"),
            jsonObject.toString()
    )

    retrofit.saveETHLimit(body).enqueue(object: Callback&amp;lt;String&amp;gt; {
        override fun onResponse(call: Call&amp;lt;String&amp;gt;?, response: Response&amp;lt;String&amp;gt;?) {}
        override fun onFailure(call: Call&amp;lt;String&amp;gt;?, t: Throwable?) {}
    })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the &lt;code&gt;saveBTCPref&lt;/code&gt; and &lt;code&gt;saveETHPref&lt;/code&gt; we attempt to send the limits set by the user to the API so it can be saved for that user.&lt;/p&gt;

&lt;p&gt;While sending, we also send the &lt;code&gt;uuid&lt;/code&gt; which is the devices’ unique identifier. Let’s create the &lt;code&gt;deviceUuid()&lt;/code&gt;method that will generate and save this UUID per device. In the &lt;code&gt;MainActivity&lt;/code&gt; class, add the following code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// File: /app/src/main/java/{package-name}/MainActivity.Kt
private fun deviceUuid() : String {
    prefs = Prefs(this)
    var uuid: String = prefs!!.deviceUuid

    if (uuid == "") {
        uuid = java.util.UUID.randomUUID().toString().replace("-", "_")
        prefs!!.deviceUuid = uuid
    }

    return uuid
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now in this function, we reference a &lt;code&gt;Prefs&lt;/code&gt; class. Create a new &lt;code&gt;Prefs&lt;/code&gt; class and paste the following code into it:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// File: /app/src/main/java/{package-name}/Prefs.Kt
import android.content.Context
import android.content.SharedPreferences

class Prefs (context: Context) {
    val PREFS_FILENAME = "com.example.coinalert.prefs"
    val DEVICE_UUID = "device_uuid"
    val prefs: SharedPreferences = context.getSharedPreferences(PREFS_FILENAME, 0);
    var deviceUuid: String
        get() = prefs.getString(DEVICE_UUID, "")
        set(value) = prefs.edit().putString(DEVICE_UUID, value).apply()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That’s all for the application. At this point, the application should build successfully, but, not function as intended. In &lt;a href="https://pusher.com/tutorials/cryptocurrency-kotlin-go-part-2" rel="noopener noreferrer"&gt;the next part&lt;/a&gt;, we will build the backend of the application so it can work as intended.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In this article, we have learned how to use Pusher Beams to notify users of changes to a cryptocurrency. You can find the repository for the application built in this article &lt;a href="https://github.com/neoighodaro/kotlin-cryptocurrency-watcher-with-push-notification" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post first appeared on the &lt;a href="https://pusher.com/tutorials/cryptocurrency-kotlin-go-part-1" rel="noopener noreferrer"&gt;Pusher Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cryptocurrency</category>
      <category>kotlin</category>
      <category>go</category>
      <category>pusher</category>
    </item>
  </channel>
</rss>
