<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Scaledrone Blog]]></title><description><![CDATA[Realtime APIs, built for developers like you.]]></description><link>https://www.scaledrone.com/blog/</link><image><url>https://www.scaledrone.com/blog/favicon.png</url><title>Scaledrone Blog</title><link>https://www.scaledrone.com/blog/</link></image><generator>Ghost 1.24</generator><lastBuildDate>Fri, 24 Apr 2026 17:46:44 GMT</lastBuildDate><atom:link href="https://www.scaledrone.com/blog/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[How to Price Your API: A Guide for Developer Tool Founders]]></title><description><![CDATA[A practical guide to API pricing for developer tool founders. Covers value metrics, free tier design, packaging models, rate limits, and the mistakes that kill API revenue.]]></description><link>https://www.scaledrone.com/blog/how-to-price-your-api/</link><guid isPermaLink="false">69d0fd3c1f45d82ebb05b94f</guid><dc:creator><![CDATA[Scaledrone]]></dc:creator><pubDate>Sat, 04 Apr 2026 17:44:56 GMT</pubDate><media:content url="https://www.scaledrone.com/blog/content/images/2026/04/pexels-cottonbro-6804594.jpg" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://www.scaledrone.com/blog/content/images/2026/04/pexels-cottonbro-6804594.jpg" alt="How to Price Your API: A Guide for Developer Tool Founders"><p>APIs are infrastructure. They get embedded into other people's code, called millions of times a day, and judged on latency before anyone ever reads your marketing page. Pricing them like traditional SaaS (pick a seat count, charge monthly, call it a day) will get you in trouble fast.</p>
<p>The economics are different. Traditional SaaS companies enjoy 80-90% gross margins because serving one more user costs almost nothing. APIs, especially ones that touch AI inference, realtime connections, or heavy compute, carry real variable costs. Every call burns CPU cycles, memory, and bandwidth. AI-native APIs often operate at 50-60% margins. If your pricing doesn't reflect that reality, growth becomes a problem you can't afford.</p>
<p>I've spent years working on pricing for SaaS and developer tools, and the pattern I see most often is founders treating their API pricing page as an afterthought. They ship the product, copy a competitor's pricing table, and figure they'll &quot;optimize later.&quot; Later usually arrives as a margin crisis or a churn spike.</p>
<p>This guide covers the decisions that matter most: choosing a value metric, designing a free tier, picking a packaging model, using rate limits strategically, and avoiding the mistakes that quietly drain revenue.</p>
<h2 id="chooseavaluemetricthatscaleswithyourcustomer">Choose a Value Metric That Scales With Your Customer</h2>
<p>The value metric is the unit your customer pays for. It's the single most important decision in your pricing architecture, because it determines whether your revenue grows naturally as your customers succeed, or stalls while they scale.</p>
<p>A good value metric satisfies three conditions. It's easy for the developer to understand and predict. It aligns with the value they actually get from your product. And it correlates with your cost to serve them.</p>
<p>The industry has largely moved from charging for &quot;access&quot; to charging for &quot;consumption.&quot; But consumption means different things depending on what your API does.</p>
<h3 id="commonapivaluemetricsandwhentouseeach">Common API value metrics and when to use each</h3>
<p><strong>Concurrent connections</strong> work best for realtime infrastructure: WebSockets, IoT, pub/sub. You're billing based on how many devices or users are simultaneously connected. The cost driver here is server memory, since each persistent connection holds open a socket.</p>
<p><strong>Events or messages</strong> fit transactional workloads: email delivery, SMS, chat messages. You charge per discrete payload sent or received. Postmark charges per email. Twilio charges per SMS. The math is simple and the developer can forecast costs accurately.</p>
<p><strong>Tokens</strong> are the standard for LLM and generative AI APIs. Because GPU utilization varies wildly based on prompt length and model size, time-based or seat-based pricing is economically unviable. OpenAI differentiates between input and output tokens, and offers steep discounts for cached inputs (up to 90% off) to incentivize developers to optimize their prompts. The pricing model itself becomes a behavioral lever, rewarding efficient architecture.</p>
<p><strong>Compute time</strong> applies to serverless functions and edge computing. Cloudflare Workers and AWS Lambda bill per millisecond of CPU execution, usually combined with a per-request fee. Generous free tiers are standard here.</p>
<p><strong>API requests</strong> (raw) work for search, geocoding, and data enrichment. Algolia and Google Maps charge fractions of a cent per HTTP request. Simple, but it can penalize developers whose workflows require multiple calls to complete a single action.</p>
<p><strong>Credits</strong> act as an abstraction layer over complex or variable compute costs. The developer buys a block of credits, and different operations burn them down at different rates. A simple query might cost 1 credit while a multi-step AI reasoning task costs 50. Credits work well when your backend costs vary too much to expose raw pricing per operation, but they require a clear exchange rate so developers can forecast spend.</p>
<p><strong>Outcomes</strong> represent the frontier. Instead of charging for resources consumed, you charge for business results achieved. Intercom moved from per-seat pricing to charging $0.99 per AI-resolved support conversation. Sierra charges only when their AI agent completes a specific valuable action like saving a membership or closing an e-commerce purchase. Outcome-based pricing shifts risk to the vendor, but it increases willingness to pay because the buyer only pays when they get value.</p>
<h3 id="howrealtimeapishandledualmetricpricing">How realtime APIs handle dual-metric pricing</h3>
<p>Realtime APIs face a particular challenge: they have two distinct cost drivers that scale independently. Persistent connections consume server memory. High-frequency messages consume CPU and bandwidth. A small number of users sending millions of messages can exhaust your bandwidth, while millions of idle connections can exhaust your memory. Either one can blow up your infrastructure costs.</p>
<p>Some providers solve this with a dual-metric approach, pricing on both concurrent connections and events per day across tiered plans. For example, a Growth tier might cap at 10,000 concurrent connections and 20 million events per day. This dual constraint protects against both abuse vectors simultaneously.</p>
<p>Others have moved toward pure consumption models. Ably recognized that peak connection pricing created &quot;wastage&quot; for developers (they paid for capacity they weren't using during off-peak hours) and shifted to billing per million connection-minutes and per million messages. The cost scales elastically with actual load.</p>
<p>The tradeoff is simplicity versus accuracy. Dual metrics are harder to explain on a pricing page but reflect real infrastructure costs more honestly. Single metrics are easier to sell but may leave money on the table or create abuse vectors.</p>
<h2 id="designafreetierthatactuallyconverts">Design a Free Tier That Actually Converts</h2>
<p>For developer tools, the free tier is the primary distribution channel. Developers evaluate an API by writing code against it. They need to test latency, read the docs, explore edge cases, and integrate it into a staging environment before they'll consider paying. If your API sits behind a &quot;Contact Sales&quot; wall or a credit card requirement, you're cutting off bottom-up adoption at the root.</p>
<p>But free tiers are expensive. Compute costs money. The question is whether your free tier functions as a customer acquisition engine or an infrastructure drain.</p>
<h3 id="whytimeboundtrialsfailforapis">Why time-bound trials fail for APIs</h3>
<p>Time-bound free trials (14 or 30 days of full access) are standard in B2B SaaS, but they consistently underperform for developer APIs. The reason is workflow mismatch. A developer implements your API in a local environment, tests it, and then leaves it idle for weeks while working on other parts of their application. By the time they're ready to push to production, the trial has expired.</p>
<p>Perpetual freemium models bounded by usage constraints convert significantly better. Benchmark data shows the median free-to-paid conversion rate for B2B SaaS is around 8%, but top-quartile developer tools with optimized freemium models achieve 15-28%. The mechanism is straightforward: the API becomes embedded in the codebase. Once the developer hits the usage limit, upgrading is cheaper than switching.</p>
<h3 id="structuringlimitstopreventabusewithoutpunishingbuilders">Structuring limits to prevent abuse without punishing builders</h3>
<p>The free tier needs to be generous enough for full end-to-end prototyping, but restrictive enough that any application generating real business value has to upgrade.</p>
<p>Supabase has an elegant approach: free projects auto-pause after 7 days of inactivity. This automatically purges abandoned projects, tutorial follow-alongs, and dead side-projects from active server memory. The developer who's actually building something won't notice, because their project stays active. If they want the database permanently awake, they upgrade to the $25/month Pro tier.</p>
<p>On the other end, watch for the burner-account anti-pattern. If developers are creating multiple accounts with different email addresses to dodge your free limits, one of two things is true: your starter tier is too expensive for the value it delivers, or your limits are too easy to manipulate. The fix usually isn't device fingerprinting. It's adjusting the economics so the pain of maintaining multiple accounts outweighs the cost of just upgrading.</p>
<h2 id="pickyourpackagingmodelpayasyougotiersorhybrid">Pick Your Packaging Model: Pay-as-You-Go, Tiers, or Hybrid</h2>
<p>The API industry is split on packaging. Pure usage-based pricing minimizes onboarding friction but creates anxiety. Tiered pricing creates predictability but introduces pricing cliffs. The data from 2025-2026 points toward a hybrid model as the most effective approach for developer tools.</p>
<h3 id="purepayasyougo">Pure pay-as-you-go</h3>
<p>In a pure PAYG model, there's no base fee. The developer pays only for what they consume. Twilio is the textbook example, charging $0.0083 per outbound SMS. Firebase's Blaze plan works similarly: a small free allowance, then granular per-operation charges.</p>
<p>The upside is frictionless onboarding. Initial cost is zero, which perfectly matches the budget of an early-stage startup. The downside is bill shock. Developers rationally fear that a coding bug, an infinite retry loop, or a bot attack will generate an enormous overnight invoice. And for the provider, revenue forecasting becomes difficult when income fluctuates with end-user engagement and macroeconomic trends.</p>
<h3 id="tieredsubscriptions">Tiered subscriptions</h3>
<p>Tiered models bundle a set allowance of usage into a fixed monthly price. Supabase's $25/month Pro tier includes 8GB of database space, 100GB of file storage, and 100,000 monthly active users, with usage-based overages beyond that.</p>
<p>Tiers function as a psychological insurance policy. The developer knows their baseline cost. Enterprise finance teams can allocate budget. The risk for the provider is breakage in both directions: if a developer only uses $5 of infrastructure on a $25 plan, you enjoy a high margin but they may feel overcharged. If a heavy user blows past the included limits without overage enforcement, your margins evaporate.</p>
<h3 id="thehybridmodelandwhymostsuccessfulapislandhere">The hybrid model (and why most successful APIs land here)</h3>
<p>The current consensus among leading developer tools is the hybrid model: tiered base subscriptions to capture platform fees, fund support, and guarantee minimum recurring revenue, paired with metered overages to capture the upside of hyperscale usage.</p>
<p>Stripe's billing infrastructure evolved specifically to support this. With their Meters API and the Metronome acquisition, platforms can charge a flat platform fee while aggregating real-time usage events for dynamic overages. This lets API providers run complex multi-dimensional pricing (charging for compute time, bandwidth, and API calls simultaneously) without building a proprietary billing engine from scratch.</p>
<p>The hybrid model gives CFOs predictability and gives infrastructure providers scalability. For most developer tool founders, it's the right starting point.</p>
<h2 id="useratelimitsasapricinglever">Use Rate Limits as a Pricing Lever</h2>
<p>Rate limits exist to protect your servers from DDoS attacks and ensure stability. They're also one of the most underused pricing levers in developer tools.</p>
<p>By mapping specific rate limits to pricing tiers, you force high-traffic users to upgrade for throughput, not just for total monthly volume. Cloudflare enforces 1,200 requests per 5 minutes for basic tokens but allocates much higher burst limits for enterprise plans. GitHub caps standard OAuth apps at 5,000 requests per hour but extends that to 15,000 for Enterprise Cloud organizations.</p>
<p>When developers hit a rate limit, the API returns an HTTP 429 &quot;Too Many Requests&quot; status code. Best practice is to include a Retry-After header in the response. But some providers turn even this into a pricing differentiator: free users get a generic 429 error, while paid users get precise headers showing exactly when they can resume queries. Operational transparency becomes a feature worth paying for.</p>
<p>The broader principle: your pricing page shows the monetary cost. Your rate limits, quotas, and overage handling define the actual developer experience. Both need to be designed together.</p>
<h2 id="avoidthefivemistakesthatkillapirevenue">Avoid the Five Mistakes That Kill API Revenue</h2>
<h3 id="chargingflatratesforvariableworkloads">Charging flat rates for variable workloads</h3>
<p>A simple GET request to retrieve a cached user profile takes milliseconds. A complex POST request triggering database writes, external webhooks, or multi-step AI inference takes orders of magnitude more compute. Charging the same flat subscription regardless of usage means heavy users subsidize light users, and your margins decay as the platform scales. Granular pricing per method (or at minimum, per operation class) solves this.</p>
<h3 id="billingforinfrastructureinsteadofvaluedelivered">Billing for infrastructure instead of value delivered</h3>
<p>If your API requires three calls to authenticate, query, and confirm an action, charging for all three frustrates developers. They feel like they're paying for your architectural overhead. The value metric should track the successful completion of whatever the developer is actually trying to do. Charge for the outcome, not the plumbing.</p>
<h3 id="hidingpricingbehindcontactsales">Hiding pricing behind &quot;Contact Sales&quot;</h3>
<p>Developers want to read the docs, look at the pricing page, and start building. If they need to schedule a sales call to discover the base price, they'll pivot to an open-source alternative or a competitor with transparent pricing. Supabase publishes exact database sizes, compute allowances, and fractional overage costs per gigabyte. They also explain why their pricing works the way it does, which builds trust with engineering buyers who are used to being sold to by enterprise salespeople.</p>
<p>Transparency is a feature. In many cases, it's the feature that wins the deal.</p>
<h3 id="shippingwithoutusagedashboardsorcostpreviews">Shipping without usage dashboards or cost previews</h3>
<p>Developers hate surprise bills. If you charge on usage but don't provide a real-time dashboard tracking that usage, trust erodes fast. Cost previews (showing the estimated cost of an operation before it executes) are becoming standard, especially for AI APIs where a single prompt can vary 100x in cost depending on the context window. Give developers full visibility into what they're spending, before the invoice arrives.</p>
<h3 id="treatingdocumentationasanafterthought">Treating documentation as an afterthought</h3>
<p>When documentation lacks clear error codes, authentication guides, and copy-paste code snippets in popular languages, developers flood your support queues. Every support ticket raises your cost of customer acquisition. Strategic documentation is a financial optimization: it lowers the barrier to the first successful API call and accelerates time-to-revenue. Good docs can cut onboarding from weeks to hours.</p>
<h2 id="thepricingtrendsworthwatching">The Pricing Trends Worth Watching</h2>
<p>Three shifts are worth paying attention to as API pricing continues to evolve.</p>
<p><strong>Credit-based pricing</strong> is surging, especially for AI-powered tools. When your backend costs vary wildly based on prompt complexity and context window size, exposing raw per-token or per-compute-second pricing can terrify developers. Credits abstract that complexity into a digestible unit. A simple query costs 1 credit, a complex reasoning task costs 50. The key is pairing credits with real-time usage dashboards and spending caps so developers maintain control.</p>
<p><strong>Outcome-based pricing</strong> is the logical endpoint of value alignment. Intercom abandoned per-seat pricing for their Fin AI agent and switched to $0.99 per AI-resolved conversation. They saw 40% higher adoption while maintaining healthy margins. Sierra followed a similar path, charging only when their AI agents achieve specific business outcomes. This shifts risk to the vendor, but it also removes the biggest objection from the buyer: &quot;what if it doesn't work?&quot;</p>
<p><strong>Machine-to-machine micropayments</strong> are emerging as AI agents become primary API consumers. Human-in-the-loop billing (signups, credit cards, monthly invoices) creates friction when the &quot;customer&quot; is an autonomous agent making thousands of calls per minute. Protocols using the HTTP 402 &quot;Payment Required&quot; status code are being developed to enable programmatic, sub-cent transactions. This could unlock monetization for high-frequency, low-value API calls that are currently unprofitable to process through traditional payment rails.</p>
<h2 id="conclusion">Conclusion</h2>
<p>How you price your API says something about how you view your developers. Transparent tiers, generous free plans, and clear usage dashboards tell developers you see them as engineering partners. That signal matters more than most founders realize.</p>
<p>In a world where AI-assisted migrations are steadily lowering switching costs, developer trust and pricing transparency are the moat. Get your value metric right, give builders room to experiment before they pay, and make sure your pricing scales honestly with the value you deliver.</p>
<p>If you're a founder working through these decisions and want structured help, <a href="https://potio.cc">Potio</a> specializes in pricing strategy for SaaS and developer tools. And if you're evaluating whether to bring in outside expertise at all, I wrote a separate piece on <a href="https://www.linkedin.com/pulse/how-choose-pricing-consultant-your-company-serge-herk%C3%BCl-5ilsf/?trackingId=APoJD5KcitYy%2BbHzT7E8Kg%3D%3D">how to choose a pricing consultant for your company</a> that covers what to look for and what to avoid.</p>
<h2 id="faq">FAQ</h2>
<h3 id="whatsthebestvaluemetricforanaiapi">What's the best value metric for an AI API?</h3>
<p>Tokens are the current standard because GPU costs vary so much based on model size and prompt length. Differentiate between input and output tokens, and consider discounts for cached inputs to incentivize efficient usage patterns. If your AI product delivers clearly measurable outcomes (like resolved support tickets), outcome-based pricing may outperform raw token billing.</p>
<h3 id="shouldiofferafreetierorafreetrialformyapi">Should I offer a free tier or a free trial for my API?</h3>
<p>A perpetual free tier with usage limits almost always outperforms a time-bound trial for developer APIs. Developers need to prototype, go idle, and come back weeks later. A 14-day trial expires before their app hits production. Usage-bounded freemium gives them room to build and creates natural upgrade pressure once the application generates real traffic.</p>
<h3 id="howdoipreventbillshockformyapicustomers">How do I prevent bill shock for my API customers?</h3>
<p>Three mechanisms: real-time usage dashboards so developers can see their consumption before the invoice, configurable spending alerts at 80% and 100% of their tier, and developer-controlled hard caps that automatically stop usage at a defined budget threshold. The goal is to give the developer control over their own risk exposure.</p>
<h3 id="whenshouldiusecreditbasedpricinginsteadofrawusagemetrics">When should I use credit-based pricing instead of raw usage metrics?</h3>
<p>Credits work best when your backend costs vary significantly across different operations and exposing raw per-unit prices would confuse developers or create anxiety. They abstract variable compute costs into a single, predictable currency. The tradeoff is transparency: you need a clear credit-to-operation mapping so developers can forecast their spend. If your API does one thing at a consistent cost, raw usage pricing is simpler and more trustworthy.</p>
<h3 id="howdoiknowifmyfreetieristoogenerous">How do I know if my free tier is too generous?</h3>
<p>Two signals. First, if your free-to-paid conversion rate is well below 8% (the B2B SaaS median), your free tier might be providing enough capacity for lightweight production use. Second, if you see developers creating multiple accounts to dodge limits, either your paid tier is too expensive for the value or your free limits are easy to game. Track how many free-tier users approach their limits and what percentage upgrade versus churn. That ratio tells you whether your free tier is doing its job.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Vue.js Real-time Chat Tutorial]]></title><description><![CDATA[In this step-by-step tutorial you'll build a real-time chat app entirely on the client side, utilizing Vue.js and Scaledrone for real-time communication.]]></description><link>https://www.scaledrone.com/blog/vue-real-time-chat-tutorial/</link><guid isPermaLink="false">6516e4d11f45d82ebb05b935</guid><category><![CDATA[Tutorials]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[vue.js]]></category><category><![CDATA[Chat]]></category><dc:creator><![CDATA[Marin Bencevic]]></dc:creator><pubDate>Thu, 12 Oct 2023 18:03:37 GMT</pubDate><media:content url="https://www.scaledrone.com/blog/content/images/2023/10/vue-chat-app.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><blockquote>
<img src="https://www.scaledrone.com/blog/content/images/2023/10/vue-chat-app.png" alt="Vue.js Real-time Chat Tutorial"><p>Check out the <a href="https://scaledrone.github.io/vue-chat-tutorial/"><strong>live example</strong></a> 🚀. As well as the full <a href="https://github.com/ScaleDrone/vue-chat-tutorial">source code on GitHub</a> 👩🏽‍💻.</p>
</blockquote>
<p>In the world of web apps, adding chat features used to be a nice extra, but now it's a must-have. Think about having an online store, a social network, or a tool for working together without the option to chat in real time. It's like having a phone without being able to make calls - it just doesn't work.</p>
<p>Imagine this: a user is using your web app and wants to talk to a friend. With chat, they don't have to leave the app to chat in real time; they can do it right within the app.</p>
<p>In this tutorial, we'll show you how to do this. We'll build a chat app using Vue.js and a tool called <a href="https://www.scaledrone.com/">Scaledrone</a>, which makes real-time chatting really easy. The app will work like Messenger, WhatsApp, or Telegram.</p>
<p>We'll use the modern Vue.js 3 web app framework and follow the best practices, like the <strong>Composition API</strong> and <strong>Single-File Components</strong>.</p>
<p>By the end, this is what our chat app will look like:</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/09/react-chat-app-scaledrone.png" alt="Vue.js Real-time Chat Tutorial"></p>
<p>And here's the cool part: the users of your Vue chat app will be able to chat with mobile users if you've already built an app using our <a href="https://www.scaledrone.com/blog/ios-chat-tutorial/">iOS</a> and <a href="https://www.scaledrone.com/blog/android-chat-tutorial/">Android</a> chat tutorials.</p>
<p>Let's get started!</p>
<h2 id="settingupavuejschatapp">Setting Up a Vue.js Chat App</h2>
<p>To focus on the meat of creating a Vue.js chat app, we took care of some of the scaffolding for you. You can begin by cloning the starter branch of <a href="https://github.com/ScaleDrone/vue-chat-tutorial/tree/starter">this tutotorial's GitHub repository</a>.</p>
<pre><code class="language-sh">git clone -b starter https://github.com/ScaleDrone/vue-chat-tutorial.git
</code></pre>
<p>Aside from a couple of empty folders and an empty Vue.js web app, this project also contains some CSS to make your chat app look nicer.</p>
<p>Next, build Vue.js 3 and its dependencies using the Node package manager:</p>
<pre><code class="language-sh">npm install
</code></pre>
<blockquote>
<p><strong>Note</strong>: Follow the instructions to <a href="https://nodejs.org/en/">install npm and Node.js</a> if you don't have them on your machine.</p>
</blockquote>
<p>Finally, you can run the app:</p>
<pre><code class="language-sh">npm run dev
</code></pre>
<p>The command will run your app on <a href="http://localhost:5174">localhost:5174</a>, although if you visit the site it will be wholly unimpressive — a blank screen. Don't worry, you'll see your chat app come alive in no time!</p>
<h2 id="showingchatmessagesusingvuecomponents">Showing Chat Messages Using Vue Components</h2>
<p>Your chat app with consist of three main parts:</p>
<ol>
<li><code>App.vue</code> which will take care of the main business logic of fetching chat messages and managing users as well as connect all of the UI together.</li>
<li>A <code>Messages</code> component that will show a list of messages.</li>
<li>An <code>Input</code> component with a text field and button so that we can send our messages.</li>
</ol>
<p><code>App.vue</code> is already provided for you in the <strong>src</strong> folder, and we'll modify it later. But first, we'll create a <code>Messages</code> component.</p>
<p>Create a new file in your <strong>src/components</strong> folder called <strong>Messages.vue</strong>. Create a new component in this file called <code>Messages</code> like in the following code:</p>
<pre><code class="language-javascript">&lt;script setup&gt;
import { onUpdated, ref } from 'vue';
import Message from './Message.vue';

const props = defineProps({
  messages: Array,
  me: Object,
});

const bottomRef = ref(null);

onUpdated(() =&gt; {
  bottomRef.value.scrollIntoView({ behavior: 'smooth' });
});
&lt;/script&gt;
</code></pre>
<p>The component will receive the messages it should display as a prop from <code>App</code>. We'll render a list containing each message. The list items themselves will contain the sender's name and avatar, as well as the text of the message.</p>
<p>Looking at the code, you might be wondering what <code>bottomRef</code> and <code>onUpdated</code> do.</p>
<p>When a new message arrives, you don't want to waste time repeatedly scrolling through a long list of previous messages just to reach the latest one. Instead, your app should automatically take you to the most recent message as soon as it appears. You do this using <code>bottomRef</code>, which is a reference to an empty div positioned beneath the latest message.</p>
<p>For this to happen automatically, you'll use Vue's <code>onUpdate</code> lifecycle function. This function allows you to register a callback that gets executed each time the component is rendered. Within the callback, you instruct Vue to scroll down to <code>bottomRef</code>, ensuring that you always end up at the very latest message.</p>
<p>Next, add a template to the file to make sure we show a list of messages:</p>
<pre><code class="language-html">&lt;template&gt;
  &lt;ul class=&quot;messagesList&quot;&gt;
    &lt;Message 
      v-for=&quot;message in messages&quot; 
      :key=&quot;message.id&quot; 
      :member=&quot;message.member&quot; 
      :data=&quot;message.data&quot; 
      :id=&quot;message.id&quot;
      :me=&quot;me&quot; /&gt;
    &lt;div ref=&quot;bottomRef&quot;&gt;&lt;/div&gt;
  &lt;/ul&gt;
&lt;/template&gt;
</code></pre>
<p><code>Messages</code> returns a list of <code>Message</code> components, which you'll implement next.</p>
<p>Add another component in the folder called <strong>Message.vue</strong>. Add the following code to the file:</p>
<pre><code class="language-javascript">&lt;script setup&gt;
import { defineProps } from 'vue';

const props = defineProps({
  member: Object,
  me: Object,
  data: String,
  id: String,
});

const { username, color } = props.member.clientData;
const messageFromMe = props.member.id === props.me.id;
const className = messageFromMe
  ? &quot;messagesMessage currentMember&quot;
  : &quot;messagesMessage&quot;;
&lt;/script&gt;
</code></pre>
<p>Each message is linked to a specific <code>member</code> (user), and every member is identified by an ID, username, avatar and a personalized color. Here, you check whether the message came from you or another user. This distinction is helpful because it allows us to display our own messages on the left side.</p>
<p>Next, add the following template to the file:</p>
<pre><code class="language-html">&lt;template&gt;
  &lt;li :key=&quot;id&quot; :class=&quot;className&quot;&gt;
    &lt;span class=&quot;avatar&quot; :style=&quot;{ backgroundColor: color }&quot; /&gt;
    &lt;div class=&quot;messageContent&quot;&gt;
      &lt;div class=&quot;username&quot;&gt;{{ username }}&lt;/div&gt;
      &lt;div class=&quot;text&quot;&gt;{{ data }}&lt;/div&gt;
    &lt;/div&gt;
  &lt;/li&gt;
&lt;/template&gt;
</code></pre>
<p>The template shows the user's avatar, their name, and the message's content.</p>
<p>Now that we have the <code>Messages</code> component, we need to make sure <strong>App.vue</strong> actually renders it. Head over to <strong>src/App.vue</strong> and add an import of the <code>Messages</code> component to the top of the file:</p>
<pre><code class="language-javascript">import Messages from './components/Messages.vue';
</code></pre>
<p>We talked about showing usernames and avatars in our app earlier. Usually, you'd have a login screen to collect this info.</p>
<p>But for this tutorial, let's keep it simple. We'll make up random names and colors for the avatars. You can do this by adding the following functions just below the <code>import</code> statement:</p>
<pre><code class="language-javascript">function randomName() {
  const adjectives = [
    'autumn', 'hidden', 'bitter', 'misty', 'silent', 'empty', 'dry', 'dark',
    'summer', 'icy', 'delicate', 'quiet', 'white', 'cool', 'spring', 'winter',
    'patient', 'twilight', 'dawn', 'crimson', 'wispy', 'weathered', 'blue',
    'billowing', 'broken', 'cold', 'damp', 'falling', 'frosty', 'green', 'long',
    'late', 'lingering', 'bold', 'little', 'morning', 'muddy', 'old', 'red',
    'rough', 'still', 'small', 'sparkling', 'shy', 'wandering',
    'withered', 'wild', 'black', 'young', 'holy', 'solitary', 'fragrant',
    'aged', 'snowy', 'proud', 'floral', 'restless', 'divine', 'polished',
    'ancient', 'purple', 'lively', 'nameless'
  ];
  const nouns = [
    'waterfall', 'river', 'breeze', 'moon', 'rain', 'wind', 'sea', 'morning',
    'snow', 'lake', 'sunset', 'pine', 'shadow', 'leaf', 'dawn', 'glitter',
    'forest', 'hill', 'cloud', 'meadow', 'sun', 'glade', 'bird', 'brook',
    'butterfly', 'bush', 'dew', 'dust', 'field', 'fire', 'flower', 'firefly',
    'feather', 'grass', 'haze', 'mountain', 'night', 'pond', 'darkness',
    'snowflake', 'silence', 'sound', 'sky', 'shape', 'surf', 'thunder',
    'violet', 'water', 'wildflower', 'wave', 'water', 'resonance', 'sun',
    'wood', 'dream', 'cherry', 'tree', 'fog', 'frost', 'voice', 'paper', 'frog',
    'smoke', 'star'
  ];
  const adjective = adjectives[Math.floor(Math.random() * adjectives.length)];
  const noun = nouns[Math.floor(Math.random() * nouns.length)];
  return adjective + noun;
}

function randomColor() {
  return '#' + Math.floor(Math.random() * 0xFFFFFF).toString(16);
}
</code></pre>
<p>To make things interesting, we'll create usernames by mixing random adjectives with nouns, giving us usernames like &quot;hiddensnow&quot; or &quot;wanderingfrog.&quot; As for the colors, we'll keep them as unique as possible by choosing random hex numbers and converting them to HTML color codes.</p>
<p>Now that we have those functions, we can use them to set our initial state in <code>App</code>. Add the following below the two functions you just created:</p>
<pre><code class="language-javascript">const messages = ref([{
  id: '1',
  data: 'This is a test message!',
  member: {
    id: '1',
    clientData: {
      color: 'blue',
      username: 'bluemoon',
    },
  },
}]);
const me = ref({
  id: '0',
  clientData: {
    color: randomColor(),
    username: randomName(),
  },
});
</code></pre>
<p>For the time being, our initial state will be one test message from a mysterious hard-coded user called <em>bluemoon</em>. As for our own identity, we'll use a randomly generated name and color.</p>
<p>Next, update the <code>template</code>  by adding the following inside the <code>&lt;div class=&quot;appContent&quot;&gt;</code> element:</p>
<pre><code class="language-jsx">&lt;Messages :messages=&quot;messages&quot; :me=&quot;me&quot; /&gt;
</code></pre>
<p>This will allow you to display a list of chat messages, using the component you created earlier.</p>
<p>Head back to your browser to see your chat message!</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/09/chat-app-message-bubble.png" alt="Vue.js Real-time Chat Tutorial"></p>
<h2 id="addingatextfieldinputtothewebapp">Adding a Text Field Input to the Web App</h2>
<p>Now that we have a way to display our messages, we need a way to type them! You'll create a new Vue component called <code>Input</code> that contains a text field and a send button. The component itself won't send any messages but will call a callback whenever someone clicks the send button.</p>
<p>Create a new file in the <strong>src/components</strong> folder called <strong>Input.vue</strong>. Add a new component to the file:</p>
<pre><code class="language-javascript">&lt;script setup&gt;
import { ref, onMounted, defineProps } from 'vue';

const props = defineProps({
  onSendMessage: Function,
});

const text = ref('');
&lt;/script&gt;

&lt;template&gt;
  &lt;div class=&quot;input&quot;&gt;
    &lt;form @submit.prevent=&quot;onSubmit&quot;&gt;
      &lt;input v-model=&quot;text&quot; @input=&quot;onChange&quot; /&gt;
      &lt;button type=&quot;button&quot; @click=&quot;onSubmit&quot;&gt;Send&lt;/button&gt;
    &lt;/form&gt;
  &lt;/div&gt;
&lt;/template&gt;
</code></pre>
<p>This component will have a text field to enter the message along with a button to send it. We'll keep track of the currently entered text in our <code>text</code> reference. You'll notice we connected a function called <code>onChange</code> to the <code>input</code> element. Add that function inside the <code>script setup</code> element:</p>
<pre><code class="language-javascript">function onChange(e) {
  const textValue = e.target.value;
  text.value = textValue;
}
</code></pre>
<p>This will make sure we always have the most recently typed message inside the <code>text</code> reference.</p>
<p>Next, we need to handle sending the message, so add the following function:</p>
<pre><code class="language-javascript">function onSubmit() {
  props.onSendMessage(text.value);
  text.value = '';
}
</code></pre>
<p>To send the message, we'll use a callback inside the component's props which we'll receive from <code>App</code>. We'll also update the ref so that we clear the text field.</p>
<p>Now <code>Input</code> is all done!</p>
<p>Just like we did with <code>Messages</code>, we need to make sure <code>App</code> will render <code>Input</code>. Head over to <strong>src/App.vue</strong> and add the import for your new component to the top of the file:</p>
<pre><code class="language-javascript">import Input from './components/Input.vue';
</code></pre>
<p>Next, update the contents of the <code>render</code> method to show the <code>Input</code> component inside the <code>appContent</code> <code>div</code>:</p>
<pre><code class="language-html">&lt;Messages :messages=&quot;messages&quot; :me=&quot;me&quot; /&gt;
&lt;Input :onSendMessage=&quot;onSendMessage&quot; /&gt;
</code></pre>
<p>Notice that you pass a callback (<code>onSendMessage</code>) to <code>Input</code>. <code>Input</code> will call this each time the user presses the send button.</p>
<p>Finally, you'll implement that callback to &quot;send&quot; a message. Add the following function just before the closing <code>&lt;/script&gt;</code> tag:</p>
<pre><code class="language-javascript">function onSendMessage(message) {
  const newMessage = {
    data: message,
    member: me.value
  }
  messages.value.push(newMessage)
}
</code></pre>
<p>For now, we'll just add it to the messages array. Later, we'll connect this to a web service to actually send messages back and forth.</p>
<p>If you head back to your web browser you should see an input field. Type in a message, hit Enter, and watch your message display in the list.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/09/sending-chat-messages-locally-in-react-1.png" alt="Vue.js Real-time Chat Tutorial"></p>
<p>Look at this, your chat app is taking shape! Kudos to you!</p>
<h2 id="showingalistofonlinemembersinachatapp">Showing a List of Online Members in a Chat App</h2>
<p>Before we connect everything to a web service, there is one more part of our chat UI that we'll tackle. In a group chat setting, it's useful to show a list of currently active users. We'll do that with a simple Vue component.</p>
<p>Create a new file in <strong>src/components</strong> called <strong>Members.vue</strong>. There, you can add the following code:</p>
<pre><code class="language-javascript">&lt;script setup&gt;
import Member from './Member.vue';
import { defineProps } from 'vue';

const props = defineProps({
  members: Array,
  me: Object,
});
&lt;/script&gt;
</code></pre>
<p>This will be a simple, stateless component that takes a list of members and the current member.</p>
<p>Next, add a template to the file:</p>
<pre><code class="language-html">&lt;template&gt;
  &lt;div class=&quot;members&quot;&gt;
    &lt;div class=&quot;membersCount&quot;&gt;
      {{ members.length }} user{{ members.length === 1 ? '' : 's' }} online
    &lt;/div&gt;
    &lt;div class=&quot;membersList&quot;&gt;
      &lt;Member v-for=&quot;member in members&quot; :key=&quot;member.id&quot; :id=&quot;member.id&quot; :client-data=&quot;member.clientData&quot;
        :is-me=&quot;member.id === me.id&quot; /&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/template&gt;
</code></pre>
<p>Using the two pieces of data from <code>props</code>, this code renders a list of <code>Member</code> components, which you'll add as a separate <strong>src/components/Member.vue</strong> file:</p>
<pre><code class="language-javascript">&lt;script setup&gt;
const props = defineProps({
  id: String,
  clientData: Object,
  isMe: Boolean,
});
&lt;/script&gt;

&lt;template&gt;
  &lt;div :key=&quot;id&quot; class=&quot;member&quot;&gt;
    &lt;div class=&quot;avatar&quot; :style=&quot;{ backgroundColor: clientData.color }&quot; /&gt;
    &lt;div class=&quot;username&quot;&gt;{{ clientData.username }} {{ isMe ? ' (you)' : '' }}&lt;/div&gt;
  &lt;/div&gt;
&lt;/template&gt;
</code></pre>
<p>This component shows the member as text, adding &quot;(you)&quot; to the end of the text if it's the current user's username.</p>
<p>By now you probably know what's coming next.</p>
<p>We'll show this component from <strong>src/pages/index.js</strong>. Head on over there and add a new import to the top of the file:</p>
<pre><code class="language-javascript">import Members from './components/Members.vue';
</code></pre>
<p>Next, add a new reference right after where <code>me</code> is declared:</p>
<pre><code class="language-javascript">const members = ref([{
  id: &quot;1&quot;,
  clientData: {
    color: 'blue',
    username: 'bluemoon',
  },
}]);
</code></pre>
<p>This state variable will keep track of active members. For now, we'll hard-code a member. In the next section, we'll fetch members from a web service.</p>
<p>Finally, add your new component to the JSX, right above <code>Messages</code>:</p>
<pre><code class="language-jsx">&lt;Members :members=&quot;members&quot; :me=&quot;me&quot; /&gt;
&lt;Messages :messages=&quot;messages&quot; :me=&quot;me&quot; /&gt;
</code></pre>
<p>Run the app again and you'll see <em>bluemoon</em> in the list of chat members!</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/09/member-list-in-chat-app.png" alt="Vue.js Real-time Chat Tutorial"></p>
<p>Now, let's roll up our sleeves and connect everything to a real web service using Scaledrone.</p>
<h2 id="connectingthechatapptoawebservice">Connecting the Chat App to a Web Service</h2>
<p>At first, the thought of creating a chat application that seamlessly delivers messages in real time can be a bit daunting. Fortunately, Scaledrone comes to the rescue, simplifying the business logic involved in developing such an app.</p>
<p>Before we dive into its usage, though, we have to ensure that Scaledrone is part of our app. Here's how.</p>
<p>Installing Scaledrone is as easy as ading a JavaScript script inside your Vue project. Open <strong>index.html</strong> in the root folder of the project. Inside the <code>head</code> tag, add the following line to the bottom of the tag:</p>
<pre><code class="language-html">&lt;script type='text/javascript' src='https://cdn.scaledrone.com/scaledrone.min.js'&gt;&lt;/script&gt;
</code></pre>
<p>This will make sure Scaledrone's code is included in our app. We're almost ready to start using it, but we need to do one more thing first: create a Scaledrone channel.</p>
<p>To successfully connect to Scaledrone, you need to get your own channel ID from Scaledrone's dashboard. If you haven't already, head over to <a href="http://scaledrone.com/">Scaledrone.com</a> and register an account. Once you've done that, go to your dashboard and click the big green <strong>+Create Channel</strong> button to get started. You can choose <strong>Never require authentication</strong> for now. Note down the channel ID from the just created channel, you'll need it in a bit.</p>
<p>Head back to <strong>src/App.vue</strong>. We'll add a function to connect to Scaledrone, but first, we need to do a bit of prep work.</p>
<p>Add a new top-level object to the file inside the <code>script setup</code> tag:</p>
<pre><code class="language-javascript">let drone = null
</code></pre>
<p>We'll use this variable to store a global Scaledrone instance that we can access in the component.</p>
<p>Add a new function to the top of the component to connect to Scaledrone:</p>
<pre><code class="language-javascript">function connectToScaledrone() {
  drone = new window.Scaledrone('YOUR-CHANNEL-ID', {
    data: me.value,
  });
  drone.on('open', error =&gt; {
    if (error) {
      return console.error(error);
    }
    me.value.id = drone.clientId;
  });
}
</code></pre>
<p>This will create a new instance of Scaledrone. Just remember to replace the string with the channel ID you got earlier. Since we're loading the script inside our HTML <code>head</code> tag, the script's contents will be attached to the global <code>window</code> object. That's where we'll ultimately fetch our Scaledrone instance from.</p>
<p>We'll also pass Scaledrone the data for the currently logged-in member. If you have additional data about the user or the client, this is a good way to supply that data, instead of having to send it with each message.</p>
<p>Moving on, we need to connect to a room. A room is a group of users that we can send messages to. You listen to those messages by subscribing to a room of a specific name. Add the following code to the end of the function you just created:</p>
<pre><code class="language-javascript">const room = drone.subscribe('observable-room');
</code></pre>
<p>We'll subscribe to a room.</p>
<blockquote>
<p>Note: You might have noticed that we named our name Scaledrone room <code>observable-room</code>. You can name the room anything you want, a single user can actually connect to an infinite amount of rooms for all sorts of application scenarios. However, in order for messages to contain the info of the sender, you need to prefix the room name with <code>&quot;observable-&quot;</code>. <a href="https://www.scaledrone.com/docs/api-clients/observable-rooms">Read more...</a></p>
</blockquote>
<p>We also need to know when the messages arrive, so we'll subscribe to the &quot;message&quot; event on the room. Add the following code to the end of the constructor:</p>
<pre><code class="language-javascript">room.on('message', message =&gt; {
  const { data, member } = message;
  messages.value.push(message)
});
</code></pre>
<p>When we get a new message, we'll add the message's text as well as the client data to our state.</p>
<p>Similarly, we need to track the logged-in members:</p>
<pre><code class="language-javascript">room.on('members', newMembers =&gt; {
  members.value = newMembers;
});
room.on('member_join', member =&gt; {
  members.value.push(member);
});
room.on('member_leave', ({ id }) =&gt; {
  const index = members.value.findIndex(m =&gt; m.id === id);
  members.value.splice(index, 1);
});
</code></pre>
<p>Scaledrone gives up the logged in members when we join and then keeps us updated when someone else joins or leaves. We'll make sure to keep our state variables updated with the correct members.</p>
<p>Now you just need to make sure the function is called when the component loads. Add this code right below the function definition:</p>
<pre><code class="language-javascript">onMounted(() =&gt; {
  if (drone === null) {
  	connectToScaledrone();
  }
});
</code></pre>
<p>You do a small check to make sure Scaledrone is not already connected and then call the function.</p>
<p>Next, we need to modify <code>onSendMessage</code> to publish a new message to all the other users in the room:</p>
<pre><code class="language-javascript">function onSendMessage(message) {
  drone.publish({
    room: 'observable-room',
    message,
  });
}
</code></pre>
<p>Scaledrone makes this a simple matter of calling the <code>publish</code> function with the data.</p>
<p>Finally, we'll remove the test message and other hard-coded data from the <code>ref</code>s so that the initial state looks like the following:</p>
<pre><code class="language-javascript">const messages = ref([]);
const members = ref([]);
const me = ref({
  username: randomName(),
  color: randomColor(),
});
</code></pre>
<p>If you switch to your web browser at this point, you'll notice that the behavior remains the same: when you send a new message, it should appear in the list. The difference is that this time it's actually being sent to Scaledrone. Feel free to open the app in another tab and chat with yourself. :)</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/09/react-chat-app-web-service.png" alt="Vue.js Real-time Chat Tutorial"></p>
<h2 id="addingatypingindicatortoavuechatapp">Adding a Typing Indicator to a Vue Chat App</h2>
<p>When you're chatting away on an app, you'd like to know what's happening on the other side. Has the other person seen your message? Are they typing back? Thankfully, with nifty features like 'read' notifications and typing indicators, chat apps keep us in the loop.</p>
<p>Fear not; Scaledrone makes this a walk in the park. Let's see how it's done.</p>
<h3 id="creatingavuetypingindicatorcomponent">Creating a Vue Typing Indicator Component</h3>
<p>As you can assume, we'll now add a typing indicator. Ours will display a message like &quot;hiddenstar is typing.&quot;</p>
<p>The first step includes creating a new Vue component that will render the typing text. To begin, create a new file in <strong>src/components</strong> called <strong>TypingIndicator.vue</strong>. Then, add the following code:</p>
<pre><code class="language-javascript">&lt;script setup&gt;
import { defineProps, computed } from 'vue';

const props = defineProps({
  members: Array,
});

const typingText = computed(() =&gt; {
  // 1
  const names = props.members.map((member) =&gt; member.clientData.username);
  if (names.length === 0) {
    return '';
  }
  // 2
  if (names.length === 1) {
    return `${names[0]} is typing`;
  }

  return `${names.slice(0, -1).join(', ')} and ${names.slice(-1)} are typing`;
});
&lt;/script&gt;
</code></pre>
<p>This component will eventually add a small <code>div</code> that gets filled with text inside <code>typingText</code> when someone is typing. You will pass a list of users that are currently typing to this component. Here's how it works:</p>
<ol>
<li>First, you get a list of usernames of all the members that are currently typing. If nobody is typing, an empty string is returned.</li>
<li>If one person is typing, the text mentions their name such as &quot;mysteriousrabbit is typing.&quot;</li>
<li>In case more people are typing, their names are joined using a comma as a separator. The text then reads something like: &quot;mysteriousrabbit, coolfrog and bluecar are typing.&quot;</li>
</ol>
<p>Next, add a template to the file to show the typing text:</p>
<pre><code class="language-html">&lt;template&gt;
  &lt;div class=&quot;typingIndicator&quot;&gt;
    &lt;p&gt;{{ typingText }}&lt;/p&gt;
  &lt;/div&gt;
&lt;/template&gt;
</code></pre>
<p>Now that we have the component, we need code to show it on the web app. Open <strong>src/App.vue</strong> and import your new component at the top of the file:</p>
<pre><code class="language-javascript">import TypingIndicator from './components/TypingIndicator.vue';
</code></pre>
<p>Next, you have to modify the template to show the typing indicator right under the <code>Messages</code> component, like so:</p>
<pre><code class="language-javascript">&lt;Messages :messages=&quot;messages&quot; :me=&quot;me&quot; /&gt;
&lt;TypingIndicator :members=&quot;members.filter(m =&gt; m.typing &amp;&amp; m.id != me.id)&quot; /&gt;
</code></pre>
<p>The code above shows the new component and passes it a list of users who are typing by checking the <code>typing</code> property.</p>
<p>The UI is now set up, all that's left is to track and send the typing state.</p>
<h3 id="trackingiftheuseriscurrentlytypinginvue">Tracking if the User is Currently Typing in Vue</h3>
<p>Right now, we aren't tracking users' typing status. And it's not as black and white as simply <em>typing/not typing</em>.</p>
<p>Someone could begin typing a message and then get sidetracked—maybe they felt like brewing a coffee—leaving their message unfinished. Conversely, if the typing indicator pops up every time someone pauses to hunt for the right emoji, it might just irk your users.</p>
<p>That's where Scaledrone and its optional library called <a href="https://www.npmjs.com/package/typing-indicator">Typing Indicator</a> step in to handle all of this for you.</p>
<blockquote>
<p>Note: While <a href="https://www.npmjs.com/package/typing-indicator">Typing Indicator</a> is created by and for Scaledrone, it is framework-agnostic and works with any JavaScript application or website, whether's it's built in Vue, React or something else entirely.</p>
</blockquote>
<p>To install Typing Indicator, navigate to the project folder in <strong>Terminal</strong> and type in:</p>
<pre><code class="language-bash">npm i typing-indicator
</code></pre>
<p>This little command will install the library.</p>
<p>You'll track the typing state from <strong>src/components/Input.vue</strong>, so navigate there and import the library at the top of the file:</p>
<pre><code>import TypingIndicator from 'typing-indicator';

let typingIndicator = null;
</code></pre>
<p>You also create a top-level variable to hold an instance of the typing indicator.</p>
<p>Next, change the definition of <code>props</code> to include a new function:</p>
<pre><code class="language-javascript">const props = defineProps({
  onSendMessage: Function,
  onChangeTypingState: Function,
});
</code></pre>
<p><code>onChangeTypingState</code> is a callback that you'll define in <strong>App.vue</strong>, but <code>Input</code> will call it when the current user changes their typing state.</p>
<p>Then, you'll add a new typing indicator when the component loads by adding the following code inside the component, right below where the props are declared:</p>
<pre><code class="language-javascript">onMounted(() =&gt; {
  if (typingIndicator === null) {
    typingIndicator = new TypingIndicator();
    typingIndicator.listen(props.onChangeTypingState);
  }
});
</code></pre>
<p>When this component loads, you will register a callback to get called whenever a user begins or stops typing.</p>
<p>Next, we need to tell the typing indicator when the text changes, so update <code>onChange</code> to the following:</p>
<pre><code class="language-javascript">function onChange(e) {
  const textValue = e.target.value;
  typingIndicator.onChange(textValue);
  text.value = textValue;
}
</code></pre>
<p>The typing indicator does the heavy lifting of tracking typing states for you. All you need to do is connect it to a function, which, in this case, is <code>onChangeTypingState</code>, and tell it when the text has changed.</p>
<p>Now, let's wrap things up in <strong>src/pages/App.vue</strong> by passing the callback in. Navigate there and modify the <code>Input</code> declaration inside the template to the following:</p>
<pre><code class="language-jsx">&lt;Input 
  :onSendMessage=&quot;onSendMessage&quot; 
  :onChangeTypingState=&quot;onChangeTypingState&quot; /&gt;
</code></pre>
<p>This will ensure that <code>Input</code> calls <code>onChangeTypingState</code>, which you'll implement next.</p>
<h3 id="sendingandreceivingthetypingstatetoyourwebservice">Sending and Receiving the Typing State to Your Web Service</h3>
<p>Right now, you are passing <code>onChangeTypingState</code> to <code>Input</code> but you haven't yet created the function. The function needs to send a message to Scaledrone, letting other users know that the current user is typing.</p>
<p>To continue, add the following function above the closing <code>&lt;/script&gt;</code> tag:</p>
<pre><code class="language-javascript">function onChangeTypingState(typing) {
  drone.publish({
    room: &quot;observable-room&quot;,
    message: { typing },
  });
}
</code></pre>
<p><code>onChangeTypingState</code> receives a boolean that indicates whether the user is typing or not. Using Scaledrone, you'll publish a new message with the updated typing state whenever it changes.</p>
<p>Next, we need to process the incoming typing messages and show the typing indicator. You'll do this in the <code>connectToScaledrone</code>. Modify the call to <code>room.on('message', ...)</code> to the following:</p>
<pre><code class="language-javascript">room.on('message', message =&gt; {
  const { data, member } = message;
  if (typeof data === 'object' &amp;&amp; typeof data.typing === 'boolean') {
    const index = members.value.findIndex(m =&gt; m.id === member.id);
    members.value[index].typing = data.typing;
  } else {
    messages.value.push(message)
  }
});
</code></pre>
<p>Here's the breakdown of what this code does:<br>
The code first evaluates the type of incoming message. If the message is a typing status, you identify the members currently typing and update their <code>typing</code> attribute. Otherwise, if it's a regular message, you add it to <code>messages</code>.</p>
<p>With this setup, you can monitor users' typing status and relay it to Scaledrone. When a new typing notification comes from Scaledrone, the user's <code>typing</code> attribute is updated. Lastly, the <code>TypingIndicator</code> component will use this status and display a typing indicator for users with <code>typing</code> set to true.</p>
<p>Head back to your browser to see the typing indicator in action!</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/09/react-chat-app-scaledrone.png" alt="Vue.js Real-time Chat Tutorial"></p>
<h2 id="allwrappedup">All Wrapped Up!</h2>
<p>There you have it! You've successfully crafted a modern Vue.js 3 chat application, and thanks to Scaledrone's capabilities, you made it seem like a breeze. The days of getting lost in backend code are over. With the aid of Scaledrone, you can dedicate yourself to refining your UI and enhancing your app's features without a second thought about backend hassles. **See the full source code for this Vue.js chat tutorial on <a href="https://github.com/ScaleDrone/vue-chat-tutorial">GitHub</a>.</p>
<p>But there's so much more potential for your chat app. Interested in expanding its features? Here are some cool additions you can add using Scaledrone's APIs:</p>
<ul>
<li>Share images and files</li>
<li>Implement authentication</li>
<li>Respond to specific messages</li>
<li>Incorporate stickers and GIFs</li>
</ul>
<p>If you have any questions or wish to share feedback, don't hesitate to <a href="https://www.scaledrone.com/contact">reach out to us</a>; we're always happy to connect.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Building a realtime voting app in React]]></title><description><![CDATA[In this post, we’ll build a realtime voting system that will allow users to vote for their favorite football player and see their votes count in realtime.]]></description><link>https://www.scaledrone.com/blog/unrealtime-voting-system-with-react-and-scaledronetitled/</link><guid isPermaLink="false">5c31abdb58db83073fd71217</guid><category><![CDATA[Tutorials]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[react]]></category><dc:creator><![CDATA[Peter Ekene]]></dc:creator><pubDate>Fri, 15 Sep 2023 18:54:12 GMT</pubDate><media:content url="https://www.scaledrone.com/blog/content/images/2023/09/PTD2SG8w-2.gif" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><h2 id="introduction">Introduction</h2>
<img src="https://www.scaledrone.com/blog/content/images/2023/09/PTD2SG8w-2.gif" alt="Building a realtime voting app in React"><p>The importance and applications of realtime polling, realtime updates and data visualization can never be over emphasized. The convenience of casting votes online and the confidence that comes with knowing that your vote actually counts as you will see it do in realtime is one of the many reasons why so many countries, companies and individuals have come to adopt electronic opinion polls to gather data on the choices of the masses on relevant topics and ideas.</p>
<p>In this post, we’ll build a realtime voting system that will allow users to vote for their favorite football player and see their votes count in realtime. Of course you can extend this application to suit any use case that is particular to you, however, for the purpose of our demonstrations, we’ll stick to voting for players.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>A basic understanding of React and Node.js will help you get the best out of this tutorial. It is assumed that you already have Node and npm installed. If that’s not the case for you, kindly check <a href="https://nodejs.org/en/">Node.js</a> and <a href="https://www.npmjs.com/">npm</a> for further instructions and installation steps.</p>
<p><strong>We’ll also use these tools:</strong></p>
<p><a href="https://dashboard.scaledrone.com"><strong>Scaledrone</strong></a> - A realtime messaging platform that will power the realtime functionalities of our app. Go ahead and <a href="https://dashboard.scaledrone.com/signup">create a free account</a> to gain access to the dashboard.</p>
<p><a href="https://www.npmjs.com/package/axios"><strong>Axios</strong></a>: a promise-based HTTP client that works both in the browser and in a Node.js environment.</p>
<h3 id="whatwellbuild">What we’ll build</h3>
<p>We’ll build this voting system where users will vote for their favorite players and see their votes and that of other users in realtime.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/09/PTD2SG8w-1.gif" alt="Building a realtime voting app in React"></p>
<h2 id="setupreactproject">Set up React project</h2>
<p>First things first, let’s create our React project. I imagine you will be fairly familiar with this step, however, we’ll walk you through it nonetheless. Open a terminal window and install the React CLI tool globally by running this command:</p>
<pre><code>npm install -g create-react-app
</code></pre>
<p>Now, open another terminal window on your Desktop and run this commands respectively to create a new React application using the CRA tool we just installed:</p>
<pre><code> mkdir polling-app // create a new project folder
 
 cd polling-app // navigate into the new folder
 
 create-react-app my-app // create a new react project called my-app
 
 cd my-app // navigate into the new react project
 
 npm start // start the development server.
</code></pre>
<p>When you visit your default browser on <code>localhost:3000</code>, you should see your project live on the browser.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/09/VNNOyW4g.gif" alt="Building a realtime voting app in React"></p>
<p>Now you can go ahead and open the project in your favorite code editor and we can start writing some code. Before we jump into it, let’s take a moment to understand our app.</p>
<p>Since, we are building a voting system, we’ll need a custom component where we’ll render our players and have users cast votes for them. Also we’ll define the mock data for our players in the <code>App</code> component where we'll equally handle server-client communications.</p>
<p>Finally we'll have a server file where we'll subscribe to Scaledrone and dispatch votes to all connected users in realtime. That said, let's get started.</p>
<h2 id="setupanodeserver">Set up a Node server</h2>
<p>Now that we’ve created the project, let’s first create our Node server before we dive into the app itself. What we want, basically, is to send a vote (the name of a player) to our server via a POST request, the server receives the vote and publishes it to the room where all subscribed users will see the result.</p>
<p><strong>Install Scaledrone</strong></p>
<p>To install Scaledrone to our application, open a terminal in the project root directory and run the  following npm command:</p>
<pre><code>npm install scaledrone-node-push --save
</code></pre>
<p>This will make Scaledrone available in our <code>node_modules</code> folder so that we can require it in our Node server. Since, we are here, let's install the other packages we'll need to build our server</p>
<pre><code>npm install express body-parser axios cors --save
</code></pre>
<p>Next, let’s create our server. First, create a <code>.env</code> file to store our environment variables In the project root directory, then set it up like so:</p>
<pre><code>//.env

CHANNEL_ID = YOUR_CHANNEL_ID
SECRET_KEY = YOUR_CHANNEL_SECRET
</code></pre>
<p><em><strong>Note</strong></em>: You will replace the <em><strong><code>YOUR_CHANNEL_ID</code></strong></em> and <em><strong><code>YOUR_CHANNEL_SECRET</code></strong></em> placeholders in the file above with the real values from the your Scaledrone dashboard.</p>
<p>To set up the Node server, create a  <code>server.js</code> file in the projects root directory and update it with the code below:</p>
<pre><code>//server.js

require(&quot;dotenv&quot;).config();
const express = require(&quot;express&quot;);
const bodyParser = require(&quot;body-parser&quot;);
const app = express();
const port = 4000;
const Scaledrone = require(&quot;scaledrone-node-push&quot;);
CHANNEL_ID = process.env.CHANNEL_ID;
CHANNEL_SECRET = process.env.SECRET_KEY;
const sd = new Scaledrone({
  channelId: CHANNEL_ID,
  secretKey: CHANNEL_SECRET
});
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use((req, res, next) =&gt; {
  res.header(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;);
  res.header(
    &quot;Access-Control-Allow-Headers&quot;,
    &quot;Origin, X-Requested-With, Content-Type, Accept&quot;
  );
  next();
});
app.post(&quot;/vote&quot;, (req, res) =&gt; {
  const { body } = req;
  const room = &quot;live-votes&quot;;
  const response = { playerId: body.vote.player_id };
  const message = response.playerId;
  sd.publish(room, message, error =&gt; {
    if (error) {
      console.log(error);
    } else {
      res.json({
        player_id: body
      });
    }
  });
});
app.listen(port, () =&gt; {
  console.log(`Server started on port ${port}`);
});
</code></pre>
<p>Let’s walk through it. Here we created a new instance of Scaledrone with the <code>ChannelID</code> and <code>SecretKey</code> gotten from the Scaledrone dashboard as we’ll demonstrate along the way. Then we defined a <code>/vote</code> route to process our votes when the client makes a POST request to that endpoint with the name of a player to vote for.</p>
<p>Finally when the server receive the request, we publish the vote in the Scaledrone room we named <code>live-votes</code>. On the client, we’ll subscribe to the same room and listen for the events which when recieved, we’ll update the <code>playerDetails</code> object with the new vote for all connected users to see in realtime.</p>
<h2 id="setupplayerscomponent">Set up players component</h2>
<p>With the server set, let’s continue hooking up the client. In the project root <code>src</code> folder, create a new folder called  <code>components</code>, inside it, create a new component called <code>players.js</code> and set it up like so:</p>
<pre><code>//src/components/players

import React, { Component } from 'react';
class Player extends Component {
    handleClick = () =&gt; {
    // coming soon ... 
    }
    render() {
        return (
            &lt;div className=&quot;App&quot;&gt;
                &lt;img className=&quot;rounded-circle&quot; src={this.props.image} alt=&quot;player&quot; /&gt;
                &lt;div className=&quot;mt-2&quot;&gt;
                    &lt;h5 className=&quot;card-title&quot;&gt;{this.props.name}&lt;/h5&gt;
                &lt;/div&gt;
                &lt;h5&gt;Goals: {this.props.goals} &lt;/h5&gt;
                &lt;div&gt;
                    &lt;h2&gt; Votes: {this.props.votes} &lt;/h2&gt;
                &lt;/div&gt;
                &lt;div className=&quot;mb-3&quot;&gt;
                    &lt;button type=&quot;button&quot; onClick={this.handleClick} className=&quot;btn btn-primary btn-lg&quot;&gt;Vote For {this.props.name}&lt;/button&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        );
    }
}
export default Player;
</code></pre>
<p>Here, we have defined a template for our app UI. However, the values of these view elements are not present at the moment since we’re passing them into the <code>players</code> component from the <code>App</code> component via props. Let’s now set up the <code>App</code> component to update the <code>players</code> component with props.</p>
<h2 id="setupappcomponent">Set up App component</h2>
<p>To display the data we have defined for the UI elements in the <code>players</code> component, let’s open our <code>App</code> component and update it with the mock data we prepared for this project like so:</p>
<pre><code>//src/App.js

import React, { Component } from &quot;react&quot;;
import &quot;./App.css&quot;;
import Player from &quot;./components/players&quot;;
import cr7 from &quot;../src/img/ronaldo.jpg&quot;;
import lm10 from &quot;../src/img/lm10.jpg&quot;;
import pogba from &quot;../src/img/paul.jpg&quot;;
const playerData = [
  {
    name: &quot;Ronaldo&quot;,
    goals: 30,
    votes: 0,
    id: 1,
    image: cr7
  },
  {
    name: &quot;Messi&quot;,
    goals: 8,
    votes: 0,
    id: 2,
    image: lm10
  },
  {
    name: &quot;Pogba&quot;,
    goals: 26,
    votes: 0,
    id: 3,
    image: pogba
  }
];
class App extends Component {
  state = {
    playerDetails: []
  };
  componentDidMount() {
    this.setState({ playerDetails: playerData });
  }
  render() {
    return playerData.map(player =&gt; (
      &lt;Player
        key={player.id}
        name={player.name}
        goals={player.goals}
        image={player.image}
        votes={player.votes}
        id={player.id}
      /&gt;
    ));
  }
}
export default App;
</code></pre>
<p>Notice that we added images locally to the project. Feel free to do the same, create a folder <code>img</code> inside the <code>src</code> folder and add your favorite player images to it, or simply reuse the sample images we used on this project, you can get it from the project repository.</p>
<p>Next, you may have noticed the Bootstrap classes on our view elements, let’s add the Bootstrap CDN to install Bootstrap and beautify the UI. Navigate to the project <code>public</code> folder and open the <code>index.html</code> file, add the CDN inside the documents <code>&lt;head&gt;</code> tag:</p>
<pre><code>// public/index.html

&lt;link href=&quot;https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css&quot; rel=&quot;stylesheet&quot; integrity=&quot;sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS&quot; crossorigin=&quot;anonymous&quot;&gt;
</code></pre>
<p>At this point, if you save and reload the browser, you should get the exact app UI as shown below:</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/09/working-UI.gif" alt="Building a realtime voting app in React"></p>
<p>At the moment the app is stale. Nothing interesting happens on the app as of yet when you click a button to cast a vote for any player. Talking about votes, let’s get on with.</p>
<h2 id="setupscaledrone">Set up Scaledrone</h2>
<p>Before we dive into the process of casting votes, let’s first, visit the Scaledrone <a href="https://dashboard.scaledrone.com/signup">sign up page</a> and create a Scaledrone account and get our credentials since we’ll need it for the next step.</p>
<p>Once you’ve signed up, you’ll be required to create a channel. Creating a channel will give you access to the channels credentials like the <code>ChannelID</code> and <code>Secret_key</code> that we’ll use to establish connection to Scaledrone via our app.</p>
<p>Next we’ll create the channel. Click the button and provide a channel name</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/09/WO1ly1zY.png" alt="Building a realtime voting app in React"></p>
<p>For now we’ll just provide the channel name and keep the defaults. In production you will have to properly authenticate your channels and manage user privileges however, we are only building for demonstration purposes so we can skip that for now.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/09/scaledrone-channel.png" alt="Building a realtime voting app in React"></p>
<p>Finally when we create the channel, we’ll have access to this credentials. Keep them handy as we’ll be using them shortly. Also copy them into the <code>server.js</code>  file we created at the begining and replace the <code>CHANNELID and SECRET_KEY</code> placeholders respectively. Now that we have successfully set up our Scaledrone credentials, let’s go back to our app and add some interactivity.</p>
<p><strong>Add Scaledrone JavaScript CDN</strong></p>
<p>Finally to interact with Scaledrone from the client, open the <code>public/index.html</code> file again and add the Scaledrone CDN to the <code>&lt;head&gt;</code> tag like so:</p>
<pre><code>  // public/index.html
  
 &lt;script src='https://cdn.scaledrone.com/scaledrone.min.js'&gt;&lt;/script&gt;
</code></pre>
<h2 id="sendingvotes">Sending votes</h2>
<p>So far we have a bare template with our players, their details and a button to cast a vote for them. Let’s hook up the buttons to send the votes to the server.</p>
<p>First, update the <code>handleClick()</code> function in the <code>player</code> component like so:</p>
<pre><code>   // src/components/players.js
   
    handleClick = () =&gt; {
         this.props.onVoteCasted(this.props.name)
    }
</code></pre>
<p>Then in the  <code>App</code> component, let’s define an event handler that will get the name of the player we are voting for from the <code>players</code> component and use <code>Axios</code> to post that player to the server. In the <code>App.js</code> file, add this code:</p>
<pre><code> // src/App.js

import axios from 'axios'; // import axios
  ...
  // below componentDidMount() and before render(), add handleEvent
  
  handleEvent = playerId =&gt; {
    const vote = { player_id: playerId };
    axios.post(&quot;http://localhost:4000/vote&quot;, { vote }).then(response =&gt; {
      console.log(response);
    });
  };
  ...
</code></pre>
<p>Here, the <code>playerId</code> variable is the name of the player the user is voting for. Once the user has clicked on a particular players vote button, the <code>playerId</code> variable will pass the name of the player to the <code>handleEvent()</code> callback function which will then post the player’s name to the server.</p>
<p>Next, Let’s add the <code>onVotecasted</code> props to the <code>render()</code> method to hook up the <code>handleEvent()</code> function here with <code>handleClick()</code> in the players component. Update the <code>render()</code> method like so:</p>
<pre><code> render() {
    return playerData.map(player =&gt;
      &lt;Player
        key={player.id}
        name={player.name}
        goals={player.goals}
        image={player.image}
        votes={player.votes}
        id={player.id}
++      onVoteCasted={this.handleEvent} // add this line
      /&gt;);
  }
</code></pre>
<h2 id="receivingvotes">Receiving votes</h2>
<p>When the server receives the player to be voted for, it publishes the name in the <code>live-votes</code> room. On the client, we listen for the event. In the <code>App.js</code> file, create a constructor and update it with the code below:</p>
<pre><code>// In App.js, before componentDidMount(), add the constructor

  constructor() {
    super();
    this.drone = new window.Scaledrone('vIh5lXOnewFxNIeC');
    this.drone.on('open', error =&gt; {
      if (error) {
        return console.error(&quot;Error&quot;);
      }
    })
    const room = this.drone.subscribe('live-votes');
    room.on('data', (data) =&gt; {
      this.state.playerDetails.map(player =&gt; {
        if (player.name === data) {
          return Object.assign({}, player, {
            votes: player.votes += 5
          });
        } else {
          return player;
        }
      });
      this.setState({
        playerDetails: this.state.playerDetails
      });
    });
  }
</code></pre>
<p>This is how we listen for the event on the room and update the <code>playerDetails</code> object with the new vote. First, we created a new Scaledrone instance globally with the <code>channelID</code>. Then we subscribed to the <code>live-votes</code> room to listen for an incoming message. When the message (players name) comes in from the server, we update the players votes by 5 counts and all connected users will see the update in realtime.</p>
<p><strong>Demo</strong><br>
Now start the server (be sure to have added your Scaledrone credentials) by running this command in a terminal window on the project root directory:</p>
<pre><code> node server
</code></pre>
<p>Then try out the application, you should get the same exact functionality.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/09/PTD2SG8w-1.gif" alt="Building a realtime voting app in React"></p>
<p>Wonderful, the app works as expected!. But that's not all, let's authenticate</p>
<h2 id="authentication">Authentication</h2>
<p>In a real word application, it’ll make sense to manage user access to the voting channels. Take for instance, in our voting application, we can make the system a bit less hackable by authenticating the voters and managing their access to the voting room.</p>
<p>We will implement JWT authentication in our app. Basically we want to determine when users can either subscribe or publish to our voting room.</p>
<p>That said, let’s jump in. To implement this feature in our existing application, this is what we’ll do:</p>
<ul>
<li>First, create a new channel on your Scaledrone dashboard and set the <code>Authentication</code> field to 'always require authentication':</li>
</ul>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/09/scaledrone-channel-2.png" alt="Building a realtime voting app in React"></p>
<ul>
<li>
<p>Next copy the keys of this new channel into your project, replace it with the existing keys from the previous channel in both your <code>App.js</code> file and <code>server.js</code> file. Then run the app again, you’ll notice that you can’t send votes. That’s because you can only communicate with the channel as an authenticated user, which at the moment you’re not. So let’s get you authenticated.</p>
</li>
<li>
<p>Install JSON web token. Navigate back to your project root directory and run</p>
<p>npm install --save jsonwebtoken</p>
</li>
<li>
<p>When a user connects to a Scaledrone room, in our case the “live-votes” room, a unique <code>clientId</code> is assigned. We’ll retrieve and send this <code>clientId</code> to the server and then use it to generate a token. The token will contain the users permissions which will determine wether the user can publish or subscribe to the room. So in your <code>App.js</code> file, update your constructor like this:</p>
<pre><code>//src/App.js

constructor() {
  super();
  this.drone = new window.Scaledrone(&quot;w4BKWxiW6yzeGrN3&quot;);
  this.drone.on(&quot;open&quot;, error =&gt; {
    if (error) {
      return console.log(error);
    }
    const user = { clientId: this.drone.clientId };
    axios.post(&quot;http://localhost:4000/auth&quot;, { user })
      .then(response =&gt; response.data)
      .then(jwt =&gt; this.drone.authenticate(jwt));
  });
  this.drone.on(&quot;authenticate&quot;, error =&gt; {
    if (error) {
      return console.error(error);
    }
    console.log(&quot;Authenticated&quot;);
   });
  const room = this.drone.subscribe(&quot;live-votes&quot;);
  room.on(&quot;data&quot;, data =&gt; {
    this.state.playerDetails.map(player =&gt; {
      if (player.name === data) {
        return Object.assign({}, player, {
          votes: (player.votes += 5)
        });
      } else {
        return player;
      }
    });
    this.setState({
      playerDetails: this.state.playerDetails
    });
  });
}
</code></pre>
</li>
</ul>
<p>This will send the user’s unique id to the server and use the returned token to authenticate the user. To hook up the server functionality, go ahead and add this route to your <code>server.js</code> file:</p>
<pre><code>//src/server.js
const jwt = require('jsonwebtoken');
...
app.post('/auth', function(req, res) {
  const { body } = req;
  const userId = body.user.clientId
  if (hasChannelAccess(userId)) {
    const payload = {
      client: body.user.clientId,
      channel: CHANNEL_ID,
      permissions: {
        '^live-votes$': {
          publish: true,
          subscribe: true,
        },
      },
      exp: Math.floor(Date.now() / 1000) + 60 * 3 // token epires in 3 minutes
    };
    const token = jwt.sign(payload, CHANNEL_SECRET, {algorithm: 'HS256'});
    res.status(200).end(token);
  } else {
    res.status(403).end('Sorry! You are not allowed.');
  }
});
function hasChannelAccess(req) {
  // Your should implement your own authentication code here.
  // You could query your user from your database and see if they are allowed to
  // connect or give them user-scoped access using JWT permissions
  return true;
}
</code></pre>
<p>Here, we defined the payload, gave publish and subscribe permissions to the user, generated a minute token for the user and sends it back to the client. This is exactly the functionality we wanted. At this point, when you run your server and reload the app, you should now be able to cast votes as you did before.</p>
<p>If we wanted to allow the users to subscribe to the live-votes channel but not publish votes into it, all we have to do is update the permissions object in our payload like so:</p>
<pre><code>permissions: {
        '^live-votes$': {
          publish: false,
          subscribe: true,
        }
}
</code></pre>
<p>With this, you can authenticate and manage users access to your application’s channels.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Scaledrone is a realtime service provider that powers both mobile and web applications alike. In this application we have leveraged its capabilities to build an extendable realtime voting platform for the web and equally discussed a few React tips along the way. There’s so much you can do with Scaledrone and this project is just one of many. Feel free to look through the <a href="https://dashboard.scaledrone.com">documentation</a> for more. If you will like to get your hands on the project source code, visit the <a href="https://github.com/ScaleDrone/nodejs-voting">project repository</a>.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Tutorial: Build a React.js Chat App]]></title><description><![CDATA[In this step-by-step tutorial you'll build a real-time chat app entirely on the client side, utilizing React and Scaledrone for real-time communication.]]></description><link>https://www.scaledrone.com/blog/tutorial-build-a-reactjs-chat-app/</link><guid isPermaLink="false">5bc992e358db83073fd711c7</guid><category><![CDATA[Tutorials]]></category><dc:creator><![CDATA[Marin Bencevic]]></dc:creator><pubDate>Sun, 03 Sep 2023 15:08:00 GMT</pubDate><media:content url="https://www.scaledrone.com/blog/content/images/2023/09/CleanShot-2023-09-03-at-13.57.58@2x-1.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://www.scaledrone.com/blog/content/images/2023/09/CleanShot-2023-09-03-at-13.57.58@2x-1.png" alt="Tutorial: Build a React.js Chat App"><p><em>Tutorial updated and completely rewritten 3. September 2023.</em></p>
<blockquote>
<p>Check out the <a href="https://scaledrone.github.io/react-chat-tutorial/"><strong>live example</strong></a> 🚀. As well as the full <a href="https://github.com/scaleDrone/react-chat-tutorial">source code on GitHub</a> 👩🏽‍💻.</p>
</blockquote>
<p>Adding chat functionality to web apps may have been a nice-to-have option in the past, but now it's a modern-day necessity. Imagine having an online marketplace, a social network, or a collaboration tool without the ability to chat in real-time. It's like having a phone without the call feature; it just doesn't cut it.</p>
<p>Picture this: a user is browsing your web app and wants to share something with their friend. With chat, they don't need to break their engagement stride by exiting the app. Instead, they can have real-time communication within the same platform.</p>
<p>In this tutorial, we'll show you how you can achieve that. We'll build a React.js chat application using the <a href="https://www.scaledrone.com/">Scaledrone</a>, a real-time messaging API which makes building chat rooms a breeze. The app will work similarly to apps like Messenger, WhatsApp or Telegram.</p>
<p>By the end, this is what our React chat app will look like:</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/09/react-chat-app-scaledrone.png" alt="Tutorial: Build a React.js Chat App"></p>
<p>Aside from sending text messages to a web service, it will feature showing avatars and usernames, chat message bubbles, a list of online members, as well as a typing indicator.</p>
<p>We'll achieve this using modern React web app best practices, including using <strong>Next.js</strong>, <strong>functional components</strong> and <strong>React Hooks</strong>.</p>
<p>And here's the bonus: this app will also be cross-platform compatible with our <a href="https://www.scaledrone.com/blog/ios-chat-tutorial/">iOS</a> and <a href="https://www.scaledrone.com/blog/android-chat-tutorial/">Android</a> chat tutorials. Let's go!</p>
<h2 id="settingupareactchatapp">Setting Up a React Chat App</h2>
<p>We'll start by creating a new React app using Next.js, an easy tool to get started with React projects.</p>
<p>To use Next.js and React, you need <a href="https://nodejs.org/en/">Node.js</a>. Follow the instructions on the link to install if you haven't already. Once that's sorted, creating our starter app becomes a breeze. Open <strong>Terminal</strong> and navigate to a folder where you'd like your app to exist. Type in the following command:</p>
<pre><code>npx create-next-app --example https://github.com/ScaleDrone/react-chat-tutorial/tree/starter app-name
cd app-name
</code></pre>
<blockquote>
<p>Note: The <code>app-name</code> can be anything you like.</p>
</blockquote>
<p>This command will download starter code with an empty React app to make this tutorial easier to follow. The starter code includes an empty <code>App</code> component and CSS to make your app look nice.</p>
<p>Open the newly created <code>app-name</code> folder in your favorite editor. Now we can dive in for real.</p>
<h2 id="showingchatmessagesusingreactcomponents">Showing Chat Messages Using React Components</h2>
<p>We'll use three main components to make our chat app.</p>
<ol>
<li>A <code>Home</code> component which will manage sending and receiving messages and rendering the inner components.</li>
<li>A <code>Messages</code> component which will be a list of messages.</li>
<li>An <code>Input</code> component with a text field and button so that we can send our messages.</li>
</ol>
<p><code>create-next-app</code> has already made a <code>Home</code> component for you in <strong>src/pages/index.js</strong>, and we'll modify it later. But first, we'll create a <code>Messages</code> component.</p>
<p>Create a new file in your <strong>src/components</strong> folder called <strong>Messages.js</strong>. Create a new component in this file called <code>Messages</code> like in the following code:</p>
<pre><code class="language-javascript">import {useEffect, useRef} from 'react';
import React from 'react';
import styles from '@/styles/Home.module.css'

export default function Messages({messages, me}) {
  const bottomRef = useRef(null);
  useEffect(() =&gt; {
    if (bottomRef &amp;&amp; bottomRef.current) {
      bottomRef.current.scrollIntoView({behavior: 'smooth'});
    }
  });
  return (
    &lt;ul className={styles.messagesList}&gt;
      {messages.map(m =&gt; Message(m, me))}
      &lt;div ref={bottomRef}&gt;&lt;/div&gt;
    &lt;/ul&gt;
  );
}
</code></pre>
<p>The component will receive the messages it should display as a prop from the <code>Home</code>component. We'll render a list containing each message. The list items themselves will contain the sender's name and avatar, as well as the text of the message.</p>
<p>Looking at the code, you might be wondering what the <code>useEffect</code> function and <code>bottomRef</code> variables are.</p>
<p>When a new message comes in, you don't want to waste time scrolling through a sea of previous messages to the bottom of the chat each time. Instead, your app should automatically scroll down to the latest message whenever a new one pops in. You can do that with <code>bottomRef</code>, a reference to an empty div below the latest message.</p>
<p>The way you do this in React is with the <code>useEffect</code> function. With it, you register a function that gets called each time the component is rendered. In the function, you tell React to scroll down to the previously mentioned <code>div</code>, effectively scrolling all the way to the last message.</p>
<p><code>Messages</code> returns a list of <code>Message</code> components, which is not yet implemented. So, our next step is implementing it by adding the following code to the bottom of the file:</p>
<pre><code class="language-javascript">function Message({member, data, id}, me) {
  // 1
  const {username, color} = member.clientData;
  // 2
  const messageFromMe = member.id === me.id;
  const className = messageFromMe ?
    `${styles.messagesMessage} ${styles.currentMember}` : styles.messagesMessage;
  // 3
  return (
    &lt;li key={id} className={className}&gt;
      &lt;span
        className={styles.avatar}
        style={{backgroundColor: color}}
      /&gt;
      &lt;div className={styles.messageContent}&gt;
        &lt;div className={styles.username}&gt;
          {username}
        &lt;/div&gt;
        &lt;div className={styles.text}&gt;{data}&lt;/div&gt;
      &lt;/div&gt;
    &lt;/li&gt;
  );
}
</code></pre>
<p>This will render the JSX for each individual message as follows:</p>
<ol>
<li>Each message is linked to a specific <code>member</code> (user), and every member is identified by an ID, username, avatar, and a personalized color.</li>
<li>Next, you check whether the message came from you or another user. This distinction is helpful because it allows us to display our own messages on the left side.</li>
<li>Finally, you construct JSX for the component. The JSX shows the user's avatar, their name, and the message's content, all stored inside the passed arguments.</li>
</ol>
<p>Now that we have the <code>Messages</code> component, we need to make sure the <code>Home</code> component actually renders it. Head over to <strong>src/pages/index.js</strong> and add an import of the <code>Messages</code> component to the top of the file:</p>
<pre><code class="language-javascript">import Messages from '@/components/Messages'
</code></pre>
<p>Earlier, we mentioned that we would show usernames and avatars in our app. Typically, you would have a login screen where you would get this information.</p>
<p>However, for the sake of this tutorial, we'll generate random names and colors for the avatars. You can do this by adding the following top-level functions to the top of the file:</p>
<pre><code class="language-javascript">function randomName() {
  const adjectives = [
    'autumn', 'hidden', 'bitter', 'misty', 'silent', 'empty', 'dry', 'dark',
    'summer', 'icy', 'delicate', 'quiet', 'white', 'cool', 'spring', 'winter',
    'patient', 'twilight', 'dawn', 'crimson', 'wispy', 'weathered', 'blue',
    'billowing', 'broken', 'cold', 'damp', 'falling', 'frosty', 'green', 'long',
    'late', 'lingering', 'bold', 'little', 'morning', 'muddy', 'old', 'red',
    'rough', 'still', 'small', 'sparkling', 'shy', 'wandering',
    'withered', 'wild', 'black', 'young', 'holy', 'solitary', 'fragrant',
    'aged', 'snowy', 'proud', 'floral', 'restless', 'divine', 'polished',
    'ancient', 'purple', 'lively', 'nameless'
  ];
  const nouns = [
    'waterfall', 'river', 'breeze', 'moon', 'rain', 'wind', 'sea', 'morning',
    'snow', 'lake', 'sunset', 'pine', 'shadow', 'leaf', 'dawn', 'glitter',
    'forest', 'hill', 'cloud', 'meadow', 'sun', 'glade', 'bird', 'brook',
    'butterfly', 'bush', 'dew', 'dust', 'field', 'fire', 'flower', 'firefly',
    'feather', 'grass', 'haze', 'mountain', 'night', 'pond', 'darkness',
    'snowflake', 'silence', 'sound', 'sky', 'shape', 'surf', 'thunder',
    'violet', 'water', 'wildflower', 'wave', 'water', 'resonance', 'sun',
    'wood', 'dream', 'cherry', 'tree', 'fog', 'frost', 'voice', 'paper', 'frog',
    'smoke', 'star'
  ];
  const adjective = adjectives[Math.floor(Math.random() * adjectives.length)];
  const noun = nouns[Math.floor(Math.random() * nouns.length)];
  return adjective + noun;
}

function randomColor() {
  return '#' + Math.floor(Math.random() * 0xFFFFFF).toString(16);
}
</code></pre>
<p>As we've said, each user will have a username and color. The usernames are generated by adding a random adjective to a noun, leading to cool-sounding names like &quot;hiddensnow&quot; or &quot;wanderingfrog&quot;. The colors are assigned by generating a random hex number.</p>
<p>Now that we have those functions, we can use them to set our initial state in the <code>Home</code> component. Add the following to the top of <code>Home</code>:</p>
<pre><code class="language-javascript">const [messages, setMessages] = useState([{
  id: '1',
  data: 'This is a test message!',
  member: {
    id: '1',
    clientData: {
      color: 'blue',
      username: 'bluemoon',
    },
  },  
}]);
const [me, setMe] = useState({
  username: randomName(),
  color: randomColor(),
});
</code></pre>
<p>For the time being, our initial state will be one test message from a mysterious hard-coded user called <em>bluemoon</em>. As for our own identity, we'll use a randomly generated name and color.</p>
<p>Next, update the <code>return</code>  by adding the following inside the <code>&lt;div className={styles.appContent}&gt;</code> tag:</p>
<pre><code class="language-jsx">&lt;Messages messages={messages} me={me}/&gt;
</code></pre>
<p>This will allow you to display a list of chat messages, using the component you created earlier.</p>
<p>Run the app by navigating to the project folder in <strong>Terminal</strong> and typing in:</p>
<pre><code class="language-bash">npm run dev
</code></pre>
<p>Head over to <a href="http://localhost:3000/">localhost:3000</a> and take a look at the first message in your chat app!</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/09/chat-app-message-bubble.png" alt="Tutorial: Build a React.js Chat App"></p>
<h2 id="addingatextfieldinput">Adding a Text Field Input</h2>
<p>Now that we have a way to display our messages, we need a way to type them! You'll create a new React component called <code>Input</code> that contains a text field and a send button. The component itself won't send any messages, but will call a callback whenever someone clicks the send button.</p>
<p>Create a new file in the <strong>src/components</strong> folder called <strong>Input.js</strong>. In this file, add a new component called <code>Input</code>:</p>
<pre><code class="language-javascript">import React from 'react';
import { useEffect, useState } from 'react';
import styles from '@/styles/Home.module.css'

export default function Input({onSendMessage}) {
  const [text, setText] = useState('');
</code></pre>
<p>This component will have a text field to enter the message along with a button to send it. We'll keep track of the currently entered text in our <code>state</code>. Continue writing the component:</p>
<pre><code class="language-javascript">function onChange(e) {
  const text = e.target.value;
  setText(text);
}
</code></pre>
<p>This will trigger a render each time there's a change in the text field. Next, we need to handle sending the message, so add the following inner function:</p>
<pre><code class="language-javascript">function onSubmit(e) {
  e.preventDefault();
  setText('');
  onSendMessage(text);
}
</code></pre>
<p>To send the message, we'll use a callback inside the component's props which we'll receive from <code>Home</code>. We don't want the app to refresh every time a new message is sent, so we'll prevent the default behavior. We'll also update the state so that we clear the textfield.</p>
<p>Finally, it's time to connect the functions with the state and render everything using JSX:</p>
<pre><code class="language-javascript">  return (
    &lt;div className={styles.input}&gt;
      &lt;form onSubmit={e =&gt; onSubmit(e)}&gt;
        &lt;input
          onChange={e =&gt; onChange(e)}
          value={text}
          type='text'
          placeholder='Enter your message and press ENTER'
          autoFocus
        /&gt;
        &lt;button&gt;Send&lt;/button&gt;
      &lt;/form&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>Now <code>Input</code> is all done!</p>
<p>Just like we did with <code>Messages</code>, we need to make sure <code>App</code> will render <code>Input</code>. Head over to <strong>src/components/index.js</strong> and add the import for your new component to the top of the file:</p>
<pre><code class="language-javascript">import Input from '@/components/Input'
</code></pre>
<p>Next, update the contents of the <code>render</code> method to show the <code>Input</code> component:</p>
<pre><code class="language-javascript">&lt;Messages messages={messages} me={me}/&gt;
&lt;Input
  onSendMessage={onSendMessage}
/&gt;
</code></pre>
<p>Notice that you pass a callback (<code>onSendMessage</code>) to <code>Input</code>. <code>Input</code> will call this each time the user presses the send button. Finally, you'll implement that callback to &quot;send&quot; a message. Add the following function just above the <code>return</code> statement:</p>
<pre><code class="language-javascript">function onSendMessage(message) {
  const newMessage = {
    data: message,
    member: me
  }
  setMessages([...messages, newMessage])
}
</code></pre>
<p>For now, we'll just add it to the messages array in our <code>state</code>. Leter, we'll connect this to a web service to actually send messages back and forth.</p>
<p>If you head back to your web browser you should see an input field. Type in a message, hit Enter, and watch your message display in the list.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/09/sending-chat-messages-locally-in-react-1.png" alt="Tutorial: Build a React.js Chat App"></p>
<p>Look at this, your chat app is taking shape! Kudos to you!</p>
<h2 id="showingalistofonlinemembers">Showing a List of Online Members</h2>
<p>Before we connect everything to a web service, there is one more part of our chat UI that we'll tackle. In a group chat setting, it's useful to show a list of currently active users. We'll do that with a simple functional React component.</p>
<p>Create a new file in <strong>src/components</strong> called <strong>Members.js</strong>. There, you can add the following component:</p>
<pre><code class="language-javascript">import React from 'react';
import styles from '@/styles/Home.module.css'

export default function Members({members, me}) {
  return (
    &lt;div className={styles.members}&gt;
      &lt;div className={styles.membersCount}&gt;
        {members.length} user{members.length === 1 ? '' : 's'} online
      &lt;/div&gt;
      &lt;div className={styles.membersList}&gt;
        {members.map(m =&gt; Member(m, m.id === me.id))}
      &lt;/div&gt;
    &lt;/div&gt;);
}
</code></pre>
<p>This is a simple, stateless component that takes a list of members and the current member. Using those two pieces of data, it renders a list of <code>Member</code> components, which you'll add to the bottom of the file:</p>
<pre><code class="language-javascript">function Member({id, clientData}, isMe) {
  const {username, color} = clientData;
  return (
    &lt;div key={id} className={styles.member}&gt;
      &lt;div className={styles.avatar} style={{backgroundColor: color}}/&gt;
      &lt;div className={styles.username}&gt;{username} {isMe ? ' (you)' : ''}&lt;/div&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>This component shows the member as text, adding &quot;(you)&quot; to the end of the text if it's the current user's username.</p>
<p>Next, we'll show this component from <strong>src/pages/index.js</strong>. Head on over there and add a new import to the top of the file:</p>
<pre><code class="language-javascript">import Members from '@/components/Members'
</code></pre>
<p>Next, add a new state variable right after where <code>[messages, newMessage]</code> are declared:</p>
<pre><code class="language-javascript">const [members, setMembers] = useState([{
  id: &quot;1&quot;,
  clientData: {
    color: 'blue',
    username: 'bluemoon',
  },
}]);
</code></pre>
<p>This state variable will keep track of active members. For now, we'll hard-code a member. We'll fetch them from a web service in the next section.</p>
<p>Finally, add your new component to the JSX, right above <code>Messages</code>:</p>
<pre><code class="language-jsx">&lt;Members members={members} me={me}/&gt;
&lt;Messages messages={messages} me={me}/&gt;
</code></pre>
<p>Run the app again and you'll see <em>bluemoon</em> in the list of chat members!</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/09/member-list-in-chat-app.png" alt="Tutorial: Build a React.js Chat App"></p>
<p>Now, let's roll up our sleeves and connect everything to a real web service using Scaledrone.</p>
<h2 id="connectingtoscaledrone">Connecting to Scaledrone</h2>
<p>Initially, the idea of crafting a chat application that smoothly shuttles messages in real-time might seem a tad intimidating. Thankfully, Scaledrone steps in to streamline the business logic behind developing a chat app.</p>
<p>Before we dive into its usage, though, we have to ensure that Scaledrone is part of our app. Here's how.</p>
<p>Open <strong>src/pages/index.js</strong>. Inside the JSX <code>head</code> tag, add the following line to the bottom of the tag:</p>
<pre><code class="language-html">&lt;script type='text/javascript' src='https://cdn.scaledrone.com/scaledrone.min.js' /&gt;
</code></pre>
<blockquote>
<p><strong>Note</strong>: If you are using a plain <strong>index.html</strong> file, simply add the same <code>script</code> tag inside the <code>head</code> tag in the file.</p>
</blockquote>
<p>This will make sure Scaledrone's code is included in our app. We're almost ready to start using it, but we need to do one more thing first: create a Scaledrone channel.</p>
<p>To successfully connect to Scaledrone, you need to get your own channel ID from Scaledrone's dashboard. If you haven't already, head over to <a href="http://scaledrone.com/">Scaledrone.com</a> and register an account. Once you've done that, go to your dashboard and click the big green <strong>+Create Channel</strong> button to get started. You can choose <strong>Never require authentication</strong> for now. Note down the channel ID from the just created channel, you'll need it in a bit.</p>
<p>Head back to <strong>index.js</strong>. We'll add a function to connect to Scaledrone, but first we need to do a bit of prep work.</p>
<p>Add a new top-level object to the file:</p>
<pre><code class="language-javascript">let drone = null
</code></pre>
<p>We'll use this variable to store a global Scaledrone instance that we can access in the component.</p>
<p>Next, Scaledrone will need access to the current state, including messages, members and the current user. To allow this, we'll create a reference using <code>useRef</code> to each of those objects. Add the following code right below the definition of <code>[me, setMe]</code>:</p>
<pre><code class="language-javascript">const messagesRef = useRef();
messagesRef.current = messages;
const membersRef = useRef();
membersRef.current = members;
const meRef = useRef();
meRef.current = me;
</code></pre>
<p>This creates references to messages, members and the current user. It also makes sure the references are updated during each render. Now, we can use these references when we are processing Scaledrone events.</p>
<p>Add a new function to the top of the component to connect to Scaledrone:</p>
<pre><code class="language-javascript">function connectToScaledrone() {
  drone = new window.Scaledrone('YOUR-CHANNEL-ID', {
    data: meRef.current,
  });
  drone.on('open', error =&gt; {
    if (error) {
      return console.error(error);
    }
    meRef.current.id = drone.clientId;
    setMe(meRef.current);
  });
}
</code></pre>
<p>This will create a new instance of Scaledrone. Just remember to replace the string with the channel ID you got earlier. Since we're loading the script inside our HTML <code>head</code> tag, the script's contents will be attached to the global <code>window</code> object. That's where we'll ultimately fetch our Scaledrone instance from.</p>
<p>We'll also pass Scaledrone the data for the currently logged-in member. If you have additional data about the user or the client, this is a good way to supply that data, instead of having to send it with each message.</p>
<p>Moving on, we need to connect to a room. A room is a group of users that we can send messages to. You listen to those messages by subscribing to a room of a specific name. Add the following code to the end of the function you just created:</p>
<pre><code class="language-javascript">const room = drone.subscribe('observable-room');
</code></pre>
<p>We'll subscribe to a room.</p>
<blockquote>
<p><strong>Note</strong>: You might have noticed that we named our name Scaledrone room <code>observable-room</code>. You can name the room anything you want, a single user can actually connect to an infinite amount of rooms for all sorts of application scenarios. However, in order for messages to contain the info of the sender, you need to prefix the room name with <code>&quot;observable-&quot;</code>. <a href="https://www.scaledrone.com/docs/api-clients/observable-rooms">Read more...</a></p>
</blockquote>
<p>We also need to know when the messages arrive, so we'll subscribe to the &quot;message&quot; event on the room. Add the following code to the end of the constructor:</p>
<pre><code class="language-javascript">room.on('message', message =&gt; {
  const {data, member} = message;
  setMessages([...messagesRef.current, message]);
});
</code></pre>
<p>When we get a new message, we'll add the message's text as well as the client data to our state.</p>
<p>Similarly, we need to track the logged-in members:</p>
<pre><code class="language-javascript">room.on('members', members =&gt; {
  setMembers(members);
});
room.on('member_join', member =&gt; {
  setMembers([...membersRef.current, member]);
});
room.on('member_leave', ({id}) =&gt; {
  const index = membersRef.current.findIndex(m =&gt; m.id === id);
  const newMembers = [...membersRef.current];
  newMembers.splice(index, 1);
  setMembers(newMembers);
});
</code></pre>
<p>Scaledrone gives us the logged-in members when we join and then keeps us updated when someone else joins or leaves. We'll make sure to keep our state variables updated with the correct members.</p>
<p>Now you just need to make sure the function is called when the component loads. Add this code right below the function definition:</p>
<pre><code class="language-javascript">useEffect(() =&gt; {
  if (drone === null) {
    connectToScaledrone();
  }
}, []);
</code></pre>
<p>You can do a small check to make sure Scaledrone is not already connected and then call the function.</p>
<p>Next, we need to modify <code>onSendMessage</code> to publish a new message to all the other users in the room:</p>
<pre><code class="language-javascript">function onSendMessage(message) {
  drone.publish({
    room: 'observable-room',
    message
  });
}
</code></pre>
<p>Scaledrone makes this a simple matter of calling the <code>publish</code> function with the data.</p>
<p>Finally, we'll remove the test message from our app's <code>state</code> so that the initial state looks like the following:</p>
<pre><code class="language-javascript">const [messages, setMessages] = useState([]);
const [members, setMembers] = useState([]);
const [me, setMe] = useState({
  username: randomName(),
  color: randomColor(),
});
</code></pre>
<p>If you switch to your web browser at this point, you'll notice that the behavior remains the same: when you send a new message, it should appear in the list. The difference is that this time it's actually being sent to Scaledrone. Feel free to open the app in another tab and chat with yourself. :)</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/09/react-chat-app-web-service.png" alt="Tutorial: Build a React.js Chat App"></p>
<h2 id="addingatypingindicator">Adding a Typing Indicator</h2>
<p>When you use a chat app, you want to know what's happening on the other side of the conversation. <em>Has the person you're talking to read your message? Are they typing a reply?</em> These are some of the questions that chat apps can answer with features like ‘seen’ confirmation, typing indicator, and more.</p>
<p>Fear not; Scaledrone makes this a walk in the park. Let's see how it's done.</p>
<h3 id="creatingareacttypingindicatorcomponent">Creating a React Typing Indicator Component</h3>
<p>As you can assume, we'll now add a typing indicator. Ours will display a message like &quot;hiddenstar is typing.&quot;</p>
<p>The first step includes creating a new React component dedicated to these subtle yet informative dots. To begin, create a new file in <strong>src/components</strong> called <strong>TypingIndicator.js</strong>. Then, add the following code:</p>
<pre><code class="language-javascript">import React from 'react';
import styles from '@/styles/Home.module.css'

export default function({members}) {
  // 1
  const names = members.map(m =&gt; m.clientData.username);
  if (names.length === 0) {
    return &lt;div className={styles.typingIndicator}&gt;&lt;/div&gt;;
  }
  // 2
  if (names.length === 1) {
    return &lt;div className={styles.typingIndicator}&gt;{names[0]} is typing&lt;/div&gt;;
  }
	// 3
  const string = names.slice(0, -1).join(', ') + ' and ' + names.slice(-1);
  return &lt;div className={styles.typingIndicator}&gt;{string} are typing&lt;/div&gt;;
}
</code></pre>
<p>This component adds a small <code>div</code> that gets filled with text when someone is typing. You will pass a list of users that are currently typing to this component. Here's how it works:</p>
<ol>
<li>First, you get a list of usernames of all the members that are currently typing. If nobody is typing, an empty <code>div</code> without any text is returned.</li>
<li>If one person is typing, the <code>div</code> portrays their action, like &quot;mysteriousrabbit is typing.&quot;</li>
<li>In case more people are typing, their names are joined using a comma as a separator. The text then reads something like: &quot;mysteriousrabbit, coolfrog and bluecar are typing.&quot;</li>
</ol>
<p>Now that we have the component, we need code to show it on the website. Open <strong>src/pages/index.js</strong> and import your new component at the top of the file:</p>
<pre><code class="language-javascript">import TypingIndicator from '@/components/TypingIndicator'
</code></pre>
<p>Next, you have to modify the JSX to show the typing indicator right under the <code>Messages</code> component, like so:</p>
<pre><code class="language-javascript">&lt;Messages messages={messages} me={me}/&gt;
&lt;TypingIndicator members={members.filter(m =&gt; m.typing &amp;&amp; m.id !== me.id)}/&gt;
&lt;Input
  onSendMessage={onSendMessage}
  onChangeTypingState={onChangeTypingState}
/&gt;
</code></pre>
<p>The code above shows the new component and passes it a list of users that are typing by checking the <code>typing</code> property.</p>
<p>Your web app will now show a textual sign whenever a user is composing a message.</p>
<h3 id="trackingiftheuseriscurrentlytyping">Tracking if the User is Currently Typing</h3>
<p>However, at this moment, the typing state of users is not being tracked. The situation isn't as clear as merely <em>typing/not typing</em>, either.</p>
<p>People might start typing a message and then suddenly get distracted—like deciding to make themselves a cup of coffee—leaving the message halfway written. On the flip side, if you trigger the typing indicator each time someone takes a moment to find the perfect emoji, well, that might just annoy your users.</p>
<p>And here is where Scaledrone and its optional library called <a href="https://www.npmjs.com/package/typing-indicator">Typing Indicator</a> step in to handle all of this for you.</p>
<blockquote>
<p>Note: While <a href="https://www.npmjs.com/package/typing-indicator">Typing Indicator</a> is created by and for Scaledrone, it is framework-agnostic and works with any JavaScript application or website.</p>
</blockquote>
<p>To install typing indicator, navigate to the project folder in <strong>Terminal</strong> and type in:</p>
<pre><code class="language-bash">npm i typing-indicator
</code></pre>
<p>This little command will install the library.</p>
<p>You'll track the typing state from <strong>src/components/Input.js</strong>, so navigate there and import the library at the top of the file:</p>
<pre><code>import TypingIndicator from 'typing-indicator';

let typingIndicator = null;
</code></pre>
<p>You also create a top-level variable to hold an instance of the typing indicator, accessible across re-renders of the component.</p>
<p>Next, change the definition of <code>Input</code> to include a new parameter:</p>
<pre><code class="language-javascript">export default function Input({onSendMessage, onChangeTypingState}) {
</code></pre>
<p><code>onChangeTypingState</code> is a callback that you'll define in <strong>index.js</strong>, but <code>Input</code> will call it when the current user changes their typing state.</p>
<p>Then, you'll add a new typing indicator when the component loads by adding the following code inside the component, right below where the state is declared:</p>
<pre><code class="language-javascript">useEffect(() =&gt; {
  if (typingIndicator === null) {
    typingIndicator = new TypingIndicator();
    typingIndicator.listen(isTyping =&gt; onChangeTypingState(isTyping));
  }
}, []);
</code></pre>
<p>When this component loads, you will register a callback to get called whenever a user begins or stops typing.</p>
<p>Next, we need to tell the typing indicator when the text changes, so update <code>onChange</code> to the following:</p>
<pre><code class="language-javascript">function onChange(e) {
  const text = e.target.value;
  typingIndicator.onChange(text);
  setText(text);
}
</code></pre>
<p>The typing indicator does the heavy lifting of tracking typing states for you.</p>
<p>All you need to do is connect it to a function, which, in this case, is <code>onChangeTypingState</code>.</p>
<p>Now, let's wrap things up in <strong>src/pages/index.js</strong> by passing the callback in. Navigate there and, first, import your new component at the top of the file:</p>
<pre><code class="language-javascript">import TypingIndicator from '@/components/TypingIndicator'
</code></pre>
<p>Then, modify the <code>Input</code> declaration inside the JSX to the following:</p>
<pre><code class="language-jsx">&lt;TypingIndicator members={members.filter(m =&gt; m.typing &amp;&amp; m.id !== me.id)}/&gt;
&lt;Input
  onSendMessage={onSendMessage}
  onChangeTypingState={onChangeTypingState}
/&gt;
</code></pre>
<p>Alongside showing your new component with members that are currently typing, this will ensure that <code>Input</code> calls <code>onChangeTypingState</code>, which you'll implement next.</p>
<h3 id="sendingandreceivingthetypingstate">Sending and Receiving the Typing State</h3>
<p>Right now, you are passing <code>onChangeTypingState</code> to <code>Input</code> but you haven't yet created the function. The function needs to send a message to Scaledrone, letting other users know that the current user is typing.</p>
<p>To continue, add the following function above the <code>return</code> statement:</p>
<pre><code class="language-javascript">function onChangeTypingState(isTyping) {
  drone.publish({
    room: 'observable-room',
    message: {typing: isTyping}
  });
}
</code></pre>
<p><code>onChangeTypingState</code> receives a boolean that indicates whether the user is typing or not. Using Scaledrone, you'll publish a new message with the updated typing state whenever it changes.</p>
<p>Next, we need to process the incoming typing messages and show the typing indicator. You'll do this in the <code>connectToScaledrone</code>. Modify the call to <code>room.on('message', ...)</code> to the following:</p>
<pre><code class="language-javascript">room.on('message', message =&gt; {
  const {data, member} = message;
  if (typeof data === 'object' &amp;&amp; typeof data.typing === 'boolean') {
    const m = membersRef.current.find(m =&gt; m.id === member.id);
    m.typing = data.typing;
    setMembers(membersRef.current);
  } else {
    setMessages([...messagesRef.current, message]);
  }
});
</code></pre>
<p>Here's the breakdown of what this code does:</p>
<p>The incoming message type is checked. If the message is about a typing state, the code finds the members that are typing and changes their <code>typing</code> property to what was passed in the message. If the message isn't about typing, it's treated as a regular message and added to <code>state.messages</code>.</p>
<p>So, with these changes, you can track the typing state of users and send it to Scaledrone. When you receive a typing message from Scaledrone, you will set the <code>typing</code> property on the user. As the last step, the <code>TypingIndicator</code> component will read that state and show a typing indicator for those users for whom <code>typing</code> is true.</p>
<p>Head back to your browser to see the typing indicator in action!</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/09/react-chat-app-scaledrone.png" alt="Tutorial: Build a React.js Chat App"></p>
<h2 id="andweredone">And We're Done!</h2>
<p>And voilà! You've just built your very own React chat app, and with the magic of Scaledrone, you've made it look easy. No more fussing over the backend—you got to focus on creating a sleek and functional user interface that's ready to impress.</p>
<p>With Scaledrone, you get to focus on making your UI beautiful, and on the functionality of your app, without worrying about the back end. <strong>You can find the full source code on <a href="https://github.com/ScaleDrone/react-chat-tutorial">GitHub</a>.</strong></p>
<p>Of course, there can be much more to your chat app. If you'd like to build on this app, consider these feature ideas to implement using Scaledrone's APIs:</p>
<ul>
<li>Sending images and attachments</li>
<li>Authentication</li>
<li>Replies to specific messages</li>
<li>Stickers and GIFs</li>
</ul>
<p>Sounds exciting, right?</p>
<p>And if you're ready to explore further, you can find the full source code or run the working prototype <a href="https://github.com/ScaleDrone/react-chat-tutorial">on GitHub</a>. For any questions or feedback, feel free to <a href="https://www.scaledrone.com/contact">contact us</a>; we'd love to hear from you.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Crypto Tracker Tutorial with Real-Time Charts]]></title><description><![CDATA[<div class="kg-card-markdown"><p>Today's cryptocurrency market is growing rapidly. Adding a real-time crypto tracker to your website could be beneficial. Not only could it attract new users, but it could also keep your current users engaged.</p>
<p>If you're a developer seeking to broaden your skills, this tutorial could be a valuable resource. It's</p></div>]]></description><link>https://www.scaledrone.com/blog/real-time-crypto-tracker-with-real-time-charts-tutorial/</link><guid isPermaLink="false">64e998481f45d82ebb05b91d</guid><category><![CDATA[Tutorials]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Shanika Wickramasinghe]]></dc:creator><pubDate>Sat, 26 Aug 2023 11:03:44 GMT</pubDate><media:content url="https://www.scaledrone.com/blog/content/images/2023/08/CleanShot-2023-08-26-at-13.44.34@2x.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://www.scaledrone.com/blog/content/images/2023/08/CleanShot-2023-08-26-at-13.44.34@2x.png" alt="Crypto Tracker Tutorial with Real-Time Charts"><p>Today's cryptocurrency market is growing rapidly. Adding a real-time crypto tracker to your website could be beneficial. Not only could it attract new users, but it could also keep your current users engaged.</p>
<p>If you're a developer seeking to broaden your skills, this tutorial could be a valuable resource. It's not just about creating a real-time crypto tracker. You'll also learn how to incorporate charts into your application.</p>
<p>To make the most of it, I suggest you try coding along with the tutorial.</p>
<h2 id="whatarewegoingtobuild">What are we going to build?</h2>
<p>Let's first understand what we're aiming to create from today's tutorial.</p>
<p>Our app will feature two pages. The first page, our homepage, will showcase a list of today's trending coins. It will display seven coins, each with its image and market cap compared to Bitcoin.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/08/Crypto-Tracker.png" alt="Crypto Tracker Tutorial with Real-Time Charts"></p>
<p>Moreover, it will have a search option, allowing users to find any coin.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/08/Crypto-Tracker-Search.png" alt="Crypto Tracker Tutorial with Real-Time Charts"></p>
<p>When a user clicks on a specific coin, they will be redirected to the second page. Here, they'll find comprehensive details about the chosen coin and three types of graphs. These graphs will show the coin's value over time.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/08/Bitcoin-chart.png" alt="Crypto Tracker Tutorial with Real-Time Charts"></p>
<p>This might seem challenging at first, but once you start working on it, you'll see how easy it can be. Now, let's explore the technologies we'll be using in this tutorial.</p>
<h2 id="technologiesused">Technologies used</h2>
<p>We're using React, a frontend library, to design our user interface.</p>
<p><a href="https://react.dev/learn">React</a> is a JavaScript library developed by Facebook. Its purpose is to build user interfaces. It allows developers to create reusable UI components. This makes building and maintaining complex user interfaces simpler. React's component-based design ensures efficient rendering of dynamic, interactive content.</p>
<p>In addition to React, we're using a special API called <a href="https://www.coingecko.com/">Coingeko API</a> to fetch coin data. This API is a strong, user-friendly tool. Developers can use it to access cryptocurrency data. It provides real-time and historical information on various cryptocurrencies. This includes prices, market capitalization, and trading volumes. The API integrates easily into apps and services. Users can stay updated on the latest crypto market developments.</p>
<p>Lastly, we're using a library called <a href="https://recharts.org/en-US">ReCharts</a> to create charts in our app. ReCharts is a simple, lightweight JavaScript library. Developers can use it to make attractive, interactive charts for web pages. It supports various chart types like line, bar, pie, and more. This lets you visualize data easily. With ReCharts, adding dynamic charts to your web apps is simple and hassle-free.</p>
<p>That's it for the technologies. Now, let's start building our application without any further delay.</p>
<h2 id="creatingthereactappandinstallingthedependencies">Creating the React app and Installing the Dependencies</h2>
<p>First of all, we need to create a React application for this project. You might have used 'create-react-app' to build React applications in the past. If that's the case, you're in for a treat with this tutorial because we'll be learning something new and improved. Instead of using 'create-react-app', we'll use <a href="https://vitejs.dev/guide/">Vite</a> to build our React app. Vite offers several benefits over <code>create-react-app</code>. Those are</p>
<ul>
<li>
<p>Efficient Development Server: Vite includes its own development server, offering faster reloads and better performance.</p>
</li>
<li>
<p>No Bundling during Development: Vite, unlike CRA, doesn't bundle code during development, enabling smoother and quicker development.</p>
</li>
<li>
<p>Optimized Production Build: Vite uses Rollup for production builds, leading to smaller bundles and better loading times.</p>
</li>
<li>
<p>Configurable: Vite provides more configuration options, giving experienced developers more control over their projects.</p>
</li>
</ul>
<h2 id="howtocreateareactapplicationusingvite">How to create a React application using Vite?</h2>
<p>First, open up your terminal and type the following command:</p>
<pre><code class="language-bash">npm create vite@latest
</code></pre>
<p>This will prompt a series of messages in your terminal.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/08/terminal-npm-create-vite@latest.png" alt="Crypto Tracker Tutorial with Real-Time Charts"></p>
<p>Next, you'll need to provide a name for your project. Let's name it &quot;frontend&quot; (or choose any other name you prefer).</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/08/vite-select-framework.png" alt="Crypto Tracker Tutorial with Real-Time Charts"></p>
<p>After naming your project, you'll be presented with several options. You can navigate through these options using the up and down arrow keys on your keyboard. Find &quot;React&quot; and press enter to create the application with React.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/08/vite-select-framework-2.png" alt="Crypto Tracker Tutorial with Real-Time Charts"></p>
<p>Next, you'll need to select &quot;JavaScript&quot;. We are using ReactJs in this tutorial, so press enter to confirm your selection.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/08/vite-done.png" alt="Crypto Tracker Tutorial with Real-Time Charts"></p>
<p>After the installation process, you'll be prompted to navigate to the newly created project folder and install all necessary dependencies for your React application.</p>
<p>To do this, first navigate to the &quot;frontend&quot; folder by typing the following command:</p>
<pre><code class="language-bash">cd frontend
</code></pre>
<p>Then install the dependencies by typing</p>
<pre><code class="language-bash">npm install
</code></pre>
<p>Once the installation is finished, type npm run dev and click on the provided link. This will take you to your new React application.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/08/vite-run-dev.png" alt="Crypto Tracker Tutorial with Real-Time Charts"></p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/08/vite-react-landing-page.png" alt="Crypto Tracker Tutorial with Real-Time Charts"></p>
<p>And that's it, we have now created the React application!</p>
<h3 id="installingthedependencies">Installing the Dependencies</h3>
<p>While the React application is running in the current terminal, let's open a new terminal to install all our dependencies.</p>
<p>Navigate to your terminal and type.</p>
<pre><code class="language-bash">npm install react-router-dom axios sass zustand
</code></pre>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/08/npm-install.png" alt="Crypto Tracker Tutorial with Real-Time Charts"></p>
<ul>
<li>react-router-dom: This package allows navigation between different views and components in a single-page React application. It helps manage routes and handle different URLs.</li>
<li>axios: Axios is a JavaScript library for making HTTP requests. It simplifies sending and handling asynchronous HTTP requests.</li>
<li>sass: SASS, a CSS preprocessor, enhances CSS with features like variables, nested rules, and mixins. It aids in writing maintainable, organized CSS code for complex projects.</li>
<li>zustand: Zustand is a state management library for React. It provides a simple, minimalistic approach to managing application state, reducing the need for complex setups.</li>
</ul>
<h2 id="creatingthefolderstructure">Creating the folder structure</h2>
<p>We won't be using the App.jsx, App.css, and index.css files. So, you can remove them from your 'src' folder.</p>
<p>We will have a 'components' folder to store all our components, a 'store' folder to manage our global states, and a 'pages' folder to organize our two pages.</p>
<p>Next, create a file named Home.js in the 'pages' folder. This will serve as our homepage, featuring the trending coins and other elements we discussed earlier.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/08/folder-structure.png" alt="Crypto Tracker Tutorial with Real-Time Charts"></p>
<p>Then well type rfc and press tab to create this code snippet.</p>
<pre><code class="language-javascript">import React from 'react';

export default function Home() {
  return (
    &lt;div&gt;
      Home
    &lt;/div&gt;
  );
}
</code></pre>
<p>This is an extension in vs code that is really useful. The extension is called ES7+ React/Redux/React-Native snippets. Install it and you are good to go.</p>
<p>Then Let’s create the other page where there are the information and the charts related to a single coin.</p>
<p>Create a new file in pages and name it as Show.jsx. Go to the file and hit rfc then tab. You’ll end up with something like this.</p>
<pre><code class="language-javascript">import React from 'react';

export default function Show() {
  return (
    &lt;div&gt;
      Show
    &lt;/div&gt;
  );
}
</code></pre>
<h2 id="creatingroutes">Creating Routes</h2>
<p>Let's go to main.jsx and set up the routes for our application.</p>
<p>First, clear everything in main.jsx and paste the following code</p>
<pre><code class="language-javascript">import ReactDOM from &quot;react-dom/client&quot;;
import { BrowserRouter, Route, Routes } from &quot;react-router-dom&quot;;
import Home from &quot;./pages/Home&quot;;
import Show from &quot;./pages/Show&quot;;

ReactDOM.createRoot(document.getElementById(&quot;root&quot;)).render(
  &lt;BrowserRouter&gt;
    &lt;Routes&gt;
      &lt;Route path=&quot;/&quot; element={&lt;Home /&gt;} /&gt;
      &lt;Route path=&quot;/:id&quot; element={&lt;Show /&gt;} /&gt;
    &lt;/Routes&gt;
  &lt;/BrowserRouter&gt;
);
</code></pre>
<p>In this code, we've set up two routes for our application. If the route is '<a href="http://localhost:5173/">http://localhost:5173/</a>', the Home page will be displayed. If the route is '<a href="http://localhost:5173/id">http://localhost:5173/id</a>', the Show page will be displayed. It's as simple as that.</p>
<h3 id="fetchingdatafromthecoingeckoapi">Fetching Data from the CoinGecko API</h3>
<p>In this step, we'll create a state store for our Home page using Zustand, a state management library. Zustand allows us to manage the states of our application globally. This means we can use and modify these states in any component across the application by simply importing the store.</p>
<p>In the context of our application, the state we are interested in is the list of trending coins, which we represent as an array of coin objects.</p>
<p>Start by creating a new file homeStore.jsx inside the 'store' folder and add the following code.</p>
<pre><code class="language-javascript">import { create } from &quot;zustand&quot;;
import axios from &quot;axios&quot;;

const homeStore = create((set) =&gt; ({
  coins: [],
  
  fetchCoins: async () =&gt; {
    const res = await axios.get(&quot;https://api.coingecko.com/api/v3/search/trending&quot;);
    const coins = res.data.coins.map((coin) =&gt; {
      return {
        id: coin.item.id,
        name: coin.item.name,
        image: coin.item.large,
        priceBtc: coin.item.price_btc.toFixed(10),
      };
    });
    
    console.log(coins);
    set({ coins });
  },
}));

export default homeStore;
</code></pre>
<p>As you can see our coins have properties such as name, image and price against BTC.</p>
<p>We also define an asynchronous function fetchCoins inside the store. This function is responsible for fetching the list of trending coins from the CoinGecko API and updating our coins state with the retrieved data.</p>
<h4 id="displayingcoinsonthehomepage">Displaying Coins on the Home Page</h4>
<p>Once we have our store set up, we can import it into Home.js and use the state and functions defined in the store.</p>
<p>Here is the updated Home.js code</p>
<pre><code class="language-javascript">import React, { useEffect } from &quot;react&quot;;
import homeStore from &quot;../store/homeStore&quot;;
import { Link } from &quot;react-router-dom&quot;;

export default function Home() {
  const store = homeStore();

  useEffect(() =&gt; {
    store.fetchCoins();
  }, []);

  return (
    &lt;div&gt;
      {store.coins.map((coin) =&gt; {
        return (
          &lt;div key={coin.id}&gt;
            &lt;div&gt;{coin.name}&lt;/div&gt;
          &lt;/div&gt;
        );
      })}
    &lt;/div&gt;
  );
}
</code></pre>
<p>In this code,</p>
<ul>
<li>
<p>We import homeStore and use it to get access to our coins state and fetchCoins function.</p>
</li>
<li>
<p>Within a useEffect hook, we call fetchCoins. This ensures that fetchCoins is executed once when the component mounts, which fetches the list of trending coins from the API and updates our coins state.</p>
</li>
<li>
<p>In the return statement of the component, we map over the coins array to display each coin's name on the page.</p>
</li>
</ul>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/08/plain-crypto-list.png" alt="Crypto Tracker Tutorial with Real-Time Charts"></p>
<h3 id="implementingsearchfunctionality">Implementing Search Functionality</h3>
<p>In this section, we will add a search functionality to our application, allowing users to search for a specific cryptocurrency coin. The goal is to display the search results in real-time as the user types into the search bar. To avoid excessive API calls and enhance user experience, we'll implement a debounce function. This function will delay the execution of the search until the user has stopped typing for a certain period of time.</p>
<h4 id="creatingthedebouncefunction">Creating the Debounce Function</h4>
<p>A debounce function is used to prevent a particular function from being called immediately. Instead, it ensures the function only gets called after the user has stopped invoking the function for a specific amount of time. In our case, we will use this function to delay the API call until the user has stopped typing.</p>
<p>Create a new folder inside the 'src' folder named 'helpers', and create a new file inside it named 'debounce.js'. Add the following code in 'debounce.js'.</p>
<pre><code class="language-javascript">export default function debounce(func, wait, immediate) {
  var timeout;

  return function () {
    var context = this,
      args = arguments;

    var later = function () {
      timeout = null;
      if (!immediate) func.apply(context, args);
    };

    var callNow = immediate &amp;&amp; !timeout;

    clearTimeout(timeout);
    timeout = setTimeout(later, wait);

    if (callNow) func.apply(context, args);
  };
}
</code></pre>
<p>You can learn more about debounce functions online for a deeper understanding. For now, just know that it helps us limit the rate of execution of our search function.</p>
<h4 id="updatingthehomestore">Updating the Home Store</h4>
<p>Let's update our homeStore.js file to include the search functionality.</p>
<pre><code class="language-javascript">import { create } from &quot;zustand&quot;;
import axios from &quot;axios&quot;;
import debounce from &quot;../helpers/debounce&quot;; 

const homeStore = create((set) =&gt; ({
  coins: [],
  trending: [],
  query: &quot;&quot;,

  setQuery: (e) =&gt; {
    set({ query: e.target.value });
    homeStore.getState().searchCoins();
  },
  
  searchCoins: debounce(async () =&gt; {
    const { query, trending } = homeStore.getState();

    if (query.length &gt; 2) {
      const res = await axios.get(
        `https://api.coingecko.com/api/v3/search?query=${query}`
      );
      const coins = res.data.coins.map((coin) =&gt; {
        return {
          name: coin.name,
          image: coin.large,
          id: coin.id,
        };
      });
      set({ coins });
    } else {
      set({ coins: trending });
    }
  }, 500),
  
  fetchCoins: async () =&gt; {
    const res = await axios.get(
      &quot;https://api.coingecko.com/api/v3/search/trending&quot;
    );  
    const coins = res.data.coins.map((coin) =&gt; {
      return {
        id: coin.item.id,
        name: coin.item.name,
        image: coin.item.large,
        priceBtc: coin.item.price_btc.toFixed(10),
      };
    });
    console.log(coins);
    set({ coins, trending: coins });
  },
}));

export default homeStore;

</code></pre>
<p>In this revised code, we've introduced two new state variables - trending and query. The former keeps track of the list of trending coins, while the latter captures the user's search input.</p>
<p>The setQuery function has been enhanced to not only take the user's input as an argument and update the query state but also to trigger the searchCoins function. The real magic happens in the searchCoins function, which is now enveloped by a debounce function with a 500 millisecond delay. This function checks the length of the user's search query. If the query is more than two characters, it sends an API request to fetch coins that match the query. On the contrary, if the query is two or fewer characters, it reverts the coins state back to trending, showing the trending coins.</p>
<p>Finally, the fetchCoins function has been updated to simultaneously set both coins and trending states with the fetched list of trending coins. This allows for the trending array to reset the coins array when needed.</p>
<h4 id="updatingthefrontend">Updating the Frontend</h4>
<p>Finally, to tie the search functionality to the frontend, we need to update the input box in our component like this.</p>
<pre><code class="language-javascript">&lt;input
  type=&quot;text&quot;
  placeholder=&quot;Search&quot;
  value={store.query}
  onChange={store.setQuery}
/&gt;
</code></pre>
<p>Here, value={store.query} binds the input box's value to the query state in homeStore. When the value of the input box changes (i.e., the user types something), we call the store.setQuery function to update our query state and initiate a search.</p>
<h2 id="creatingthecoininfopage">Creating the Coin Info page</h2>
<p>In this next phase, we aim to construct a page displaying detailed information about a chosen coin. This information will be available once a coin is clicked.</p>
<h3 id="constructingtheshowstorejsx">Constructing the showStore.jsx</h3>
<p>To start, we'll create another store named showStore.jsx to handle data specific to this page. The following code will be included in this store.</p>
<pre><code class="language-javascript">import { create } from &quot;zustand&quot;;
import axios from &quot;axios&quot;;

const showStore = create((set) =&gt; ({
 graphData: [],
 fetchData: async (id) =&gt; {
   const [graphsRes, dataRes] = await Promise.all([
     axios.get(
       `https://api.coingecko.com/api/v3/coins/${id}/market_chart?vs_currency=usd&amp;days=100&amp;interval=daily&amp;precision=2`
     ),
     axios.get(
       `https://api.coingecko.com/api/v3/coins/${id}?localization=true&amp;tickers=true&amp;market_data=true&amp;community_data=true&amp;developer_data=true&amp;sparkline=true`
     ),
   ]);

   const graphData = graphsRes.data.prices.map((price) =&gt; {
     const [timeStamp, priceValue] = price;
     const date = new Date(timeStamp).toLocaleDateString(&quot;en-us&quot;);
     return {
       Date: date,
       Price: priceValue,
     };
   });

   console.log(dataRes);

   set({
     data: dataRes.data, // Set the fetched data to `store.data`
     graphData,
   });
 },
}));

export default showStore;
</code></pre>
<p>This page intends to display two types of data.</p>
<ul>
<li>
<p>A graph representing the coin's price over time</p>
</li>
<li>
<p>Basic information about the selected coin</p>
</li>
</ul>
<p>We need two API calls to retrieve this information. These requests are simultaneously made using Promise.all() to optimize loading time.</p>
<p>The fetchData function is responsible for making these API calls and storing the results in the graphData array and data object.</p>
<p>For the graph, we use the 'market_chart' endpoint to get price and date data. We then map over the graphsRes.data.prices array to transform the timestamp into a more readable date format and construct the graphData array.</p>
<h3 id="installingandusingrecharts">Installing and Using Recharts</h3>
<p>To render the charts on our page, we'll use a React chart library called <a href="https://recharts.org/en-US/guide/installation">Recharts</a>. This can be installed by running the following command in your terminal.</p>
<pre><code class="language-bash">npm install recharts
</code></pre>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/08/pm-install-recharts.png" alt="Crypto Tracker Tutorial with Real-Time Charts"></p>
<p>In our show.jsx page, we plan to show three different charts from which the user can choose to view any at a given time.</p>
<h2 id="whatischackrauiandhowtouseitinourreactapp">What is chackraUI and how to use it in our react app?</h2>
<p>Chakra UI is a robust, user-friendly library of React components that can greatly enhance your React project. It provides a comprehensive set of customizable, accessible UI components that allow developers to quickly build beautiful and responsive user interfaces. Chakra UI emphasizes on accessibility, ensuring your web app is usable by all users, including those with disabilities. Let's discuss how to install and implement Chakra UI in your React project.</p>
<h3 id="installingchakraui">Installing Chakra UI</h3>
<p>To install Chakra UI, open your terminal and type the following.</p>
<pre><code class="language-bash">npm i @chakra-ui/react @emotion/react @emotion/styled framer-motion
</code></pre>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/08/npm-install-2.png" alt="Crypto Tracker Tutorial with Real-Time Charts"></p>
<h3 id="implementingchakrauiinyourproject">Implementing Chakra UI in Your Project</h3>
<p>After installing Chakra UI, it's time to incorporate it into your project. We need to wrap our entire application with the <chakraprovider> component. Here's an example of how to do this.</chakraprovider></p>
<pre><code class="language-javascript">import ReactDOM from &quot;react-dom/client&quot;;
import { BrowserRouter, Route, Routes } from &quot;react-router-dom&quot;;
import Home from &quot;./pages/Home&quot;;
import Show from &quot;./pages/Show&quot;;
import { ChakraProvider } from &quot;@chakra-ui/react&quot;;  

ReactDOM.createRoot(document.getElementById(&quot;root&quot;)).render(
  &lt;BrowserRouter&gt;
    &lt;ChakraProvider&gt;
      &lt;Routes&gt;
        &lt;Route path=&quot;/&quot; element={&lt;Home /&gt;} /&gt;
        &lt;Route path=&quot;/:id&quot; element={&lt;Show /&gt;} /&gt;
      &lt;/Routes&gt;
    &lt;/ChakraProvider&gt;
  &lt;/BrowserRouter&gt;
);

</code></pre>
<p>In the code snippet above, we imported ChakraProvider and wrapped the routes inside it. With this setup, you're ready to use Chakra UI's components in your project.</p>
<h3 id="workingwiththetabscomponentinchakraui">Working with the Tabs Component in Chakra UI</h3>
<p>Here's an example of how to use the Tabs component in Chakra UI.</p>
<pre><code class="language-javascript">&lt;Tabs&gt;
  &lt;TabList&gt;
    &lt;Tab&gt;Line Chart&lt;/Tab&gt;
    &lt;Tab&gt;Bar Chart&lt;/Tab&gt;
    &lt;Tab&gt;Area Chart&lt;/Tab&gt;
  &lt;/TabList&gt;
  &lt;TabPanels&gt;
    &lt;TabPanel&gt;
      &lt;p&gt;one!&lt;/p&gt;
    &lt;/TabPanel&gt;
    &lt;TabPanel&gt;
      &lt;p&gt;two!&lt;/p&gt;
    &lt;/TabPanel&gt;
    &lt;TabPanel&gt;
      &lt;p&gt;three!&lt;/p&gt;
    &lt;/TabPanel&gt;
  &lt;/TabPanels&gt;
&lt;/Tabs&gt;;

</code></pre>
<p>In this example, the Tabs component contains multiple Tab and TabPanel components. Each Tab corresponds to a TabPanel.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/08/tabs.png" alt="Crypto Tracker Tutorial with Real-Time Charts"></p>
<h3 id="incorporatinggraphsintothetabpanels">Incorporating Graphs into the Tab Panels</h3>
<p>To visualize the data, we can incorporate different types of charts within the TabPanel components. The following code snippet demonstrates how to do this.</p>
<pre><code class="language-javascript">import { useEffect } from &quot;react&quot;;
import showStore from &quot;../store/showStore&quot;;
import { useParams } from &quot;react-router-dom&quot;;
import { Tab, TabList, TabPanel, TabPanels, Tabs } from &quot;@chakra-ui/react&quot;;
import {
  XAxis,
  YAxis,
  CartesianGrid,
  Tooltip,
  ResponsiveContainer,
  LineChart,
  Line,
  Legend,
} from &quot;recharts&quot;;

export default function Show() {
  const store = showStore();
  // to get the coin id from the url
  const params = useParams();

  useEffect(() =&gt; {
    store.fetchData(params.id);
  }, []);

  return (
    &lt;div&gt;
      &lt;Tabs&gt;
        &lt;TabList&gt;
          &lt;Tab&gt;Line Chart&lt;/Tab&gt;
          &lt;Tab&gt;Bar Chart&lt;/Tab&gt;
          &lt;Tab&gt;Area Chart&lt;/Tab&gt;
        &lt;/TabList&gt;

        &lt;TabPanels&gt;
          &lt;TabPanel&gt;
            &lt;div className=&quot;width&quot;&gt;
              &lt;div className=&quot;show-graph&quot;&gt;
                &lt;ResponsiveContainer width=&quot;100%&quot; height={300}&gt;
                  &lt;LineChart
                    width={500}
                    height={300}
                    data={store.graphData}
                    margin={{
                      top: 5,
                      right: 30,
                      left: 20,
                      bottom: 5,
                    }}
                  &gt;
                    &lt;CartesianGrid strokeDasharray=&quot;3 3&quot; /&gt;
                    &lt;XAxis dataKey=&quot;Date&quot; /&gt;
                    &lt;YAxis /&gt;
                    &lt;Tooltip /&gt;
                    &lt;Legend /&gt;
                    &lt;Line type=&quot;monotone&quot; dataKey=&quot;Price&quot; stroke=&quot;#8884d8&quot; /&gt;
                  &lt;/LineChart&gt;
                &lt;/ResponsiveContainer&gt;
              &lt;/div&gt;
            &lt;/div&gt;
          &lt;/TabPanel&gt;

          &lt;TabPanel&gt;
            &lt;p&gt;two!&lt;/p&gt;
          &lt;/TabPanel&gt;

          &lt;TabPanel&gt;
            &lt;p&gt;three!&lt;/p&gt;
          &lt;/TabPanel&gt;
        &lt;/TabPanels&gt;
      &lt;/Tabs&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>In this code, we use the useEffect hook to fetch data from the store, and display a Line Chart inside the TabPanel. Note that we set the data prop of LineChart to store.graphData, which is the data fetched from the store.</p>
<p>We can similarly add Bar and Area charts in the remaining TabPanel components.</p>
<pre><code class="language-javascript">&lt;TabPanel&gt;
  &lt;div className=&quot;width&quot;&gt;
    &lt;div className=&quot;show-graph&quot;&gt;
      &lt;ResponsiveContainer width=&quot;100%&quot; height=&quot;100%&quot;&gt;
        &lt;BarChart
          width={500}
          height={300}
          data={store.graphData}
          margin={{
            top: 5,
            right: 30,
            left: 20,
            bottom: 5,
          }}
        &gt;
          &lt;CartesianGrid strokeDasharray=&quot;3 3&quot; /&gt;
          &lt;XAxis dataKey=&quot;Date&quot; /&gt;
          &lt;YAxis /&gt;
          &lt;Tooltip /&gt;
          &lt;Legend /&gt;
          &lt;Bar dataKey=&quot;Price&quot; fill=&quot;#8884d8&quot; /&gt;
        &lt;/BarChart&gt;
      &lt;/ResponsiveContainer&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/TabPanel&gt;

&lt;TabPanel&gt;
  &lt;div className=&quot;width&quot;&gt;
    &lt;div className=&quot;show-graph&quot;&gt;
      &lt;ResponsiveContainer width=&quot;100%&quot; height=&quot;100%&quot;&gt;
        &lt;AreaChart
          data={store.graphData}
          margin={{
            top: 10,
            right: 30,
            left: 0,
            bottom: 0,
          }}
        &gt;
          &lt;CartesianGrid strokeDasharray=&quot;3 3&quot; /&gt;
          &lt;XAxis dataKey=&quot;Date&quot; /&gt;
          &lt;YAxis /&gt;
          &lt;Tooltip /&gt;
          &lt;Area
            type=&quot;monotone&quot;
            dataKey=&quot;Price&quot;
            stroke=&quot;#8884d8&quot;
            fill=&quot;#8884d8&quot;
          /&gt;
        &lt;/AreaChart&gt;
      &lt;/ResponsiveContainer&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/TabPanel&gt;
</code></pre>
<p>So the output will look like this.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/08/crypto-area-chart.png" alt="Crypto Tracker Tutorial with Real-Time Charts"></p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/08/crypto-bar-chart.png" alt="Crypto Tracker Tutorial with Real-Time Charts"></p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/08/crypto-line-chart.png" alt="Crypto Tracker Tutorial with Real-Time Charts"></p>
<h3 id="displayingcoindetails">Displaying Coin Details</h3>
<p>After retrieving the data from the API and setting it in the store, we can access the necessary fields using store.data.<fieldname>. Here's an example of displaying various coin details int he show.js file.</fieldname></p>
<pre><code class="language-javascript">&lt;div className=&quot;show-details&quot;&gt;
  &lt;div className=&quot;width&quot;&gt;
    &lt;h2&gt;Details&lt;/h2&gt;
    &lt;div className=&quot;show-details-row&quot;&gt;
      &lt;h4&gt;Market Cap Rank&lt;/h4&gt;
      &lt;span&gt;{store.data.market_cap_rank}&lt;/span&gt;
    &lt;/div&gt;
    &lt;div className=&quot;show-details-row&quot;&gt;
      &lt;h4&gt;24th High&lt;/h4&gt;
      &lt;span&gt;${store.data.market_data.high_24h.usd}&lt;/span&gt;
    &lt;/div&gt;
    &lt;div className=&quot;show-details-row&quot;&gt;
      &lt;h4&gt;24th Low&lt;/h4&gt;
      &lt;span&gt;${store.data.market_data.low_24h.usd}&lt;/span&gt;
    &lt;/div&gt;
    &lt;div className=&quot;show-details-row&quot;&gt;
      &lt;h4&gt;Circulating Supply&lt;/h4&gt;
      &lt;span&gt;${store.data.market_data.circulating_supply}&lt;/span&gt;
    &lt;/div&gt;
    &lt;div className=&quot;show-details-row&quot;&gt;
      &lt;h4&gt;Current Price&lt;/h4&gt;
      &lt;span&gt;${store.data.market_data.current_price.usd}&lt;/span&gt;
    &lt;/div&gt;
    &lt;div className=&quot;show-details-row&quot;&gt;
      &lt;h4&gt;1y Change&lt;/h4&gt;
      &lt;span&gt;
        {store.data.market_data.price_change_percentage_1y.toFixed(2)}%
      &lt;/span&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>This code will provide the following output.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/08/coin-details.png" alt="Crypto Tracker Tutorial with Real-Time Charts"></p>
<p>We can also display the coin image and the coin name at the top of the page.</p>
<pre><code class="language-javascript">&lt;header className=&quot;show-header&quot;&gt;
  &lt;img src={store.data.image.large} /&gt;
  &lt;h2&gt;
    {store.data.name} ({store.data.symbol})
  &lt;/h2&gt;
&lt;/header&gt;
</code></pre>
<p>In the code snippet above, store.data.image.large is the URL of the coin image, and store.data.name and store.data.symbol are the name and symbol of the coin, respectively. You willg et the following output from this code.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/08/bitcoin-logo.png" alt="Crypto Tracker Tutorial with Real-Time Charts"></p>
<h2 id="makingthecoinlistinteractive">Making the Coin List Interactive</h2>
<p>The focus of this section is on making the trending items list and the search list interactive. Specifically, we want to associate a link with each coin so that when clicked, the user is redirected to the relevant show.jsx page for that coin. This will require creating a new component which we'll name ListItems.jsx.</p>
<h3 id="creatingthelistitemsjsxcomponent">Creating the ListItems.jsx Component</h3>
<p>Inside the components folder located in the src directory, let's create a new component, ListItems.jsx. Here's how the code looks.</p>
<pre><code class="language-javascript">import { Link } from &quot;react-router-dom&quot;;

export default function ListItems({ coin }) {
  return (
    &lt;div className=&quot;home-crypto&quot;&gt;
      &lt;Link to={`/${coin.id}`}&gt;
        &lt;span className=&quot;home-crypto-image&quot;&gt;
          &lt;img src={coin.image} /&gt;
        &lt;/span&gt;
        &lt;span className=&quot;home-crypto-name&quot;&gt;{coin.name}&lt;/span&gt;
        &lt;span className=&quot;home-crypto-prices&quot;&gt;
          &lt;span className=&quot;home-crypto-btc&quot;&gt;{coin.priceBtc} BTC&lt;/span&gt;
          &lt;span className=&quot;home-crypto-usd&quot;&gt;{coin.priceUsd}$&lt;/span&gt;
        &lt;/span&gt;
      &lt;/Link&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>In this component, we use the <link> component from react-router-dom. The coin image is retrieved and displayed from <span classname="home-crypto-image">. The coin name and prices (in both BTC and USD) are fetched and displayed using the <span classname="home-crypto-prices"> and <span classname="home-crypto-name">{coin.name}</span> tags.</span></span></p>
<h3 id="incorporatinglistitemscomponentintothehomeview">Incorporating ListItems Component into the Home View</h3>
<p>The next step is to integrate this new ListItems component into the home.jsx file. Here's the updated code.</p>
<pre><code class="language-javascript">import { useEffect } from &quot;react&quot;;
import homeStore from &quot;../src/stores/homeStore&quot;;
import ListItems from &quot;./components/ListItems&quot;;

export default function Home() {
  const store = homeStore();

  useEffect(() =&gt; {
    store.fetchCoins();
  }, []);

  return (
    &lt;div&gt;
      &lt;header className=&quot;home-search&quot;&gt;
        &lt;div className=&quot;width&quot;&gt;
          &lt;h2&gt;Search for a coin&lt;/h2&gt;
          &lt;div className=&quot;home-search-input&quot;&gt;
            &lt;input
              type=&quot;text&quot;
              placeholder=&quot;Search&quot;
              value={store.query}
              onChange={store.setQuery}
            /&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/header&gt;
      &lt;div className=&quot;home-cryptos&quot;&gt;
        &lt;div className=&quot;width&quot;&gt;
          &lt;h2&gt; {!store.searched ? &quot;Trending Coins&quot; : &quot;Searched Results&quot;}&lt;/h2&gt;
          {store.coins.map((coin) =&gt; {
            return &lt;ListItems key={coin.id} coin={coin} /&gt;;
          })}
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>In the above code, we map through the coin array and for each coin, we return the ListItems component with coin.id and coin as props.</p>
<pre><code class="language-javascript">{store.coins.map((coin) =&gt; {
  return &lt;ListItems key={coin.id} coin={coin} /&gt;;
})}
</code></pre>
<p><img src="https://www.scaledrone.com/blog/content/images/2023/08/list-of-coins.png" alt="Crypto Tracker Tutorial with Real-Time Charts"></p>
<h2 id="implementingtheapplicationheader">Implementing the Application Header</h2>
<p>The last part of this step involves adding a header to our application. We will create a straightforward header with a black background and white text. Upon clicking the header text, the user will be redirected to the Home.jsx page.</p>
<h3 id="creatingtheheaderjsxcomponent">Creating the Header.jsx Component</h3>
<p>To implement this, we create a new file, Header.jsx, in the components folder. Here's how the code for Header.jsx looks.</p>
<pre><code class="language-javascript">import { Link } from &quot;react-router-dom&quot;;

export default function Header({ back }) {
  return (
    &lt;header className=&quot;header&quot;&gt;
      &lt;div className=&quot;width&quot;&gt;
        &lt;h1&gt;
          &lt;Link to=&quot;/&quot;&gt;Crypto Tracker&lt;/Link&gt;
        &lt;/h1&gt;
      &lt;/div&gt;
    &lt;/header&gt;
  );
}
</code></pre>
<p>Like in the ListItems component, we use the <link> component to navigate to the home page when the header text is clicked.</p>
<p>With these implementations, the core components of our application are ready. Finally, we need to focus on the styling to make our application visually appealing.</p>
<h2 id="finalstyling">Final styling</h2>
<p>In this section, we will finalize the look and feel of our application. To do so, create a new file named styles.scss in the src directory. Next, copy and paste the following code into this newly created file.</p>
<pre><code class="language-css">* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: &quot;Nunito&quot;, sans-serif;
}

input[type=&quot;text&quot;] {
  -webkit-appearance: none;
  -moz-appearance: none;
  -ms-appearance: none;
  -o-appearance: none;
  appearance: none;
  border: none;
  outline: none;
  display: block;
  width: 100%;
  border-radius: 0%;
}

.width {
  max-width: 600px;
  margin: 0 auto;
  width: 92%;
  position: relative;
}

.header {
  background: #050505;
  color: white;
  padding: 20px 0;
  position: sticky;
  top: 0;
  z-index: 10;
}

h1 {
  text-align: center;
  color: white;
  text-transform: uppercase;
  font-size: 20px;
  letter-spacing: 2px;
}

h1 a {
  color: white;
}

a {
  text-decoration: none;
}

img {
  max-width: 100%;
  display: block;
}

svg {
  position: absolute;
  top: 50%;
  left: 0;
  margin-top: -12px;
  background-color: rgb(249, 251, 251);
}

.home-search {
  padding: 40px 0;
  text-align: center;
}

h2 {
  font-size: 32px;
  margin-bottom: 8px;
}

input {
  border: 1px solid #ddd;
  padding: 0 24px;
  border-radius: 8px;
  line-height: 54px;
}

.home-cryptos {
  h2 {
    font-size: 26px;
    margin-bottom: 20px;
    text-align: center;
    margin-bottom: 20px;
  }
}

.home-crypto {
  a {
    display: flex;
    gap: 30px;
    align-items: center;
    padding: 20px 0;
    border-bottom: 1px solid #ddd;
  }

  span {
    display: block;
  }
}

.home-crypto-image {
  width: 50px;
}

.home-crypto-name {
  flex: 1;
  font-size: 20px;
  color: #111;
  font-weight: 600;
}

.home-crypto-btc {
  font-size: 16px;
  color: #111;
}

.home-crypto-usd {
  font-size: 10px;
  color: #111;
}

.show-header {
  padding: 40px 0;
  text-align: center;
  font-size: 32px;

  img {
    width: 80px;
    border-radius: 50%;
    margin: 0 auto;
  }

  h2 {
    font-size: 32px;
    margin-top: 10px;
  }
}

.show-graph {
  height: 200px;
  width: 100%;
}

.show-details {
  padding-top: 100px;
  padding-bottom: 60px;

  h2 {
    font-size: 26px;
    margin-bottom: 20px;
    text-align: center;
    margin-bottom: 20px;
  }
}

.show-details-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 24px 0;
  border-bottom: 1px solid #ddd;
  color: #666;

  h4 {
    font-size: 16px;
    color: #111;
  }
}
</code></pre>
<p>The above style specifications should be quite straightforward to understand, so we won't delve into the specifics for each one.</p>
<p>Finally, import the style specifications into your main.jsx file using the following code.</p>
<pre><code class="language-javascript">import &quot;./styles.scss&quot;;
</code></pre>
<p>Now everything is set, and your application should be good to go.</p>
<h2 id="finalreviewofthecode">Final review of the code</h2>
<p>As we said earlier, you can download this app from my <a href="https://github.com/ShanikaNishadhi/CryptoTrackerSN/tree/main">GitHub repo</a>. You can find all the files we developed here in that repository. Click the following links to get the final code of each file.</p>
<p><a href="https://github.com/ShanikaNishadhi/CryptoTrackerSN/blob/main/frontend/src/Home.jsx">Home.jsx</a></p>
<p><a href="https://github.com/ShanikaNishadhi/CryptoTrackerSN/blob/main/frontend/src/Show.jsx">Show.jsx</a></p>
<p><a href="https://github.com/ShanikaNishadhi/CryptoTrackerSN/blob/main/frontend/src/stores/homeStore.jsx">homeStore.jsx</a></p>
<p><a href="https://github.com/ShanikaNishadhi/CryptoTrackerSN/blob/main/frontend/src/stores/showStore.jsx">showStore.jsx</a></p>
<p><a href="https://github.com/ShanikaNishadhi/CryptoTrackerSN/blob/main/frontend/src/components/Header.jsx">Header.jsx</a></p>
<p><a href="https://github.com/ShanikaNishadhi/CryptoTrackerSN/blob/main/frontend/src/components/ListItems.jsx">ListItems.jsx</a></p>
<p><a href="https://github.com/ShanikaNishadhi/CryptoTrackerSN/blob/main/frontend/src/helpers/debounce.jsx">Debounce.jsx</a></p>
<h2 id="conclusion">Conclusion</h2>
<p>We've now successfully concluded our Real-Time Crypto Tracker with Real-Time Charts tutorial. To solidify your understanding of what we've covered, I encourage you to interact further with the Coindgecko API. By implementing different functionalities within this app or creating a completely new one, you can extend your learning.</p>
<p>Experimentation is a key part of mastery, so try incorporating more chart types and different data sets. The UI library we utilized in this tutorial is quite handy for front-end development practice, so don't hesitate to experiment with various components.</p>
<p>I hope this tutorial has proven insightful and educational. Looking forward to meeting you in the next tutorial!</p>
</div>]]></content:encoded></item><item><title><![CDATA[How to Recognize Languages and Names With Natural Language on iOS]]></title><description><![CDATA[<div class="kg-card-markdown"><p>If your app deals with user input text in any way, there's a good chance you will benefit from machine learning text analysis. You might want to suggest categories or tags based on the description of an item or content of a post. If you have an app that works</p></div>]]></description><link>https://www.scaledrone.com/blog/recognizing-languages-and-names-with-natural-language-on-ios/</link><guid isPermaLink="false">5c3c40a258db83073fd71244</guid><dc:creator><![CDATA[Marin Bencevic]]></dc:creator><pubDate>Sat, 17 Jun 2023 13:44:38 GMT</pubDate><media:content url="https://www.scaledrone.com/blog/content/images/2023/06/abc.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://www.scaledrone.com/blog/content/images/2023/06/abc.png" alt="How to Recognize Languages and Names With Natural Language on iOS"><p>If your app deals with user input text in any way, there's a good chance you will benefit from machine learning text analysis. You might want to suggest categories or tags based on the description of an item or content of a post. If you have an app that works across languages, you can detect the language of a string and translate it to the user's native language. You might want to know what a piece of text is about for faster search, better recommendations or better targeted ads. The possibilities are endless.</p>
<p>In all of these cases, iOS offers a one-stop-shop for all your text-related machine learning needs: the <strong>Natural Language framework</strong>. This framework lets you detect languages and tags or split sentences into parts. Combine it with a custom <strong>Core ML</strong> model, and you can do custom text classification or tagging on the user's device.</p>
<p>In this Natural Language tutorial you'll find out how to detect the language of an arbitrary string, as well as detect which tags were mentioned in the string, including company, place, and personal names.</p>
<p>By the end of the tutorial, you'll have an app like this:</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2019/01/Simulator-Screen-Shot---iPhone-8---2019-01-14-at-08.34.27.png" alt="How to Recognize Languages and Names With Natural Language on iOS"></p>
<h2 id="settinguptheui">Setting up the UI</h2>
<p>Open up Xcode and create a new <strong>single view application</strong>. Open <strong>Main.storyboard</strong> to add your UI. We need four labels and a text field. In order to easily lay them out, we'll use a stack view, so start by dragging in a <strong>stack view</strong> to the screen. Add three constraints between this <strong>stack view</strong> and the <strong>view</strong>:</p>
<ol>
<li>Leading space to safe area with a constant of 20</li>
<li>Trailing space to safe area with a constant of 20</li>
<li>Top space to safe area with a constant of 20</li>
</ol>
<p>With that in place, add <strong>four labels</strong> inside the stack view, one below the other. Edit them to have the following text, from the top-most one to the bottom one:</p>
<ol>
<li><code>You are speaking in</code></li>
<li><code>English</code></li>
<li><code>and you are talking about</code></li>
<li><code>Apple</code></li>
</ol>
<p>These are placeholder texts which you'll change in code later. I changed the English and Apple labels to have a bold font and larger text, just to make it look a bit nicer.</p>
<p>Next, drag over a new <strong>text field</strong> and position it underneath the stack view (<em>not</em> inside it). Add three new constraints to the this text field:</p>
<ol>
<li>Leading space to safe area with a constant of 20</li>
<li>Trailing space to safe area with a constant of 20</li>
<li>Vertical spacing to the stack view with a constant of 20</li>
</ol>
<p>By the end of this your storyboard should look like this:</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2019/01/Screenshot-2019-01-14-at-08.38.32.png" alt="How to Recognize Languages and Names With Natural Language on iOS"></p>
<p>Next, open up <strong>ViewController.swift</strong> and drag in three new outlets:</p>
<ol>
<li>An outlet for the English label named <code>languageLabel</code></li>
<li>An outlet for the Apple label named <code>entityLabel</code></li>
<li>An outlet for the text field named <code>textField</code></li>
</ol>
<pre><code class="language-swift">@IBOutlet weak var languageLabel: UILabel!
@IBOutlet weak var entityLabel: UILabel!
@IBOutlet weak var textField: UITextField!
</code></pre>
<p>Now that our UI is in place, we can start hacking away!</p>
<h2 id="recognizinglanguages">Recognizing Languages</h2>
<p>In order to recognize languages, naturally, we need a Language Recognizer. <code>NLLanguageRecognizer</code> is a class from Natural Language that can give you information about a given string's possible language. Start by importing Natural Language at the top of <strong>ViewController.swift</strong>:</p>
<pre><code class="language-swift">import NaturalLanguage
</code></pre>
<p>Next, add the following property to the <code>ViewController</code> class, below the outlets:</p>
<pre><code class="language-swift">let languageRecognizer = NLLanguageRecognizer()
</code></pre>
<p>Now it's time to add a new method to the class that we'll use to recognize our language:</p>
<pre><code class="language-swift">func recognizeLanguage(_ text: String)-&gt; NLLanguage? {
  languageRecognizer.reset()
  languageRecognizer.processString(text)
  return languageRecognizer.dominantLanguage
}
</code></pre>
<p>First we'll reset the recognizer to make sure it clears any strings we provided earlier. We'll then give it a new string to process and ask it for the dominant language.</p>
<p>The language recognizer can never be 100% sure, so the dominant language is the one it thinks is the most likely candidate. If you want to see other possible languages and just <em>how</em> likely they are, you can use the <code>languageHypotheses</code> method, which gives you a set of languages and a score of how likely they fit the given string.</p>
<p>You can also make the recognizer more precise by helping it out a bit: by setting the <code>languageHints</code> property you can tell the recognizer how likely you think each language is. It will then take this information into account when deciding the language of a string. You can also limit which languages are possible by setting the <code>languageConstraints</code> property.</p>
<p>Now that we have a way to recognize languages, let's hook it up to our text field and UI. Add the following lines to the bottom of <code>viewDidLoad</code>:</p>
<pre><code class="language-swift">languageLabel.text = &quot;&quot;
entityLabel.text = &quot;&quot;
textField.addTarget(self, 
  action: #selector(onTextFieldChanged), for: .editingChanged)
</code></pre>
<p>First we'll clear out the UI, and then add a function that will get called each time the text field's value changes.</p>
<p>Now it's time to implement that function. Add the following method to the class:</p>
<pre><code class="language-swift">@objc func onTextFieldChanged(_ textField: UITextField) {
  guard let text = textField.text else {
    return
  }
  
  if let language = recognizeLanguage(text) {
    let name = language.rawValue
    languageLabel.text = name
  }
}
</code></pre>
<p>First we'll grab the text from the text field, and then call our previously defined method to get the language of the text. Finally, we'll set the language's <code>rawValue</code> to our label. The language is an enum with a raw string value representing the language code. (&quot;en&quot; for English, &quot;es&quot; for Spanish, etc.)</p>
<p>If you run the project now and type in a string, you should see the string's language pop up in the UI. Pretty cool!</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2019/01/Simulator-Screen-Shot---iPhone-8---2019-01-14-at-08.36.48.png" alt="How to Recognize Languages and Names With Natural Language on iOS"></p>
<h2 id="taggingtext">Tagging Text</h2>
<p>Now that we know the language, it's time to figure out what the user is talking about. For language recognition we used a recognizer, so, naturally, for tagging we use a tagger.</p>
<p><code>NLTagger</code> is a very powerful class from Natural Language. It can detect parts of sentences (verbs, nouns, adjectives etc.), special characters (whitespace, quotes), words and idioms, as well as names places, people and organizations. That's a lot of stuff!</p>
<p>In our case, we're interested in the names of places, people and organizations. Things like &quot;California&quot;, &quot;Tim Cook&quot; or &quot;Apple&quot;. We'll detect these and display what word it is and what type of tag it is for each detected word.</p>
<p>Start by adding the following method to <code>ViewController</code>:</p>
<pre><code class="language-swift">func recognizeTags(_ text: String) -&gt; [(String, NLTag)] {
  let tags: [NLTag] = [.personalName, .placeName, .organizationName]
  let tagger = NLTagger(tagSchemes: [.nameType])
  tagger.string = text
}
</code></pre>
<p>This function takes a text input and returns a list of tagged words. The list elements are tuples where the first item is the actual word, and the second item is that word's tag.</p>
<p>We defined a list of tags we're interested in, created an <code>NLTagger</code> instance, and given it our text. We initialize it with the <code>.nameType</code> scheme because we want the tagger to find names inside our string. We can use other schemes, like the <code>.lemma</code> scheme which will give us a word's stem. Using these schemes we can detect lexical classes, languages, scripts and other word types.</p>
<p>Next, add the following code to the end of the method:</p>
<pre><code class="language-swift">var results: [(String, NLTag)] = []
tagger.enumerateTags(
  in: text.startIndex..&lt;text.endIndex,
  unit: .word,
  scheme: .nameType,
  options: [.omitWhitespace, .omitPunctuation, .joinNames],
  using: { tag, range in
    guard let tag = tag, tags.contains(tag) else {
      return true
    }
    
    print(&quot;Found tag \(tag) in text \&quot;\(text[range])\&quot;&quot;)
    results.append((String(text[range]), tag))
    return true
})
return results
</code></pre>
<p>This might seem a little dense at first, but bear with me. We want to enumerate trough every word the tagger found, so we call the <code>enumerateTags</code> method and make sure it goes from start to the end of the string. Again we use the <code>.nameType</code> scheme, and make sure we omit whitespace and punctuation since we don't need them. We also make sure names are joined so &quot;Tim Cook&quot; doesn't become a cook named Tim.</p>
<p>We give <code>enumerate</code> a closure to call for each new tag it found. In the closure we'll filter the tags so that we only save the ones we're interested in. If the tag is one of the ones we need, we'll append it to our result array, together with the actual word.</p>
<p>As we did in the previous section, we need to connect this method to our UI. Add the following code to the end of <code>onTextFieldChanged</code>:</p>
<pre><code class="language-swift">let results = recognizeTags(text)
let entityText = results
  .map { (word, tag) in
    &quot;\(word) (\(tag.rawValue))&quot;
  }
  .joined(separator: &quot;, &quot;)
entityLabel.text = entityText
</code></pre>
<p>We'll get all the tag pairs, convert each one to a string where the tag type is in brackets, and join them all into a big comma-separated string. Finally, we'll set that as the label's text.</p>
<p>If you run the project now, you should see all the different types of names as well as the language.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2019/01/Simulator-Screen-Shot---iPhone-8---2019-01-14-at-08.34.27.png" alt="How to Recognize Languages and Names With Natural Language on iOS"></p>
<p>Congratulations! You now have an app that recognizes both the language a user is writing in as well as what they're writing about!</p>
<h2 id="goingevenfurther">Going Even Further</h2>
<p>This tutorial only scratched the surface of what is possible with Natural Language. If you combine it with <strong>Core ML</strong>, you can train your own text classifiers or taggers. For instance, you can automatically categorize new listings in your web shop or detect inappropriate language and harassment in your chat app.</p>
<p>If you would like to read more about machine learning on iOS, check out our <a href="https://www.scaledrone.com/blog/ios-machine-learning-understanding-the-basics/">machine learning overview</a>. Good luck!</p>
</div>]]></content:encoded></item><item><title><![CDATA[How to Detect Faces on iOS Using Vision and Machine Learning]]></title><description><![CDATA[<div class="kg-card-markdown"><p>Apple's Vision framework offers a lot of tools for image processing and image-based machine learning, one of those is detecting faces. This tutorial will show you how to detect faces using Vision, and display those faces on the screen. By the end of the tutorial you'll have an app that</p></div>]]></description><link>https://www.scaledrone.com/blog/how-to-detect-faces-on-ios-using-vision-and-machine-learning/</link><guid isPermaLink="false">5c29c76058db83073fd711fe</guid><dc:creator><![CDATA[Marin Bencevic]]></dc:creator><pubDate>Wed, 31 Jul 2019 05:04:16 GMT</pubDate><media:content url="https://www.scaledrone.com/blog/content/images/2019/07/facial-detection-street.jpg" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://www.scaledrone.com/blog/content/images/2019/07/facial-detection-street.jpg" alt="How to Detect Faces on iOS Using Vision and Machine Learning"><p>Apple's Vision framework offers a lot of tools for image processing and image-based machine learning, one of those is detecting faces. This tutorial will show you how to detect faces using Vision, and display those faces on the screen. By the end of the tutorial you'll have an app that looks like this:</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2018/12/Simulator-Screen-Shot---iPhone-8---2018-12-28-at-14.40.39.png" alt="How to Detect Faces on iOS Using Vision and Machine Learning"></p>
<p>You can use facial detection to crop images, focus on important parts of an image, or for letting the users easily tag their friends in an image. You can find the full code of this tutorial <a href="https://github.com/ScaleDrone/ios-face-detection-tutorial">on GitHub</a>.</p>
<h2 id="settinguptheui">Setting up the UI</h2>
<p>We'll start by making sure we can see and pick new photos to detect faces on. Create a new <strong>single-view app</strong> project in Xcode, and open up <strong>Main.storyboard</strong>.</p>
<p>Start by dragging an <strong>image view</strong> from the object library onto the storyboard. Once it's there, add four new constraints to it so that it's fixed to the leading, trailing, top and bottom edges of the safe area. This image view will show the image the faces are being detected on.</p>
<p>Next, drag a button to the storyboard, and add two new constraints:</p>
<ol>
<li>make the button's trailing space equal to the safe area trailing space with a constant of 20</li>
<li>make the button's top space equal to the safe area top space with a constant of 20</li>
</ol>
<p>This should fix the button to the top-right edge of the screen. You'll use this button to show an image picker, so give it a title like &quot;Pick image&quot;. I used a camera emoji. By the end of this, your storyboard should look like this:</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2018/12/Screenshot-2018-12-31-at-08.40.38.png" alt="How to Detect Faces on iOS Using Vision and Machine Learning"></p>
<p>Next, open <strong>ViewController.swift</strong> and add an <code>IBOutlet</code> to the storyboard's image view called <code>imageView</code>.</p>
<pre><code class="language-swift">@IBOutlet weak var imageView: UIImageView!
</code></pre>
<p>Next, add a new <code>IBAction</code> from the button in the storyboard, and call it <code>cameraButtonTapped</code>.</p>
<pre><code class="language-swift">@IBAction func cameraButtonTapped(_ sender: UIButton) {
}
</code></pre>
<p>Next, add the following method to the class:</p>
<pre><code class="language-swift">func showImagePicker(sourceType: UIImagePickerController.SourceType) {
  let picker = UIImagePickerController()
  picker.sourceType = sourceType
  picker.delegate = self
  present(picker, animated: true)
}
</code></pre>
<p>This method will show a new image picker as a modal screen, configured with the source type that was passed to the method. At this point you'll get an error because you didn't implement <code>UIImagePickerControllerDelegate</code> yet, so let's do this next. Add the following extension to the bottom of the file:</p>
<pre><code class="language-swift">extension ViewController: UIImagePickerControllerDelegate,
  UINavigationControllerDelegate {
  
  func imagePickerController(
    _ picker: UIImagePickerController, 
    didFinishPickingMediaWithInfo info: 
    [UIImagePickerController.InfoKey : Any]) {
    
    guard let image = info[.originalImage] as? UIImage else {
      return
    }
    
    imageView.image = image
    dismiss(animated: true)
  }
}
</code></pre>
<p>This will fetch the picked image and show it on the view controller's image view. It will then dismiss the modal image picker.</p>
<p>We now have the code for the image picker in place, but we're not actually calling the methods anywhere. We'll do this from <code>cameraButtonTapped</code>, when the button gets tapped.</p>
<p>First we'll present an action sheet to let the user pick whether they want to take a new photo or pick an existing one from their library. Add the following code to <code>cameraButtonTapped</code>:</p>
<pre><code class="language-swift">    let actionSheet = UIAlertController(
      title: &quot;Detect faces&quot;,
      message: &quot;Pick a photo to detect faces on.&quot;,
      preferredStyle: .actionSheet)
    
    let cameraAction = UIAlertAction(
      title: &quot;Take a new photo&quot;,
      style: .default,
      handler: { [weak self] _ in
        self?.showImagePicker(sourceType: .camera)
      })
      
    let libraryAction = UIAlertAction(
      title: &quot;Pick from your library&quot;,
      style: .default,
      handler: { [weak self] _ in
        self?.showImagePicker(sourceType: .savedPhotosAlbum)
      })
      
    actionSheet.addAction(cameraAction)
    actionSheet.addAction(libraryAction)
    
    present(actionSheet, animated: true)
</code></pre>
<p>The user can pick from two options: either selecting an image from their library or taking a new image. Both of these options will call <code>showImagePicker</code>, but with a different parameter for the source type.</p>
<p>We'll add one more tweak: add the following line to the bottom of <code>viewDidLoad</code>:</p>
<pre><code class="language-swift">imageView.contentMode = .scaleAspectFit
</code></pre>
<p>This will make sure the image isn't stretched when it's displayed on the image view.</p>
<p>If you run the project now, you should be able to pick an image from your library and see it appear on the view controller.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2018/12/Screenshot-2018-12-28-at-11.11.05.png" alt="How to Detect Faces on iOS Using Vision and Machine Learning"></p>
<h2 id="detectingfaces">Detecting Faces</h2>
<p>Now that we have our image, we can detect faces on it! To do this we'll use Vision: a built-in framework for image processing and using image-based machine learning.</p>
<p>Vision provides out-of-the-box APIs for detecting faces and facial features, rectangles, text and bar codes (including QR codes). Additionally, you can combine it with custom Core ML models for building things like custom object classifiers. For more information on the different machine learning frameworks in iOS, check out the <a href="https://www.scaledrone.com/blog/ios-machine-learning-understanding-the-basics/">introduction to machine learning on iOS</a>.</p>
<p>Before we can start using Vision, we need to import it, so add the following import to the top of the file:</p>
<pre><code class="language-swift">import Vision
</code></pre>
<p>We'll start by adding a new method to the class called <code>detectFaces</code>:</p>
<pre><code class="language-swift">func detectFaces(on image: UIImage) {
  let handler = VNImageRequestHandler(
    cgImage: image.cgImage!,
    options: [:])
}
</code></pre>
<p>The core of vision is the request handler. This handler performs different operations on a single image, which is why we are creating it with the image view's image. The different actions are called &quot;requests&quot;, and a handler can perform multiple requests at once.</p>
<p>Since we need to detect faces, we can use the built-in <code>VNDetectFaceRectanglesRequest</code>, which lets us find the bounding boxes of faces on our image.</p>
<p>Add the following property to the class:</p>
<pre><code class="language-swift">lazy var faceDetectionRequest = 
  VNDetectFaceRectanglesRequest(completionHandler: self.onFacesDetected)
</code></pre>
<p>A single request can be performed on multiple images, which is why it's good practice to create one shared request object instead of creating it every time a function is called.</p>
<p>Note that there is also a <code>VNDetectFaceLandmarksRequest</code>. Alongside face bounding boxes, it detects facial features like noses, eyes, mouths and similar. Because of that, it's slower than <code>VNDetectFaceRectanglesRequest</code>, so we won't be using it.</p>
<p>Next, we'll implement the completion handler by adding the following method to the class:</p>
<pre><code class="language-swift">func onFacesDetected(request: VNRequest, error: Error?) {
  guard let results = request.results as? [VNFaceObservation] else {
    return
  }
  
  for result in results {
    print(&quot;Found face at \(result.boundingBox)&quot;)
  }
}
</code></pre>
<p>For now we'll just print out the results to the console. The <code>boundingBox</code> property contains a normalized rectangle of where the face is. This means that its origin and scale are percentages of the image's width and height, and not absolute values. We'll see how to deal with that later on in the tutorial.</p>
<p>Next, we need to actually perform the request. We'll do this by adding the following code to the bottom of <code>detectFaces</code>:</p>
<pre><code class="language-swift">DispatchQueue.global(qos: .userInitiated).async {
  do {
    try handler.perform([self.faceDetectionRequest])
  } catch {
    print(error)
  }
}
</code></pre>
<p>We'll hop over to a background queue so we don't block the main thread while detection is going on. There, we'll try to use the handler to perform the request. If it fails, we'll simply print the error out.</p>
<p>Finally, we need to call the <code>detectFaces</code> method from <code>imagePickerController</code>, so add the following line to the extension, right above the call to <code>dismiss</code>:</p>
<pre><code class="language-swift">detectFaces(on: image)
</code></pre>
<p>If you run the project now and pick an image with a face on it, you should see the faces' bounding boxes printed to the console.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2018/12/Screenshot-2018-12-28-at-13.20.32.png" alt="How to Detect Faces on iOS Using Vision and Machine Learning"></p>
<h2 id="drawingboxes">Drawing Boxes</h2>
<p>Now that we have the bounding boxes, it's time to draw them on the screen! We'll use Core Animation to create a <code>CALayer</code> for each face's bounding rectangle, and add them to the screen.</p>
<p>Start by adding the following property to the class:</p>
<pre><code class="language-swift">let resultsLayer = CALayer()
</code></pre>
<p>This layer will hold all of the faces' rectangles. We'll add this layer to the image view by adding the following code to the bottom of <code>viewDidLoad</code>:</p>
<pre><code class="language-swift">imageView.layer.addSublayer(resultsLayer)
</code></pre>
<p>As mentioned before, Vision returns normalized rectangles in the image. This means we need a way to convert from those normalized values into actual coordinates in the image view. To do this, we first need to know where the image actually is within the view (because it gets scaled to fit inside the view). We can do by adding this handy extension <a href="https://www.hackingwithswift.com/example-code/uikit/how-to-find-an-aspect-fit-images-size-inside-an-image-view">by Paul Hudson</a> to the top of the file:</p>
<pre><code class="language-swift">extension UIImageView {
  var contentClippingRect: CGRect {
    guard let image = image else { return bounds }
    guard contentMode == .scaleAspectFit else { return bounds }
    guard image.size.width &gt; 0 &amp;&amp; image.size.height &gt; 0 else { return bounds }
    
    let scale: CGFloat
    if image.size.width &gt; image.size.height {
      scale = bounds.width / image.size.width
    } else {
      scale = bounds.height / image.size.height
    }
    
    let size = CGSize(
      width: image.size.width * scale, 
      height: image.size.height * scale)
    let x = (bounds.width - size.width) / 2.0
    let y = (bounds.height - size.height) / 2.0
    
    return CGRect(x: x, y: y, width: size.width, height: size.height)
  }
}
</code></pre>
<p>Now that we have the image's bounds, we can use it to de-normalize our face bounding boxes. Add the following method to <code>ViewController</code>:</p>
<pre><code class="language-swift">func denormalized(
  _ normalizedRect: CGRect, 
  in imageView: UIImageView)-&gt; CGRect {
  
  let imageSize = imageView.contentClippingRect.size
  let imageOrigin = imageView.contentClippingRect.origin
  
  let newOrigin = CGPoint(
    x: normalizedRect.minX * imageSize.width + imageOrigin.x,
    y: (1 - normalizedRect.maxY) * imageSize.height + imageOrigin.y)
  let newSize = CGSize(
    width: normalizedRect.width * imageSize.width,
    height: normalizedRect.height * imageSize.height)
  
  return CGRect(origin: newOrigin, size: newSize)
}
</code></pre>
<p>This is a lot of math, but don't panic!</p>
<p>Since the values in the normalized <code>CGRect</code> are percentages, we can get the actual values by multiplying them with the images width or height, respectively. We'll also add the images origin to the new rect's origin coordinates so it starts from the same point the image starts. The normalized rectangle has its origin in the lower-left corner, while Core Animation expects drawing to start from the top-left, which is why we're subtracting the origin's y value from 1. Whew!</p>
<p>Now that we have all the pieces in place, it's time to put them together and actually draw these rectangles on the screen. Add the following code to the bottom of <code>onFacesDetected</code>:</p>
<pre><code class="language-swift">DispatchQueue.main.async {
  CATransaction.begin()
  for result in results {
    print(result.boundingBox)
    self.drawFace(in: result.boundingBox)
  }
  CATransaction.commit()
}
</code></pre>
<p>Since the callback can be called on an arbitrary thread, we need to move back to the main thread in order to update the UI. We'll perform all updates inside a <code>CATransaction</code> so that they happen in a single UI pass. For each bounding box, we'll draw a face. The final key to this puzzle is adding the <code>drawFace</code> method to the class:</p>
<pre><code class="language-swift">private func drawFace(in rect: CGRect) {
  let layer = CAShapeLayer()
  let rect = self.denormalized(rect, in: self.imageView)
  layer.path = UIBezierPath(rect: rect).cgPath
  layer.strokeColor = UIColor.yellow.cgColor
  layer.fillColor = nil
  layer.lineWidth = 2
  self.resultsLayer.addSublayer(layer)
}
</code></pre>
<p>This method creates a new layer and draws the denormalized rectangle with a yellow outline. It then adds this layer to the results layer.</p>
<p>If you run the project now, you should see a yellow rectangle around people's faces!</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2018/12/Simulator-Screen-Shot---iPhone-8---2018-12-28-at-14.40.39.png" alt="How to Detect Faces on iOS Using Vision and Machine Learning"></p>
<p>And that's it! You can find the full code of this tutorial <a href="https://github.com/ScaleDrone/ios-face-detection-tutorial">on GitHub</a>.</p>
<h2 id="othercoolmachinelearningtools">Other Cool Machine Learning Tools</h2>
<p>This Vision tutorial only scratched the surface of machine learning on iOS. You can easily adapt this code to use Vision to detect facial features like eyes or noses. You can also combine Core ML with Vision to use custom object detection models on images. Or better yet, train your own model with Create ML to train your own models. There are also other frameworks like Natural Language. You can learn more about the iOS machine learning landscape <a href="https://www.scaledrone.com/blog/ios-machine-learning-understanding-the-basics/">in our article on iOS machine learning</a>!</p>
</div>]]></content:encoded></item><item><title><![CDATA[Go Chat App Tutorial: Build a Real-time Chat]]></title><description><![CDATA[<div class="kg-card-markdown"><p>In this tutorial we'll be building a chat app using Go, JavaScript and Scaledrone realtime messaging platform. <a href="https://github.com/ScaleDrone/golang-chat-tutorial">You can find the full source from GitHub.</a></p>
<iframe src="https://player.vimeo.com/video/319174303" width="640" height="389" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen></iframe>
<h2 id="structureoftheproject">Structure of the project</h2>
<p>Our tutorial will be broken into two sections:</p>
<ol>
<li><strong>Go authentication server</strong> which is responsible for giving precise access rights for each</li></ol></div>]]></description><link>https://www.scaledrone.com/blog/go-chat-app-tutorial-build-a-real-time-chat/</link><guid isPermaLink="false">5c6e70d558db83073fd7125f</guid><category><![CDATA[Go]]></category><category><![CDATA[Chat]]></category><category><![CDATA[Tutorials]]></category><dc:creator><![CDATA[Scaledrone]]></dc:creator><pubDate>Sat, 23 Feb 2019 13:39:38 GMT</pubDate><media:content url="https://www.scaledrone.com/blog/content/images/2019/02/go-chat-header@2x.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://www.scaledrone.com/blog/content/images/2019/02/go-chat-header@2x.png" alt="Go Chat App Tutorial: Build a Real-time Chat"><p>In this tutorial we'll be building a chat app using Go, JavaScript and Scaledrone realtime messaging platform. <a href="https://github.com/ScaleDrone/golang-chat-tutorial">You can find the full source from GitHub.</a></p>
<iframe src="https://player.vimeo.com/video/319174303" width="640" height="389" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen></iframe>
<h2 id="structureoftheproject">Structure of the project</h2>
<p>Our tutorial will be broken into two sections:</p>
<ol>
<li><strong>Go authentication server</strong> which is responsible for giving precise access rights for each user and allowing them to connect to Scaledrone.</li>
<li><strong>JavaScript chat app</strong> that authenticates itself using the Go server and manages user communication.</li>
</ol>
<img src="https://www.scaledrone.com/blog/content/images/2019/02/swimlanes-go.png" srcset="/blog/content/images/2019/02/swimlanes-go@2x.png 2x" style="max-height:500px" alt="Go Chat App Tutorial: Build a Real-time Chat">
<h2 id="creatingthegoauthenticationserver">Creating the Go authentication server</h2>
<p>You can find the full <a href="https://github.com/ScaleDrone/golang-chat-tutorial/blob/master/main.go"><code>main.go</code> file from GitHub</a>.</p>
<p>Let's start by defining a few constants and starting an http server for serving static files and providing a single endpoint for user authentication: <code>POST http://localhost:8080/auth</code>.</p>
<p>Replace the <code>scaledroneID</code> and <code>scaledroneSecret</code> constants with the ID and secret from the Scaledrone dashboard.</p>
<pre><code class="language-go">package main

import (
	&quot;fmt&quot;
	&quot;math/rand&quot;
	&quot;net/http&quot;
	&quot;strconv&quot;
	&quot;time&quot;

	jwt &quot;github.com/dgrijalva/jwt-go&quot;
	&quot;github.com/gorilla/mux&quot;
)

const (
	scaledroneID     = &quot;YOUR_SCALEDRONE_ID&quot;     // 👈 PS! Replace this with your own channel ID 🚨
	scaledroneSecret = &quot;YOUR_SCALEDRONE_SECRET&quot; // 👈 PS! Replace this with your own channel secret 🚨
	port             = &quot;:8080&quot;
)

func main() {
	r := mux.NewRouter()
	r.HandleFunc(&quot;/auth&quot;, auth).Methods(&quot;POST&quot;)
	r.PathPrefix(&quot;/&quot;).Handler(http.FileServer(http.Dir(&quot;./static&quot;))).Methods(&quot;GET&quot;)
	fmt.Printf(&quot;Server is running on localhost%s&quot;, port)
	panic(http.ListenAndServe(port, r))
}
</code></pre>
<p>Every user connecting to the chatroom will authenticate themselves with a JSON Web Token generated by the Go server that the user then passes on to Scaledrone. This gives us the ability to define precise access levels for each user.</p>
<p>In addition to the standard <code>exp</code> claim (expiration time of the token), Scaledrone uses a few custom claims like</p>
<ul>
<li><code>client</code> the client ID of the connecting user</li>
<li><code>channel</code> the channel ID that your app uses</li>
<li><code>data</code> additional server-side data you can bind to a user. This data will be readable by all users.</li>
<li><code>permissions</code> a regular expressions map of access rights. Each regexp string will target specific rooms.</li>
</ul>
<blockquote>
<p>You can read more about Scaledrone's JSON Web Token authentication <a href="https://www.scaledrone.com/docs/jwt-authentication">here</a>.</p>
</blockquote>
<pre><code class="language-go">type customClaims struct {
	jwt.StandardClaims
	Client      string                      `json:&quot;client&quot;`
	Channel     string                      `json:&quot;channel&quot;`
	Data        userData                    `json:&quot;data&quot;`
	Permissions map[string]permissionClaims `json:&quot;permissions&quot;`
}

type permissionClaims struct {
	Publish   bool `json:&quot;publish&quot;`
	Subscribe bool `json:&quot;subscribe&quot;`
}

type userData struct {
	Color string `json:&quot;color&quot;`
	Name  string `json:&quot;name&quot;`
}

func auth(w http.ResponseWriter, r *http.Request) {
	clientID := r.FormValue(&quot;clientID&quot;)
	if clientID == &quot;&quot; {
		http.Error(w, &quot;No clientID defined&quot;, http.StatusUnprocessableEntity)
		return
	}

	// public room
	publicRoomRegex := &quot;^observable-room$&quot;
	// private room of the request user
	userPrivateRoomRegex := fmt.Sprintf(&quot;^private-room-%s$&quot;, clientID)
	// private rooms of every user besides the request user
	otherUsersPrivateRoomsRegex := fmt.Sprintf(&quot;^private-room-(?!%s$).+$&quot;, clientID)
	claims := customClaims{
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: time.Now().Add(time.Minute * 3).Unix(),
		},
		Client:  clientID,
		Channel: scaledroneID,
		Data: userData{
			Name:  getRandomName(),
			Color: getRandomColor(),
		},
		Permissions: map[string]permissionClaims{
			publicRoomRegex: permissionClaims{ // public room
				Publish:   true, // allow publishing to public chatroom
				Subscribe: true, // allow subscribing to public chatroom
			},
			userPrivateRoomRegex: permissionClaims{
				Publish:   false, // no need to publish to ourselves
				Subscribe: true,  // allow subscribing to private messages
			},
			otherUsersPrivateRoomsRegex: permissionClaims{
				Publish:   true,  // allow publishing to other users
				Subscribe: false, // don't allow subscribing to messages sent to other users
			},
		},
	}

	// Create a new token
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// Sign the token with our secret
	tokenString, err := token.SignedString([]byte(scaledroneSecret))
	if err != nil {
		http.Error(w, &quot;Unable to sign the token&quot;, http.StatusUnprocessableEntity)
		return
	}
	// Send the token to the user
	w.Write([]byte(tokenString))
}
</code></pre>
<p>As you can see, each authenticating user is given individual access levels to three rooms defined by regular expressions. Each regexp defines one type of access level.</p>
<ul>
<li><code>^observable-room$</code> Targets the public room in which everyone will be able to publish messages and subscribe to them.
<blockquote>
<p>To get access to the who's online features we're using Scaledrone's <a href="https://www.scaledrone.com/docs/api-clients/observable-rooms">observable rooms feature</a>. For this to work, the room name needs to be prefixed with <code>observable-.</code></p>
</blockquote>
</li>
<li><code>^private-room-clientid$</code> Targets the private room of the user currently being authenticated. That user will be the only one that can subscribe to their own room (into which other users will be sending private messages).</li>
<li><code>^private-room-(?!clientid$).+$</code> Targets all private rooms besides the private room of the user currently being authenticated. This allows everyone to publish private messages to other users but not subscribe to them.</li>
</ul>
<hr>
<p>To keep the tutorial shorter, we're going to be assiging each user a random color and name using the JWT <code>data</code> claim. In your own app, pull this data from your database.</p>
<pre><code class="language-go">func getRandomName() string {
	adjs := []string{&quot;autumn&quot;, &quot;hidden&quot;, &quot;bitter&quot;, &quot;misty&quot;, &quot;silent&quot;, &quot;empty&quot;, &quot;dry&quot;, &quot;dark&quot;, &quot;summer&quot;, &quot;icy&quot;, &quot;delicate&quot;, &quot;quiet&quot;, &quot;white&quot;, &quot;cool&quot;, &quot;spring&quot;, &quot;winter&quot;, &quot;patient&quot;, &quot;twilight&quot;, &quot;dawn&quot;, &quot;crimson&quot;, &quot;wispy&quot;, &quot;weathered&quot;, &quot;blue&quot;, &quot;billowing&quot;, &quot;broken&quot;, &quot;cold&quot;, &quot;damp&quot;, &quot;falling&quot;, &quot;frosty&quot;, &quot;green&quot;, &quot;long&quot;, &quot;late&quot;, &quot;lingering&quot;, &quot;bold&quot;, &quot;little&quot;, &quot;morning&quot;, &quot;muddy&quot;, &quot;old&quot;, &quot;red&quot;, &quot;rough&quot;, &quot;still&quot;, &quot;small&quot;, &quot;sparkling&quot;, &quot;throbbing&quot;, &quot;shy&quot;, &quot;wandering&quot;, &quot;withered&quot;, &quot;wild&quot;, &quot;black&quot;, &quot;young&quot;, &quot;holy&quot;, &quot;solitary&quot;, &quot;fragrant&quot;, &quot;aged&quot;, &quot;snowy&quot;, &quot;proud&quot;, &quot;floral&quot;, &quot;restless&quot;, &quot;divine&quot;, &quot;polished&quot;, &quot;ancient&quot;, &quot;purple&quot;, &quot;lively&quot;, &quot;nameless&quot;}
	nouns := []string{&quot;waterfall&quot;, &quot;river&quot;, &quot;breeze&quot;, &quot;moon&quot;, &quot;rain&quot;, &quot;wind&quot;, &quot;sea&quot;, &quot;morning&quot;, &quot;snow&quot;, &quot;lake&quot;, &quot;sunset&quot;, &quot;pine&quot;, &quot;shadow&quot;, &quot;leaf&quot;, &quot;dawn&quot;, &quot;glitter&quot;, &quot;forest&quot;, &quot;hill&quot;, &quot;cloud&quot;, &quot;meadow&quot;, &quot;sun&quot;, &quot;glade&quot;, &quot;bird&quot;, &quot;brook&quot;, &quot;butterfly&quot;, &quot;bush&quot;, &quot;dew&quot;, &quot;dust&quot;, &quot;field&quot;, &quot;fire&quot;, &quot;flower&quot;, &quot;firefly&quot;, &quot;feather&quot;, &quot;grass&quot;, &quot;haze&quot;, &quot;mountain&quot;, &quot;night&quot;, &quot;pond&quot;, &quot;darkness&quot;, &quot;snowflake&quot;, &quot;silence&quot;, &quot;sound&quot;, &quot;sky&quot;, &quot;shape&quot;, &quot;surf&quot;, &quot;thunder&quot;, &quot;violet&quot;, &quot;water&quot;, &quot;wildflower&quot;, &quot;wave&quot;, &quot;water&quot;, &quot;resonance&quot;, &quot;sun&quot;, &quot;wood&quot;, &quot;dream&quot;, &quot;cherry&quot;, &quot;tree&quot;, &quot;fog&quot;, &quot;frost&quot;, &quot;voice&quot;, &quot;paper&quot;, &quot;frog&quot;, &quot;smoke&quot;, &quot;star&quot;}
	return adjs[rand.Intn(len(adjs))] + &quot;_&quot; + nouns[rand.Intn(len(nouns))]
}

func getRandomColor() string {
	return &quot;#&quot; + strconv.FormatInt(rand.Int63n(0xFFFFFF), 16)
}
</code></pre>
<h2 id="settingupthefrontend">Setting up the frontend</h2>
<p>All of the frontend files live in the <code>/static</code> directory.</p>
<h3 id="indexhtml">index.html</h3>
<p>You can find the full <a href="https://github.com/ScaleDrone/golang-chat-tutorial/blob/master/static/index.html"><code>index.html</code> file from here</a>. The file contains:</p>
<ul>
<li>Scaledrone JavaScript library script tag</li>
<li>Reference to the <code>script.js</code> file</li>
<li>HTML Markup</li>
<li>CSS Styles</li>
</ul>
<h3 id="scriptjs">script.js</h3>
<p>You can find the full <a href="https://github.com/ScaleDrone/golang-chat-tutorial/blob/master/static/script.js"><code>script.js</code> file from here</a>.</p>
<p>Let's get started by connecting to Scaledrone and then authenticating ourselves by making a POST request to our Go authentication server at <code>http://localhost:8080/auth</code>. Once we receive the JSON Web Token, we pass it on to Scaledrone.</p>
<pre><code class="language-js">// 👇 PS! Replace this with your own channel ID 🚨
const CLIENT_ID = 'YOUR_SCALEDRONE_ID';

// public room
const PUBLIC_ROOM_NAME = 'observable-room';
// array of connected memebers
let members = [];
// the session user
let me;
// keeping track of which room the user has selected
let selectedRoom = PUBLIC_ROOM_NAME;
// room name to messages map, this is used to store messages for displaying them
// at a later state
const roomMessages = {};

const drone = new Scaledrone(CLIENT_ID);

drone.on('open', error =&gt; {
  if (error) {
    return console.error(error);
  }
  // get JWT from the Go server for the clientID
  const formData = new FormData();
  formData.append('clientID', drone.clientId);
  fetch('/auth', {body: formData, method: 'POST'})
    .then(res =&gt; res.text())
    .then(jwt =&gt; drone.authenticate(jwt));
});

drone.on('authenticate', error =&gt; {
  if (error) {
    return console.error(error);
  }
  console.log('Successfully connected to Scaledrone');
  joinPublicRoom();
  joinPersonalRoom();
});
</code></pre>
<p>Once we've successfully connected and authenticated ourselves, we'll subscribe to messages both from the public room as well as our own private room (for private messages sent to us).</p>
<p>As all users subscribe to the public room, we can use it for detecting who is connected to the app as well as get notified when new users join and leave.</p>
<p>This is the most complex section of this tutorial but don't be alarmed. It's actually quite repetitive, and I added comments that will help you understand what's going on.</p>
<pre><code class="language-js">// Start subscribing to messages from the public room
function joinPublicRoom() {
  const publicRoom = drone.subscribe(PUBLIC_ROOM_NAME);
  publicRoom.on('open', error =&gt; {
    if (error) {
      return console.error(error);
    }
    console.log(`Successfully joined room ${PUBLIC_ROOM_NAME}`);
  });

  // Received array of members currently connected to the public room
  publicRoom.on('members', m =&gt; {
    members = m;
    me = members.find(m =&gt; m.id === drone.clientId);
    DOM.updateMembers();
  });

  // New member joined the public room
  publicRoom.on('member_join', member =&gt; {
    members.push(member);
    DOM.updateMembers();
  });

  // Member left public room (closed browser tab)
  publicRoom.on('member_leave', ({id}) =&gt; {
    const index = members.findIndex(member =&gt; member.id === id);
    members.splice(index, 1);
    DOM.updateMembers();
  });

  // Received public message
  publicRoom.on('message', message =&gt; {
    const {data, member} = message;
    if (member &amp;&amp; member !== me) {
      addMessageToRoomArray(PUBLIC_ROOM_NAME, member, data);
      if (selectedRoom === PUBLIC_ROOM_NAME) {
        DOM.addMessageToList(data, member);
      }
    }
  });
}

// Start subscribing to messages from my private room (PMs to me)
function joinPersonalRoom() {
  const roomName = createPrivateRoomName(drone.clientId);
  const myRoom = drone.subscribe(roomName);
  myRoom.on('open', error =&gt; {
    if (error) {
      return console.error(error);
    }
    console.log(`Successfully joined room ${roomName}`);
  });

  myRoom.on('message', message =&gt; {
    const {data, clientId} = message;
    const member = members.find(m =&gt; m.id === clientId);
    if (member) {
      addMessageToRoomArray(createPrivateRoomName(member.id), member, data);
      if (selectedRoom === createPrivateRoomName(clientId)) {
        DOM.addMessageToList(data, member);
      }
    } else {
      /* Message is sent from golang using the REST API.
       * You can handle it like a regular message but it won't have a connection
       * session attached to it (this means no member argument)
       */
    }
  });
}

drone.on('close', event =&gt; {
  console.log('Connection was closed', event);
});

drone.on('error', error =&gt; {
  console.error(error);
});

function changeRoom(name, roomName) {
  selectedRoom = roomName;
  DOM.updateChatTitle(name);
  DOM.clearMessages();
  if (roomMessages[roomName]) {
    roomMessages[roomName].forEach(({data, member}) =&gt;
      DOM.addMessageToList(data, member)
    );
  }
}

function createPrivateRoomName(clientId) {
  return `private-room-${clientId}`;
}

function addMessageToRoomArray(roomName, member, data) {
  console.log('add', roomName, member.id, data);
  roomMessages[roomName] = roomMessages[roomName] || [];
  roomMessages[roomName].push({member, data});
}
</code></pre>
<p>Lastly, we'll be writing the code that directly manipulates the DOM and renders our application. We won't be using any fancy frameworks as the frontend can easily be built with under a hundred lines of native JavaScript:</p>
<pre><code class="language-js">const DOM = {
  elements: {
    me: document.querySelector('.me'),
    membersList: document.querySelector('.members-list'),
    messages: document.querySelector('.messages'),
    input: document.querySelector('.message-form__input'),
    form: document.querySelector('.message-form'),
    chatTitle: document.querySelector('.chat-title'),
    room: document.querySelector('.room'),
  },

  // Send message to Scaledrone and clear the input
  sendMessage() {
    const {input} = this.elements;
    const value = input.value;
    if (value === '') {
      return;
    }
    input.value = '';
    drone.publish({
      room: selectedRoom,
      message: value,
    });
    addMessageToRoomArray(selectedRoom, me, value);
    this.addMessageToList(value, me);
  },

  // Create DOM element with member name and color
  createMemberElement(member) {
    const { name, color } = member.authData;
    const el = document.createElement('div');
    el.appendChild(document.createTextNode(name));
    el.className = 'member';
    el.style.color = color;
    if (member !== me) {
      // Listen to user clicking on another user
      el.addEventListener('click', () =&gt;
        changeRoom(member.authData.name, createPrivateRoomName(member.id))
      );
    }
    return el;
  },

  // Rerender the list of connected members
  updateMembers() {
    this.elements.me.innerHTML = '';
    this.elements.me.appendChild(this.createMemberElement(me));
    this.elements.membersList.innerHTML = '';
    members.filter(m =&gt; m !== me).forEach(member =&gt;
      this.elements.membersList.appendChild(this.createMemberElement(member))
    );
  },

  // Create a DOM element for the message
  createMessageElement(text, member) {
    const el = document.createElement('div');
    el.appendChild(this.createMemberElement(member));
    el.appendChild(document.createTextNode(text));
    el.className = 'message';
    return el;
  },

  // Add message element to the messages container
  addMessageToList(text, member) {
    const el = this.elements.messages;
    const wasTop = el.scrollTop === el.scrollHeight - el.clientHeight;
    el.appendChild(this.createMessageElement(text, member));
    if (wasTop) {
      el.scrollTop = el.scrollHeight - el.clientHeight;
    }
  },

  updateChatTitle(roomName) {
    this.elements.chatTitle.innerText = roomName;
  },

  clearMessages() {
    this.elements.messages.innerHTML = '';
  },
};
// Listen to submitting the input form
DOM.elements.form.addEventListener('submit', () =&gt;
  DOM.sendMessage()
);
// Listen to user clicking on the public room label
DOM.elements.room.addEventListener('click', () =&gt;
  changeRoom('Public room', PUBLIC_ROOM_NAME)
);
</code></pre>
<h2 id="bravoyouaredone">Bravo! You are done. 🐹✈️</h2>
<p>If anything was left unclear from this tutorial, I recommend grabbing the full source code from <a href="https://github.com/ScaleDrone/golang-chat-tutorial">GitHub</a>, replacing the channel ID and channel secret and running the project.</p>
<p>And as always, if you have any questions or feedback feel free to <a href="https://www.scaledrone.com/contact">contact us</a>.</p>
<img src="https://www.scaledrone.com/blog/content/images/2019/02/go-chat-screenshot-1.png" srcset="/blog/content/images/2019/02/go-chat-screenshot@2x-1.png 2x" style="max-height:500px" alt="Go Chat App Tutorial: Build a Real-time Chat"></div>]]></content:encoded></item><item><title><![CDATA[iOS Chat Tutorial Part 2: Typing Indicators and List of Online Users]]></title><description><![CDATA[<div class="kg-card-markdown"><p>Adding a chat screen to your app can make it easier for your users to communicate, making your app more engaging and useful. In <a href="https://www.scaledrone.com/blog/ios-chat-tutorial/">Part 1 of the iOS Chat Tutorial</a>, we created a basic chat app using Scaledrone and MessageKit.</p>
<p>In this part, we'll expand our chat app to</p></div>]]></description><link>https://www.scaledrone.com/blog/ios-chat-tutorial-part-2/</link><guid isPermaLink="false">5b9f9375e14c9423cd8d75c9</guid><dc:creator><![CDATA[Marin Bencevic]]></dc:creator><pubDate>Tue, 08 Jan 2019 15:10:52 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>Adding a chat screen to your app can make it easier for your users to communicate, making your app more engaging and useful. In <a href="https://www.scaledrone.com/blog/ios-chat-tutorial/">Part 1 of the iOS Chat Tutorial</a>, we created a basic chat app using Scaledrone and MessageKit.</p>
<p>In this part, we'll expand our chat app to include <strong>typing indicators</strong> and a <strong>list of online members</strong>. These can be useful features in any chat app and can both be easily implemented with Scaledrone. Let's get started!</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2018/09/Screen-Shot-2018-09-17-at-13.47.07.png" alt=""></p>
<blockquote>
<p>Note: This tutorial starts off at the end of part 1. To follow along, either go trough part 1 of this tutorial, or download <a href="https://github.com/ScaleDrone/ios-chat-tutorial/tree/master">the source code</a> for part 1 of this tutorial.</p>
</blockquote>
<h2 id="showingonlinemembers">Showing Online Members</h2>
<p>We'll start by adding presence indicators for online users. We'll do this by adding another view controller that shows online users in a table view. This view controller will be accessed from a navigation item from the main view controller of our app.</p>
<p>Open the project from part 1 of this tutorial, and open <strong>AppDelegate.swift</strong>. Change the contents of <code>application(_:didFinishLaunchingWithOptions:)</code> to the following:</p>
<pre><code class="language-swift">let navigationController = UINavigationController(
  rootViewController: ViewController())
window?.rootViewController = navigationController
window?.makeKeyAndVisible()
return true
</code></pre>
<p>Instead of presenting the <code>ViewController</code>, this will make sure the first view controller in our app is a navigation controller. This will allow us to show the list of online members.</p>
<p>Let's build that list. Create a new <code>UITableViewController</code> subclass file called <code>MembersViewController</code>. This class will observe members joining and leaving, and will update a table view accordingly. Add the following to the class:</p>
<pre><code class="language-swift">class MembersViewController: UITableViewController {

  var members: [Member] = []
	
  override func tableView(
    _ tableView: UITableView, 
    numberOfRowsInSection section: Int) -&gt; Int {
  
    return members.count
  }
	
  override func tableView(
    _ tableView: UITableView, 
    cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {
  
    let cell = UITableViewCell()
    cell.textLabel?.text = members[indexPath.row].name
    return cell
  }
}
</code></pre>
<p>This is standard <code>UITableViewDataSource</code> code to make sure we display the users. Next, we need to update the table view when the list of members changes. We'll do this by using <strong>Notification Center</strong>. <code>ChatService</code> will post a new notification when the list of online members changes. Our new view controller will listen to those notifications.</p>
<p>Go into <strong>ChatService.swift</strong> and add the following extension to the top of the file:</p>
<pre><code class="language-swift">extension Notification.Name {
  static let MembersChanged = Notification.Name(&quot;MembersChanged&quot;)
}
</code></pre>
<p>This will make sure we can use a notification for when the members are changed. Now, back in <strong>MembersViewController.swift</strong> add these methods to the file:</p>
<pre><code class="language-swift">override func viewDidLoad() {
  super.viewDidLoad()
  NotificationCenter.default.addObserver(self,
    selector: #selector(membersChanged),
    name: .MembersChanged,
    object: nil)
}

deinit {
  NotificationCenter.default.removeObserver(self)
}
</code></pre>
<p>When the view controller loads, we'll subscribe to notifications matching the name we defined earlier. If the object gets deleted, we have to make sure to unsubscribe from the notification. When a new notification comes in, <code>membersChanged</code> will handle that notification, so let's implement that method:</p>
<pre><code class="language-swift">@objc func membersChanged(notification: Notification) {
  guard let newMembers = notification.object as? [Member] else {
    return
  }
  
  self.members = newMembers
  tableView.reloadData()
}
</code></pre>
<p>The notification includes new online members, so we have to pull them out of the notification and update our UI.</p>
<p>That's our table view controller implemented, but we need to show it on the screen. To do this, we'll add a new navigation item to our main view controller. Open <strong>ViewController.swift</strong> and add the following code to the top of <code>viewDidLoad</code>:</p>
<pre><code class="language-swift">let item = UIBarButtonItem(
  title: &quot;0&quot;,
  style: .plain,
  target: self,
  action: #selector(didTapMembersButton))
navigationItem.setRightBarButton(item, animated: false)
</code></pre>
<p>We create a new <code>UIBarButtonItem</code> and add it to the navigation bar. The title of this item will be the number of currently logged in users, which, for now, is 0. When the user taps the button, the <code>didTapUsersButton</code> method will get called:</p>
<pre><code class="language-swift">@objc func didTapMembersButton() {
  let vc = MembersViewController()
  vc.members = self.members
  navigationController?.pushViewController(vc, animated: true)
}
</code></pre>
<p>Inside the method we'll create the members view controller and push it onto the navigation stack.</p>
<p>I mentioned earlier the title of the button will change to show the number of currently logged in members. To do this, we'll subscribe to the same notification as in the members view controller. Add the following to <code>viewDidLoad</code>:</p>
<pre><code class="language-swift">NotificationCenter.default.addObserver(
  self,
  selector: #selector(membersChanged),
  name: .MembersChanged,
  object: nil)
</code></pre>
<p>We're going to keep track of online members inside an array, so let's add a property to the class:</p>
<pre><code class="language-swift">var members: [Member] = []
</code></pre>
<p>Also add the following two methods, much like you did in the members view controller:</p>
<pre><code class="language-swift">@objc func membersChanged(notification: Notification) {
  guard let newMembers = notification.object as? [Member] else {
    return
  }
  
  self.members = newMembers
  navigationItem.rightBarButtonItem?.title = &quot;\(newMembers.count)&quot;
}

deinit {
  NotificationCenter.default.removeObserver(self)
}
</code></pre>
<p>This will make sure we update the title whenever the number of members changes.</p>
<p>If you run the project now, you'll see a new button in the navigation bar which will lead you to an empty list of members.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2018/09/Screen-Shot-2018-09-17-at-13.51.51.png" alt=""></p>
<p>Now it's time to actually listen to when new members join our room.</p>
<h2 id="listeningtochanges">Listening to Changes</h2>
<p>Scaledrone supports <strong>observable rooms</strong>. These are like regular rooms, i.e. members that join a room receive notifications sent to that room. They have an additional benefit, though: you can observe changes to the room. Changes like when a new member joins or leaves, which is exactly what we need.</p>
<p>In our <code>ChatService</code>, we'll maintain an array of current members in the room which we'll set when we first join a room. When a user leaves or joins, we'll update the array to reflect that change.</p>
<p>Open <strong>ChatService.swift</strong> and add the following property to the top of the class:</p>
<pre><code class="language-swift">private(set) var members: [Member] = [] {
  didSet {
    NotificationCenter.default.post(name: .MembersChanged, object: members)
  }
}
</code></pre>
<p>This is our array of members in the room. Whenever we make a change to this list, we'll post a notification to update our UI.</p>
<p>Before we continue, we need to make sure we can easily construct a <code>Member</code> instance from Scaledrone's <code>ScaledroneMember</code> class. <code>ScaledroneMember</code> contains the client data (as JSON) that was mentioned in Part 1. Add the following extension to the top of the file:</p>
<pre><code class="language-swift">extension Member {
  init?(scaledroneMember: ScaledroneMember) {
    guard let data = scaledroneMember.clientData else {
      return nil
    }
    self.init(fromJSON: data)
  }
}
</code></pre>
<p>To observe changes to a room, we have to implement <code>ScaledroneObservableRoomDelegate</code>, we'll do this by adding the following extension to the bottom of the file:</p>
<pre><code class="language-swift">extension ChatService: ScaledroneObservableRoomDelegate {
  func scaledroneObservableRoomDidConnect(
    room: ScaledroneRoom, 
    members: [ScaledroneMember]) {
    
    self.members = members.compactMap(Member.init)
  }
  
  func scaledroneObservableRoomMemberDidJoin(
    room: ScaledroneRoom, 
    member: ScaledroneMember) {
    
    guard let newMember = Member(scaledroneMember: member) else {
      return
    }
    members.append(newMember)
  }
  
  func scaledroneObservableRoomMemberDidLeave(
    room: ScaledroneRoom, 
    member: ScaledroneMember) {
    
    guard let leftMember = Member(scaledroneMember: member) else {
      return
    }
    
    if let index = members
      .firstIndex(where: { $0.name == leftMember.name }) {
      members.remove(at: index)
    }
  }
}
</code></pre>
<p>When we first join the room, we'll set the members array to a list of members we get from Scaledrone. When a new member joins, we'll add them to the array. When a member leaves, we'll find them in the array and remove them. This will make sure our array of members is always up to date.</p>
<p>Finally, we need to make sure we set <code>ChatService</code> as the observable delegate by adding the following line to the bottom of <code>scaledroneDidConnect</code>:</p>
<pre><code class="language-swift">room?.observableDelegate = self
</code></pre>
<p>Run the app now. You should see the button's title update to &quot;1&quot;, and when you tap on it, the list should show your username. If you open the app in another simulator or device, you should also see the new user in the list. Pretty cool, right?</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2018/09/Simulator-Screen-Shot---iPhone-8---2018-09-17-at-09.08.10.png" alt=""></p>
<h2 id="addingatypingindicator">Adding a Typing Indicator</h2>
<p>To show a typing indicator when a user is writing, a label will pop up from the bottom of the chat screen. This label will contain the names of all the users that are currently typing.</p>
<p>Let's start building this. In <strong>ViewController.swift</strong>, add the following lazy property to the top of the class, to create the label.</p>
<pre><code class="language-swift">lazy var typingLabel: UILabel = {
  let label = UILabel()
  label.text = &quot;&quot;
  label.textAlignment = .center
  label.backgroundColor = messageInputBar.backgroundView.backgroundColor!
  return label
}()
</code></pre>
<p>Now we need to add it to the input bar. MessageKit has a stack based UI, so adding a new view to the input bar is pretty easy. Add the following line to the top of <code>viewDidLoad</code>:</p>
<pre><code class="language-swift">messageInputBar.topStackView.addArrangedSubview(typingLabel)
</code></pre>
<p>To make the label appear and disappear we'll simply hide it inside an animation block. Add the following two methods to the class:</p>
<pre><code class="language-swift">func showTypingLabel() {
  UIView.animate(withDuration: 0.3) {
    self.typingLabel.isHidden = false
  }
}

func hideTypingLabel() {
  UIView.animate(withDuration: 0.3) {
    self.typingLabel.isHidden = true
  }
}
</code></pre>
<p>Now that we have the UI, we need to hook up some logic to call these functions. Add a new property to the top of the class:</p>
<pre><code class="language-swift">var typingMembers: [Member] = [] {
  didSet {
    let otherMembers = typingMembers.filter { $0.name != member.name }
    switch otherMembers.count {
    case 0:
      hideTypingLabel()
    case 1:
      typingLabel.text = &quot;\(otherMembers[0].name) is typing&quot;
      showTypingLabel()
    default:
      let names = otherMembers.map { $0.name }.joined(separator: &quot;, &quot;)
      typingLabel.text = &quot;\(names) are typing&quot;
      showTypingLabel()
    }
  }
}
</code></pre>
<p>This array will hold all of the members which are currently typing. Whenever this array changes, we'll update the state of the typing label. We'll filter out the current user from the array, since they already know if they're typing or not. :)</p>
<p>We need to modify <strong>ChatService.swift</strong> to tell us when the user is typing. Add a new property to the <code>ChatService</code> class, right underneath where you declared the message callback:</p>
<pre><code class="language-swift">private let typingCallback: (Member, _ isTyping: Bool)-&gt; Void
</code></pre>
<p>Now modify the initializer of the class to the following:</p>
<pre><code class="language-swift">init(
  member: Member,
  onRecievedMessage: @escaping (Message)-&gt; Void,
  onMemberTypingStatusChanged: @escaping (Member, _ isTyping: Bool)-&gt; Void) {
  
  self.messageCallback = onRecievedMessage
  self.typingCallback = onMemberTypingStatusChanged
  self.scaledrone = Scaledrone(
    channelID: &quot;YOUR-CHANNEL-ID&quot;,
    data: member.toJSON)
  scaledrone.delegate = self
}
</code></pre>
<blockquote>
<p>Note: If you're copying and pasting code, make sure to set your channel ID!</p>
</blockquote>
<p>This lets the <code>ChatService</code> notify the <code>ViewController</code> about new users that are typing, just like it's doing with messages.</p>
<p>To let our user notify the rest of the room of their typing status, we'll send a simple message containing a boolean for whether the user is typing or not. Add these two methods to the class:</p>
<pre><code class="language-swift">func startTyping() {
  room?.publish(message: [&quot;typing&quot;: true])
}

func stopTyping() {
  room?.publish(message: [&quot;typing&quot;: false])
}
</code></pre>
<p>Since we attached the client data to our member, we don't need to send any additional information in the message.</p>
<p>We also need to parse this message when we receive it. Modify the contents of <code>scaledroneRoomDidReceiveMessage</code> to the following:</p>
<pre><code class="language-swift">func scaledroneRoomDidReceiveMessage(
  room: ScaledroneRoom,
  message: Any,
  member: ScaledroneMember?) {
  
  guard
    let memberData = member?.clientData,
    let member = Member(fromJSON: memberData)
  else {
    print(&quot;Could not parse data.&quot;)
    return
  }
  
  if let typingData = message as? [String: Bool],
    let isTyping = typingData[&quot;typing&quot;] {
    
    typingCallback(member, isTyping)
  
  } else if let text = message as? String {
    
    let message = Message(
      member: member,
      text: text,
      messageId: UUID().uuidString)
    messageCallback(message)
  }
}
</code></pre>
<p>This is a lengthy block of code but you have already seen most of it. If we find the typing data, we'll call the typing callback. Otherwise if we receive a new chat message, we'll call the message callback.</p>
<p>That's all we need to do in <code>ChatService</code>. Now we need to fix that error in <strong>ViewController.swift</strong>. Modify the part where you create the chat service in <code>viewDidLoad</code> to the following:</p>
<pre><code class="language-swift">chatService = ChatService(
  member: member,
  onRecievedMessage: { [weak self] message in
    self?.messages.append(message)
    self?.messagesCollectionView.reloadData()
    self?.messagesCollectionView.scrollToBottom(animated: true)
  },
  onMemberTypingStatusChanged: { [weak self] (member, isTyping) in
    guard let `self` = self else { return }
    if isTyping {
      if !self.typingMembers.contains { $0.name == member.name } {
        self.typingMembers.append(member)
      }
    } else {
      if let index = self.typingMembers
        .firstIndex(where: { $0.name == member.name }) {
        self.typingMembers.remove(at: index)
      }
    }
  })
</code></pre>
<p>We'll add a new parameter to the initializer for handling the typing status. This is similar code to the way we handle online members: if a new user starts typing, we'll add them to the array. If they stop typing, we'll find them and remove them from the array.</p>
<p>One final thing we need to do is to actually call the methods to send the typing status updates. Thankfully, <code>MessageInputBarDelegate</code> has a method to track changes to the input bar's text. That's exactly what we need. Add the following method to the <code>MessageInputBarDelegate</code> extension:</p>
<pre><code class="language-swift">func messageInputBar(
  _ inputBar: MessageInputBar, 
  textViewTextDidChangeTo text: String) {
  
  if text.isEmpty {
    chatService.stopTyping()
  } else {
    chatService.startTyping()
  }
}
</code></pre>
<p>Finally, add the following line to the end of <code>messageInputBar(_:didPressSendButtonWith:)</code>:</p>
<pre><code class="language-swift">chatService.stopTyping()
</code></pre>
<p>Now the rest of the room knows when the current user is typing.</p>
<p>Run the project on two different simulators. Try typing on one. You should see a label pop up from the other simulator saying that the user is typing.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2018/09/Simulator-Screen-Shot---iPhone-8---2018-09-17-at-09.17.58.png" alt=""></p>
<h2 id="easypeasy">Easy Peasy</h2>
<p>Now your chat app is starting to look like the real deal! WhatsApp better look out, there's a new player in town. :)</p>
<p><strong>You can find the full source code <a href="https://github.com/ScaleDrone/ios-chat-tutorial-part2">on GitHub</a>.</strong> This tutorial only scratched what Scaledrone can do for you and is the ideal basis for any of your future realtime needs.</p>
<p><em>Last Updated July 1, 2023</em></p>
</div>]]></content:encoded></item><item><title><![CDATA[iOS Machine Learning: Understanding The Basics]]></title><description><![CDATA[<div class="kg-card-markdown"><p>Machine learning is one of those terms in programming that sounds like you need a PhD from Stanford to understand, but is in reality pretty simple and easy to get started with. Here's some big words, don't panic: iOS lets you train your own convolutional neural network using transfer learning.</p></div>]]></description><link>https://www.scaledrone.com/blog/ios-machine-learning-understanding-the-basics/</link><guid isPermaLink="false">5c0fe3aa58db83073fd711eb</guid><category><![CDATA[iOS]]></category><dc:creator><![CDATA[Marin Bencevic]]></dc:creator><pubDate>Wed, 12 Dec 2018 11:25:45 GMT</pubDate><media:content url="https://www.scaledrone.com/blog/content/images/2018/12/piano-robot.jpg" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://www.scaledrone.com/blog/content/images/2018/12/piano-robot.jpg" alt="iOS Machine Learning: Understanding The Basics"><p>Machine learning is one of those terms in programming that sounds like you need a PhD from Stanford to understand, but is in reality pretty simple and easy to get started with. Here's some big words, don't panic: iOS lets you train your own convolutional neural network using transfer learning. By the end of this article you'll understand every term from the earlier sentence.</p>
<p>Apple also provides a bunch of different tools and frameworks for out-of-the-box machine learning features and it can be confusing to know how they all fit together and where you should start. This article will shine some light on this issue and point you in the right direction.</p>
<p>Recognizing cats is just one of the many uses for machine learning. You can process a sentence and detect its meaning or emotion. Speaking of emotions, you can tell whether a person is happy or sad from their photo. You can detect faces, landmarks, read text in an image or even get a textual description of an image. Machine learning is a simple but very flexible tool to solve all kinds of problems.</p>
<h2 id="howdoesmachinelearningwork">How does machine learning work?</h2>
<p>Let's think about how you would recognize a car in an image. You, as a human, have eventually learned that cars have some car-like features. Most of them have four wheels. They're roughly cuboid in shape. They usually have doors with windows on the sides and big windows at the front and back. We can go on and on, but you get the gist: you recognize a car by recognizing a set of car-like features.</p>
<p>Not all of those features are equally important. Some cars have a spoiler, but the absence of a spoiler does not automatically make something less car-like. However, missing two out of four wheels makes something much less car-like. In other words, some features carry more weight than others.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2018/12/640px-The_C1_Electric_Vehicle_by_Lit_Motors.jpg" alt="iOS Machine Learning: Understanding The Basics"><br>
<em>(Lit Motors C-1: Technically a car?; credit: <a href="https://www.flickr.com/photos/intelfreepress/8201892064/sizes/o/in/photostream/">Intel Free Press</a>)</em></p>
<p>Machine learning works the same way. A machine learning model contains a set of features, each with their own weight. The model will then recognize those features in the original image, sum them all up with their respective weights, and pop out a result.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2018/12/Rosenblattperceptron-1.png" alt="iOS Machine Learning: Understanding The Basics"><br>
<em>(credit: Perceptron. Mitchell, Machine Learning, p87.)</em></p>
<p>But summing up the features is easy: what's complex is finding those features and how much does each feature matter. When machine learning first started, humans would program in those features. They would analyze the data, find connections and patterns and program in those patterns.</p>
<p>The &quot;machine&quot; part was learning how much each feature actually matters. Each feature is multiplied by a weight, which can either increase of decrease how much it impacts the result. The model starts out with random weights. You then give it an image of a car and tell it &quot;this is a car.&quot; It will modify the weights so that the output for that image is &quot;car&quot;. You then repeat this process for thousands or millions of images, and you get back a model with correctly configured weights. This process is called <strong>training</strong>.</p>
<p>Because you configured the model with thousands of car images, the model should produce the output &quot;car&quot; for a new, never before seen, image.</p>
<p>Of course, lots of things can go wrong in this step. If you give the model only a few images, it might be very unsure of the answer. This is called <strong>under-fitting</strong>. However, giving the model <em>too much</em> information is also not good, because it will think only cars with spoilers are cars. This is called <strong>over-fitting</strong>. By selecting the correct features and giving it enough data we strike a balance between over-fitting and under-fitting.</p>
<p>To make sure our model behaves correctly we always take a bit of data we have and don't feed it to the model. We can then check what result we get from the model when we give it that data, so that we can check if it's working correctly or not. This is called <strong>validation</strong>.</p>
<h2 id="neuralnetworks">Neural Networks</h2>
<p>What I described here is <em>traditional</em> machine learning. Convolutional neural networks are a part of <em>deep</em> learning and they go one step further: in CNNs, even the features are learned automatically. All you do is adjust a few parameters, give it the inputs and outputs and the network does the rest. Pretty cool, right?</p>
<p>They do this by basically trying out what works, and discarding what doesn't. They might learn to recognize spoilers, but realize those don't really matter all that much. Or they might say wheels are somewhat important, but the number of wheels is <em>very</em> important.</p>
<p>At the end of this training process you get a convolutional neural network, which is a set of features and weights connected in layers to other features and weights.</p>
<img alt="iOS Machine Learning: Understanding The Basics" src="https://www.scaledrone.com/blog/content/images/2018/12/tensorflow.png" srcset="/blog/content/images/2018/12/tensorflow@2x.png 2x">
<p><em>(<a href="https://playground.tensorflow.org">https://playground.tensorflow.org</a>)</em></p>
<p>If you teach a human to recognize dogs, you can very easily teach them to recognize cats. Just tell them &quot;they're kind of like dogs, but more self-centered and smaller.&quot; CNNs are the same way. If you have a network that recognizes cats, you can easily reuse most of it for dogs. If you can recognize cars, you can probably also recognize motorcycles. Just take the same features but give them different weights!</p>
<p>This process of reusing networks is the standard way people use machine learning today. If you want your own image classifier, you take a network that's been trained on millions of images, and add your own bit of data to that. The network already knows how to recognize all kinds of features, so it can easily adapt to your specific images. This type of machine learning is called <strong>transfer learning</strong>.</p>
<h2 id="whatdoesiosoffer">What does iOS offer?</h2>
<p>As machine learning found its way into mainstream software development, Apple gave us lots of tools to use it. They range from easy to use libraries all the way to command line scripts to make your own models.</p>
<p>Here's an overview of the different machine learning tools Apple provides:</p>
<h3 id="vision">Vision</h3>
<p>Vision is a framework for working with images. It contains ready-made functions for detecting rectangles, text and barcodes (including QR codes). This is perfect for scanning loyalty or gift cards, receipts, stickers, documents and other rectangular objects.</p>
<p>Vision also offers easy to use face detection and tracking, so you can identify people inside photos and make sure the image is cropped correctly, the person is in focus and other cool things that improve the user's experience.</p>
<p>The first step in using Vision is to create a <code>VNImageRequestHandler</code>. This class is responsible for performing requests and calling completion handlers. It also keeps track of the image you want to work on.</p>
<pre><code class="language-swift">let requestHandler = VNImageRequestHandler(
    cgImage: image,
    options: [:])
</code></pre>
<p>Your next step is to create a request which will specify what the handler should do. In this case, we'll create a face detection request. These can be reused for multiple images, and you can use multiple requests in tandem on a single request handler, so you can detect, for instance, both rectangles and text at the same time.</p>
<pre><code class="language-swift">let request = VNDetectFaceRectanglesRequest { (request, error) in
    guard
        error != nil,
        let results = request.results as? [VNFaceObservation]
    else {
        print(error ?? &quot;results are incorrect&quot;)
        return
    }
    
    for result in results {
        print(&quot;Found a face at \(result.boundingBox.origin)&quot;)
    }
}
</code></pre>
<p>Finally, we perform the actual request.</p>
<pre><code class="language-swift">do {
    try requestHandler.perform([request])
} catch {
    print(error)
}
</code></pre>
<p>And that's pretty much all there is to it!</p>
<h3 id="naturallanguage">Natural Language</h3>
<p>A framework like Vision, but used for analyzing human languages. For instance, Natural Language lets you split a sentence into words and get information about each word, like if it's a number, an emoji or something else.</p>
<p>You can also detect unknown languages. Using <code>NLLanguageRecognizer</code> you can find out what's the most likely language of some string. This can help you automatically translate different languages for users, or to detect what language your users prefer and adjust your app accordingly.</p>
<p>Another big use case of Natural Language is linguistic tagging. Tagging gives you more information about your users input. You can split a sentence into nouns, verbs, adjectives etc. to parse natural language commands. You can also identify people or places that are mentioned in the sentence. This makes it easy to recognize things like &quot;Add a reminder to buy Suzan a present for her birthday.&quot;</p>
<p>If you want to get the names from a string, you can create an <code>NLTagger</code> with the <code>.nameType</code> tag scheme. This scheme also includes place and organization names, so the tagger will also detect those.</p>
<pre><code class="language-swift">let tagger = NLTagger(tagSchemes: [.nameType])
tagger.string = text
</code></pre>
<p>Next, call <code>enumerateTags</code> to get all the tags.</p>
<pre><code class="language-swift">tagger.enumerateTags(
    in: text.startIndex..&lt;text.endIndex,
    unit: .word,
    scheme: .nameType,
    using: { (tag, range) in
        if tag == .personalName {
            print(&quot;Found name: \(text[range])&quot;)
        }
        return true
	})
</code></pre>
<p>Since we're only interested in personal names, we'll print those out and ignore the other tags.</p>
<h3 id="coreml">Core ML</h3>
<p>Core ML lets you use your own custom machine learning models. This can be any model, from pre-made ones you found on the internet all the way to models you trained from scratch. You can use Core ML to load models and then combine them with Vision or Natural Language for things like object recognition or complex language processing.</p>
<p>Apple also provides a <a href="https://developer.apple.com/machine-learning/build-run-models/">set of ready-mode models</a> for working with images. Different models are good at different tasks. For general object recognition, I recommend starting with MobileNet as its optimized for mobile devices.</p>
<p>To use one of these models, simply download it and drag and drop it into your Xcode project. You can then use Core ML to instantiate that model.</p>
<pre><code class="language-swift">let model: VNCoreMLModel

do {
    model = try VNCoreMLModel(for: MobileNet().model)
} catch {
    print(error)
    return
}
</code></pre>
<p>To use it with vision we'll create a request just like in the Vision example, but this time it's a Core ML request with our model.</p>
<pre><code class="language-swift">let request = VNCoreMLRequest(
    model: model,
    completionHandler: { request, error in
        guard
            let results = request.results as? [VNClassificationObservation]
        else {
            return
        }
        
        for result in results {
            print(&quot;Found object: \(result.identifier)&quot;)
        }
	})
</code></pre>
<p>We can then perform this request just like in the above example. As you can see, you can add object recognition to your app in just a few lines of code!</p>
<h3 id="createml">Create ML</h3>
<p>While Core ML lets you <em>use</em> your own models, Create ML lets you create them from the data you have. Create ML is optimized for macOS and is integrated with Xcode's playgrounds, so it's the easiest way for iOS developers to train their own machine learning models.</p>
<p>To create an custom image classifier model, simply launch a new macOS playground and write these two lines of code:</p>
<pre><code class="language-swift">import CreateMLUI

let builder = MLImageClassifierBuilder()
builder.showInLiveView()
</code></pre>
<p>A new pane shows up in the assistant editor where you can drag and drop images organized by class. Each class needs to have its own folder. Drag and drop those folders and the training will start immediately.</p>
<p>You can also tweak settings like how many iterations it will take, or whether it should create new input images by morphing the original ones to make the model more robust.</p>
<p>This is where <strong>transfer learning</strong> kicks in: Apple has their own network to recognize images, so Create ML is <em>really</em> fast and produces tiny models, because it leans heavily on the knowledge it already has. Once you have a model you can use Core ML to add it to your app like in the example above.</p>
<h3 id="turicreate">Turi Create</h3>
<p>Used for the same purpose as Create ML, but it's a little more flexible and supports more types of models. The catch is that it's a Python library, so no Swift support!</p>
<p>But don't worry if you never used Python. Setting up Turi Create couldn't be easier, and Python is a very simple language that you can get the hang of pretty fast. Turi Create is out of scope for this article, but it's good to know that you can get even more flexibility when you run into issues with Create ML.</p>
<h2 id="startusingml">Start Using ML</h2>
<p>As you can see, Apple offers a lot of tools with varying levels of flexibility. If you need general object detection, you can easily use Vision. If you want to detect a specific object, you can build your own model with Create ML and use Core ML in combination with Vision to detect that object. Trust me, it sounds a lot more complicated than it really is.</p>
<p>I urge you to try some of these approaches out. Machine learning is hitting the mainstream hard, and it's here to stay. By learning more about it you can still be ahead of the curve and provide your users with cool features your competitors might not have. Good luck!</p>
</div>]]></content:encoded></item><item><title><![CDATA[Fixing Common Issues With the iOS Keyboard]]></title><description><![CDATA[<div class="kg-card-markdown"><p>Dealing with the keyboard on iOS can be a tricky task. The keyboard often hides important buttons or information or hides part of a scroll view. I can't log into my mobile banking app on an iPhone SE, because the keyboard pops up over the &quot;Login&quot; button. You</p></div>]]></description><link>https://www.scaledrone.com/blog/fixing-common-issues-with-the-ios-keyboard/</link><guid isPermaLink="false">5bb70aa658db83073fd711b9</guid><category><![CDATA[iOS]]></category><dc:creator><![CDATA[Marin Bencevic]]></dc:creator><pubDate>Thu, 22 Nov 2018 14:56:13 GMT</pubDate><media:content url="https://www.scaledrone.com/blog/content/images/2018/11/iphone-keyboard-issues@2x.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://www.scaledrone.com/blog/content/images/2018/11/iphone-keyboard-issues@2x.png" alt="Fixing Common Issues With the iOS Keyboard"><p>Dealing with the keyboard on iOS can be a tricky task. The keyboard often hides important buttons or information or hides part of a scroll view. I can't log into my mobile banking app on an iPhone SE, because the keyboard pops up over the &quot;Login&quot; button. You don't want those kinds of issues! In this tutorial, you'll see how to observe when the keyboard rises and falls, and how to adjust buttons and scroll views accordingly.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2018/10/Screenshot-2018-10-04-at-08.33.49.png" alt="Fixing Common Issues With the iOS Keyboard"></p>
<p>Here are the two most common issues that can happen when a keyboard appears:</p>
<ol>
<li>The keyboard covers a vital button, like the &quot;Login&quot; button in the example above. If you don't have a way to dismiss the keyboard, you have just locked your user out of using the app. Even if you <em>can</em> dismiss the keyboard, it's still not a very good user experience to have a button disappear.</li>
<li>The keyboard rises above a scroll view, hiding the content on the bottom. The scroll view's bounds will extend underneath the keyboard, making the content on the bottom of the scrollview invisible.</li>
</ol>
<p><img src="https://www.scaledrone.com/blog/content/images/2018/10/Screenshot-2018-10-04-at-07.58.16.png" alt="Fixing Common Issues With the iOS Keyboard"></p>
<p>In this tutorial, you'll find out how to fix these two issues in a general, reusable, and protocol-oriented way. The solution you will make will apply to almost any screen in your app.</p>
<p>Fixing the second issue is easy. You need to observe when the keyboard appears and get its height. Once you have the height you can increase the scroll view's content insets by that height. This adds padding to the bottom of the scroll view, so everything that was covered by the keyboard is pushed up.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2018/10/Screenshot-2018-10-04-at-07.57.48.png" alt="Fixing Common Issues With the iOS Keyboard"></p>
<p>To fix the issue with the keyboard covering a button, there are two things you can do:</p>
<ol>
<li>Make the whole screen scrollable, and then do the same things as mentioned above for the scroll view problem.</li>
<li>Push the button up (by manipulating its constraints or frame) so that it's no longer being covered by the keyboard. This will only work if you have space for all your UI elements above the keyboard. It also might require changing the margins of all the views so that the whole screen is more compact.</li>
</ol>
<p>Let's see these solutions in action!</p>
<h2 id="theappleway">The Apple Way</h2>
<p>The default ways Apple gives us to deal with this are cumbersome. To observe the keyboard rising, you need to subscribe to the <code>keyboardDidShowNotification</code>.</p>
<pre><code class="language-swift">class ViewController: UIViewController {

  override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(
      self,
      selector: #selector(keyboardWasShown),
      name: UIWindow.keyboardDidShowNotification,
      object: nil)
  }
  
  @objc func keyboardWasShown(_ notification: NSNotification) {
    
  }
}
</code></pre>
<p>Once you have the <code>NSNotification</code>, you can fetch the keyboard's size from the <code>userInfo</code> property of the notification. The key which holds the size is <code>keyboardFrameEndUserInfoKey</code>. This gives you the frame of the keyboard at the <em>end</em> of the rising animation. You can also track each frame change, as well as see the frame at the start of the animation. There aren't very useful to us, so we'll stick with the end frame.</p>
<pre><code class="language-swift">@objc private func keyboardWasShown(_ notification: NSNotification) {
  let key = UIResponder.keyboardFrameBeginUserInfoKey
  guard let frameValue = notification.userInfo?[key] as? NSValue else {
    return
  }
  
  let frame = frameValue.cgRectValue
}
</code></pre>
<p>If you're dealing with a scroll view, once you have the frame it's easy enough to increase its bottom inset. Simply add the following lines to the end of <code>keyboardWasShown</code>:</p>
<pre><code class="language-swift">scrollView.contentInset.bottom = frame.size.height
scrollView.scrollIndicatorInsets.bottom = frame.size.height
</code></pre>
<p>If you're dealing with a hidden button, the exact steps to fix the issue depend on your specific UI. The general gist is that you have to raise the button by a certain amount so that it's visible. In other words, the button's offset from the bottom of the screen needs to be greater than (or equal) to the keyboard's height.</p>
<p>You also have to undo these changes once the keyboard goes back down. To do this, you can observe a second notification, <code>keyboardWillHideNotification</code>. This notification will get triggered before the keyboard hides. Add the following lines to the bottom of <code>viewDidLoad</code>:</p>
<pre><code class="language-swift">NotificationCenter.default.addObserver(
   self,
   selector: #selector(keyboardWasShown),
   name: UIResponder.keyboardWillHideNotification,
   object: nil)
</code></pre>
<p>Next, add the following method to the class:</p>
<pre><code class="language-swift">@objc func keyboardWillHide() {
  scrollView.contentInset.bottom = 0
  scrollView.scrollIndicatorInsets.bottom = 0
}
</code></pre>
<p>Since we know that the keyboard's height is going back to zero, there's no need to fetch the frame this time. Simply reset the insets to their initial values.</p>
<p>And that's it! Our screen now no longer has any issues.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2018/10/Simulator-Screen-Shot---iPhone-8---2018-10-04-at-06.54.12.png" alt="Fixing Common Issues With the iOS Keyboard"></p>
<p>While this solution works, it's not convenient. Having to write these boilerplate lines over and over again for every screen will get pretty tiring. Thankfully, we can use some cool Swift features to reduce the boilerplate to a minimum.</p>
<h2 id="anicerway">A Nicer Way</h2>
<p>Like a good Swift developer, we'll start with a protocol!</p>
<pre><code class="language-swift">public protocol KeyboardObserving: class {
  func keyboardWillShow(withSize size: CGSize)
  func keyboardWillHide()
}
</code></pre>
<p>Anyone who is interested in getting notified when the keyboard appears can implement this protocol. We'll hide the protocol's logic in an extension which will subscribe to the notifications. Coming up is a lengthy block of code but don't panic: you've already seen most of this code earlier in the tutorial.</p>
<pre><code class="language-swift">extension KeyboardObserving {
  
  public func addKeyboardObservers(to notificationCenter: NotificationCenter) {
    notificationCenter.addObserver(
      forName: UIResponder.keyboardWillShowNotification,
      object: nil,
      queue: nil,
      using: { [weak self] notification in
        let key = UIResponder.keyboardFrameEndUserInfoKey
        guard let keyboardSizeValue = notification.userInfo?[key] as? NSValue else {
          return;
        }
        
        let keyboardSize = keyboardSizeValue.cgRectValue
        self?.keyboardWillShow(withSize: keyboardSize.size)
    })
    notificationCenter.addObserver(
      forName: UIResponder.keyboardWillHideNotification,
      object: nil,
      queue: nil,
      using: { [weak self] _ in
        self?.keyboardWillHide()
    })
  }
  
  public func removeKeyboardObservers(from notificationCenter: NotificationCenter) {
    notificationCenter.removeObserver(
      self,
      name: UIResponder.keyboardWillHideNotification,
      object: nil)
    notificationCenter.removeObserver(
      self,
      name: UIResponder.keyboardWillShowNotification,
      object: nil)
  }
}
</code></pre>
<p>This is the same code as in the view controller before. The difference is that anyone who implements the protocol can use these functions. We can reduce a lot of our view controller's code this way.</p>
<pre><code class="language-swift">class ViewController: UIViewController, KeyboardObserving {
  
  @IBOutlet var scrollView: UIScrollView!

  override func viewDidLoad() {
    super.viewDidLoad()
    addKeyboardObservers(to: .default)
  }
  
  func keyboardWillShow(withSize size: CGSize) {
    scrollView.contentInset.bottom = size.height
    scrollView.scrollIndicatorInsets.bottom = size.height
  }
  
  func keyboardWillHide() {
    scrollView.contentInset.bottom = 0
    scrollView.scrollIndicatorInsets.bottom = 0
  }
  
  deinit {
    removeKeyboardObservers(from: .default)
  }
}
</code></pre>
<p>That's 10 lines of code less just in this one view controller. You'll probably want to observe the keyboard in many screens in your app, so this little bit of extra work pays off immensely!</p>
<h2 id="goingastepfurther">Going a Step Further</h2>
<p>Setting the scroll view's content insets when the keyboard appears is a frequent requirement. You'll have to do this in almost any screen that combines a keyboard and a scroll view. Why not go a step further without protocols, and define one for that specific use case.</p>
<pre><code class="language-swift">public protocol ScrollViewKeyboardObserving: KeyboardObserving {
  var keyboardObservingScrollView: UIScrollView { get }
}
</code></pre>
<p>Swift lets us inherit from protocols. Since we're making a specific case of the <code>KeyboardObserving</code> protocol, it makes sense to inherit from it. Our only requirement is that the implementer of the protocol exposes a scroll view that we can set insets on.</p>
<p>Next, we'll define an extension that does the same thing as in our view controller.</p>
<pre><code class="language-swift">extension ScrollViewKeyboardObserving {
  func keyboardWillShow(withSize size: CGSize) {
    keyboardObservingScrollView.contentInset.bottom = size.height
    keyboardObservingScrollView.scrollIndicatorInsets.bottom = size.height
  }
  
  func keyboardWillHide() {
    keyboardObservingScrollView.contentInset.bottom = 0
    keyboardObservingScrollView.scrollIndicatorInsets.bottom = 0
  }
}
</code></pre>
<p>This extension implements the <code>KeyboardObserving</code> protocol, so anyone that conforms to <code>ScrollViewKeyboardObserving</code> will automatically conform to that protocol.</p>
<p>Going back to our view controller, we can rewrite it as the following:</p>
<pre><code class="language-swift">class ViewController: UIViewController, ScrollViewKeyboardObserving {
  
  @IBOutlet var scrollView: UIScrollView!
  
  var keyboardObservingScrollView: UIScrollView {
    return scrollView
  }

  override func viewDidLoad() {
    super.viewDidLoad()
    addKeyboardObservers(to: .default)
  }
  
  deinit {
    removeKeyboardObservers(from: .default)
  }
}
</code></pre>
<p>Reducing even more lines of code. We used Swift features like protocol-oriented programming, extensions and inheritance to reduce our boilerplate to an absolute minimum. Dealing with scroll views is now a simple task of declaring a variable and calling two functions.</p>
<h2 id="whybother">Why Bother?</h2>
<p>The original solution worked fine, so why go through the trouble of making things reusable? Well, first of all, we have fewer lines of code. Since code is the single largest source of bugs, reducing the number of code reduces bugs. We also have a single point to change, refactor and debug the behaviour. If the code were copied and pasted into a bunch of view controllers, it would have been a pain to fix a bug in each instance of the code.</p>
<p>Making things reusable makes life easier on both you and other developers reading your code. There's no reason why you should make your life harder: always think of coding things in the most general way you can.</p>
</div>]]></content:encoded></item><item><title><![CDATA[WebSockets vs. Server-Sent Events (SSEs)]]></title><description><![CDATA[<div class="kg-card-markdown"><p>This blog post compares two similar technologies: WebSockets and SSEs. We'll describe their similarities and differences. Next, we'll look at best use cases for both. Finally, we’ll have a look at the additional features offered by ScaleDrone's Enhanced WebSockets.</p>
<h2 id="websocketsandssesthesimilarities">WebSockets and SSEs: the similarities</h2>
<p>The last two blog posts</p></div>]]></description><link>https://www.scaledrone.com/blog/websockets-vs-server-sent-events-sses/</link><guid isPermaLink="false">5b6488c5e14c9423cd8d756a</guid><category><![CDATA[SSE]]></category><dc:creator><![CDATA[Eric van Rees]]></dc:creator><pubDate>Thu, 11 Oct 2018 08:20:21 GMT</pubDate><media:content url="https://www.scaledrone.com/blog/content/images/2018/10/anh-duy-410255-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://www.scaledrone.com/blog/content/images/2018/10/anh-duy-410255-unsplash.jpg" alt="WebSockets vs. Server-Sent Events (SSEs)"><p>This blog post compares two similar technologies: WebSockets and SSEs. We'll describe their similarities and differences. Next, we'll look at best use cases for both. Finally, we’ll have a look at the additional features offered by ScaleDrone's Enhanced WebSockets.</p>
<h2 id="websocketsandssesthesimilarities">WebSockets and SSEs: the similarities</h2>
<p>The last two blog posts covered <a href="https://www.scaledrone.com/blog/introduction-to-websockets/">WebSocket</a> and <a href="https://www.scaledrone.com/blog/getting-to-know-server-sent-events-sse/">SSE technology</a> in detail. Let´s now have a look at the similarities between the two. For starters, both WebSockets and SSEs make use of HTTP connections, although in case of WebSockets a TCP handshake effectively upgrades from HTTP protocol to WebSocket protocol, allowing WebSocket applications to more easily fit into existing infrastructures. However, the main similarity between both is their functionality: both push data from the client to server, a process also known as “server push”. With this in mind, let´s now have a look at some differences between the two before we can talk about best use cases for both technologies.</p>
<h2 id="differencesbetweenssesandwebsockets">Differences between SSEs and WebSockets</h2>
<p>Before we get into the differences between both technologies, we should state they're not competing technologies, nor is one better than the other. The popularity of WebSockets over SSEs can be explained by the fac that WebSockets have received more attention (and appreciation) than SSEs, and it’s a fact that more browsers support it natively than they support SSEs.</p>
<p>The differences between both technologies are not that big, and if you want to do “server-push” only, both are good choices. Let´s have a look at their differences. By far the biggest difference between both technologies is that WebSockets are full-duplex, bidirectional communication between client and server, whereas SSEs are mono-directional.</p>
<p>SSEs come with a set of features that WebSockets lack by design, such as automatic reconnection, event IDs and sending arbitrary events. On the other hand, WebSockets are able to detect a dropped client connection, whereas SSEs first need to send a message before detecting the same issue. In terms of browser support, both Internet Explorer and Edge do not yet support SSEs, although polyfills that simulate SSE functionality are available to solve this issue.</p>
<p>Although WebSockets use an initial HTTP connection, this connection is updated after a TCP handshake after which data is sent through the WebSocket protocol. This is a more complex protocol than the SSE protocol. Because they’re bidirectional, WebSocktets require more development effort than SSEs, that only need to send an HTTP message with a specific header, whereas a WebSocket needs to establish and maintain a TCP socket communication, as well as a listener socket on the server side.</p>
<p>It´s good to know that SSE suffers from a limitation to the maximum number of open connections, which can be specially painful when opening various tabs as the limit is per browser is six. Only WebSockets can transmit both binary data and UTF-8, whereas SSE is limited to UTF-8.</p>
<p>While WebSockets are more complex and demanding than SSEs, a full-duplex TCP connection makes it useful for a wider range of application scenarios. SSE is a simpler and faster solution, but it isn't extensible: if your web application requirements were to change, it would need to be refactored using WebSocket. Although WebSocket technology presents more upfront work, it´s a more versatile and extensible framework, so a better option for complex applications that will add new features over time.</p>
<h2 id="wsandssebestusecases">WS and SSE Best Use Cases</h2>
<p>In the end, whether you should be using WebSockets or SSEs depends on your use case. As stated before, SSEs cannot provide bidirectional client-server communication as opposed to WebSockets. Use cases that require such communication are real-time multi-player games and messaging and chat apps. When there´s no need for sending data from a client, SSEs might be a better option than WebSockets. Examples of such use cases are status updates, news feeds and other automated data push mechanisms.</p>
<h3 id="howscaledroneuseswebsockettechnology">How Scaledrone uses WebSocket technology</h3>
<p>Scaledrone offer a real-time messaging service and platform that can be used for sending live updates, create chatrooms and collaborative tools. Scaledrone makes use of Enhanced WebSockets that offer additional features. These are used when possible. When needed, it falls back to technologies such as XHR streaming, JSONP polling and XHR polling. Use cases for which they´re used are chat, notifications and real-time updates, for the creation of live maps, real-time dashboards and much more.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In this blog post we compared WebSockets and SSEs. We discussed the differences and similarities between the two technologies. While both can be used for “server push”, there are many subtle differences between the two. Because WebSockets have received more attention from developers and bloggers, SSEs are lesser-known but no less potent. We looked at best use cases for both and concluded that the use case should define your final choice if “server push” is what you´re after. Finally, we described how Scaledrone uses WebSocket technology to drive the real-time web.</p>
</div>]]></content:encoded></item><item><title><![CDATA[iOS Chat Tutorial: Building A Realtime Messaging App]]></title><description><![CDATA[In this step-by-step tutorial you'll build a real-time iOS chat app, utilizing Swift and Scaledrone for real-time communication.]]></description><link>https://www.scaledrone.com/blog/ios-chat-tutorial/</link><guid isPermaLink="false">5b93eff3e14c9423cd8d75a9</guid><category><![CDATA[Chat]]></category><category><![CDATA[Tutorials]]></category><category><![CDATA[iOS]]></category><dc:creator><![CDATA[Marin Bencevic]]></dc:creator><pubDate>Sun, 23 Sep 2018 17:26:08 GMT</pubDate><media:content url="https://www.scaledrone.com/blog/content/images/2018/09/ios-chat-tutorial@2x.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://www.scaledrone.com/blog/content/images/2018/09/ios-chat-tutorial@2x.png" alt="iOS Chat Tutorial: Building A Realtime Messaging App"><p>One thing that can differentiate your app from other apps on the market is building in chat functionality. Online marketplaces, social networks and collaboration tools can all benefit from in-app chat. Your users can communicate with each other without having to exchange contact information or leave the app.</p>
<p>In this tutorial, we're going to be building a realtime group chat for iOS using the <a href="https://github.com/ScaleDrone/scaledrone-swift">Scaledrone Swift API Client</a>. It will work very similarly to apps such as WhatsApp, Facebook Messager and LINE.</p>
<p>By following this tutorial you'll make a chat application that works cross platform with our <a href="https://www.scaledrone.com/blog/android-chat-tutorial/">Android chat tutorial</a>.</p>
<p><strong>You can find the full source code <a href="https://github.com/ScaleDrone/ios-chat-tutorial/">on GitHub</a>.</strong></p>
<blockquote>
<p>Note: This tutorial uses Xcode 10 and Swift 4.2.</p>
</blockquote>
<h2 id="settinguptheapp">Setting Up The App</h2>
<p>First things first, in <strong>Xcode</strong>, create a new <strong>Single View App</strong>, and call it whatever you like. To make our lives easer, we'll use two dependencies. The first one is <strong>Scaledrone</strong>, which simplifies real time messaging. The second one is <strong>MessageKit</strong>, a chat UI library which provides a customizable list of messages, with chat bubbles, avatars, username labels and other neat UI features.</p>
<p>Now that we have our project, we need to add Scaledrone and MessageKit as dependencies. We'll do this by using <strong>CocoaPods</strong>, a dependency manager for iOS. If you don't have CocoaPods installed, install it by following the instructions on the <a href="https://cocoapods.org">CocoaPods website</a>.</p>
<p>In Terminal, navigate to the root directory of your Xcode project using the <code>cd</code> command.</p>
<pre><code class="language-shell">cd Path/To/YourProject/
</code></pre>
<p>Once you're in the directory, run the following command to initialize CocoaPods.</p>
<pre><code class="language-shell">pod init
</code></pre>
<p>This will create a text file called <em>Podfile</em> in the directory. Open it with your favorite text editor, and insert the following lines right above the <code>end</code>.</p>
<pre><code class="language-ruby">pod 'MessageKit'
pod 'Scaledrone', '~&gt; 0.3.0'
</code></pre>
<p>Back in Terminal, still in the same directory, run the following command:</p>
<pre><code class="language-shell">pod install
</code></pre>
<p>This will install MessageKit, Scaledrone and all of their dependencies. It will also generate a <code>.xcworkspace</code> file. Close your project in Xcode, and open up the workspace by double clicking on the <code>.workspace</code> file. In order for your dependencies to work, you need to use the <strong>workspace</strong> file, and not <code>.xcodeproj</code>.</p>
<blockquote>
<p>Note: At the time of writing, MessageKit is not updated for Swift 4.2, so you might get compiler errors when building after installing the pod. To fix this, click on the <strong>Pods</strong> project in the <strong>Project navigator</strong>, and then select the <strong>MessageKit</strong> target. Go into <strong>Build Settings</strong> and set <strong>Swift Language Version</strong> to <strong>Swift 4</strong>. After this the errors should disappear.</p>
</blockquote>
<h2 id="creatingtheui">Creating the UI</h2>
<p>Thanks to MessageKit, creating a good looking chat UI is a pretty simple process. Xcode has already generated a <strong>ViewController.swift</strong> file for you. This will be the main view controller in your app.</p>
<p>The first thing you'll do is import the MessageKit library so you can work with it. Add the following line to the top of the file.</p>
<pre><code class="language-swift">import MessageKit
</code></pre>
<p>Now change the <code>ViewController</code> class declaration so that it inherits from <code>MessagesViewController</code>, and not <code>UIViewController</code>.</p>
<pre><code class="language-swift">class ViewController: MessagesViewController {
</code></pre>
<p><code>MessagesViewController</code> is the cornerstone of MessageKit. It contains a collection view that displays messages, as well as an input bar for sending a new message.</p>
<p>If you run the project now, you'll see that we already have a basic chat UI set up. The only problem is that we can't actually send any messages, so the messages collection view is blank.</p>
<img alt="iOS Chat Tutorial: Building A Realtime Messaging App" src="https://www.scaledrone.com/blog/content/images/2018/10/ios-chat-empty-screen.png" srcset="/blog/content/images/2018/10/ios-chat-empty-screen@2x.png 2x" class="mobile-screenshot">
<p>First of all, we need a way to store all the messages that we want to display. We'll need a model to hold our messages, as well as a model for the users. Create a new file called <strong>Message.swift</strong> and add the following code:</p>
<pre><code class="language-swift">import Foundation
import UIKit
import MessageKit

struct Member {
  let name: String
  let color: UIColor
}

struct Message {
  let member: Member
  let text: String
  let messageId: String
}
</code></pre>
<p>These are just simple models to hold our message data. Instead of avatars, each member will have a color assigned to them and displayed next to their message.</p>
<p>Because MessageKit works with messages that conform to the <code>MessageType</code> protocol, we'll extend <code>Message</code> to do so:</p>
<pre><code>extension Message: MessageType {
  var sender: Sender {
    return Sender(id: member.name, displayName: member.name)
  }
  
  var sentDate: Date {
    return Date()
  }
  
  var kind: MessageKind {
    return .text(text)
  }
}
</code></pre>
<p>Back in <strong>ViewController.swift</strong>, add an array of messages above <code>viewDidLoad</code>, as well as a property for the current user:</p>
<pre><code class="language-swift">var messages: [Message] = []
var member: Member!
</code></pre>
<p>Now that we have those, we need to connect them to the UI. To update the view, as well as handle user interaction, we need to implement four protocols:</p>
<ol>
<li><code>MessagesDataSource</code> which provides the number and content of messages.</li>
<li><code>MessagesLayoutDelegate</code> which provides height, padding and alignment for different views.</li>
<li><code>MessagesDisplayDelegate</code> which provides colors, styles and views that define the look of the messages.</li>
<li><code>MessageInputBarDelegate</code> which handles sending and typing new messages.</li>
</ol>
<p>We'll implement these in a series of four extensions of the <code>ViewController</code> class, starting with the data source. Add this code to the bottom of the file:</p>
<pre><code class="language-swift">extension ViewController: MessagesDataSource {
  func numberOfSections(
    in messagesCollectionView: MessagesCollectionView) -&gt; Int {
    return messages.count
  }
  
  func currentSender() -&gt; Sender {
    return Sender(id: member.name, displayName: member.name)
  }
  
  func messageForItem(
    at indexPath: IndexPath,
    in messagesCollectionView: MessagesCollectionView) -&gt; MessageType {
    
    return messages[indexPath.section]
  }
  
  func messageTopLabelHeight(
    for message: MessageType, 
    at indexPath: IndexPath, 
    in messagesCollectionView: MessagesCollectionView) -&gt; CGFloat {
    
    return 12
  }
  
  func messageTopLabelAttributedText(
    for message: MessageType, 
    at indexPath: IndexPath) -&gt; NSAttributedString? {
    
    return NSAttributedString(
      string: message.sender.displayName,
      attributes: [.font: UIFont.systemFont(ofSize: 12)])
  }
}
</code></pre>
<p>These functions are pretty straightforward. First we create a <code>Sender</code> instance from our member. Then, we return the number of the messages. Each section contains a new message, so we simply return the message for that section index. Lastly, we return an attributed text containing the username of the sender for a label above the message.</p>
<p>The next protocol we need to implement is the layout delegate. Since we're not customizing the layout, this will be really easy:</p>
<pre><code class="language-swift">extension ViewController: MessagesLayoutDelegate {
  func heightForLocation(message: MessageType, 
    at indexPath: IndexPath, 
    with maxWidth: CGFloat, 
    in messagesCollectionView: MessagesCollectionView) -&gt; CGFloat {
    
    return 0
  }
}
</code></pre>
<p>We're simply returning <code>0</code> for the height, and MessageKit will take care of calculating it for us.</p>
<p>To implement the <code>MessagesDisplayDelegate</code>, we'll make sure we set the member's color as the background color of the avatar view.</p>
<pre><code class="language-swift">extension ViewController: MessagesDisplayDelegate {
  func configureAvatarView(
    _ avatarView: AvatarView, 
    for message: MessageType, 
    at indexPath: IndexPath, 
    in messagesCollectionView: MessagesCollectionView) {
    
    let message = messages[indexPath.section]
    let color = message.member.color
    avatarView.backgroundColor = color
  }
}
</code></pre>
<p>The last protocol we need to implement is the input bar delegate. This allows us to actually send a new message. For now, we'll just append the message onto the array. Later on we'll actually send it to Scaledrone.</p>
<pre><code class="language-swift">extension ViewController: MessageInputBarDelegate {
  func messageInputBar(
    _ inputBar: MessageInputBar, 
    didPressSendButtonWith text: String) {
    
    let newMessage = Message(
      member: member, 
      text: text, 
      messageId: UUID().uuidString)
      
    messages.append(newMessage)
    inputBar.inputTextView.text = &quot;&quot;
    messagesCollectionView.reloadData()
    messagesCollectionView.scrollToBottom(animated: true)
  }
}
</code></pre>
<p>We create a new message with the text provided by the method. We use the built-in <code>UUID</code> class to get a unique string which we'll use for the message's ID. We'll then append that message to the array, and reload the collection view so it updates.</p>
<p>Now we implemented all the protocols we need, but there's still one small step we need to do. We need to tell MessageKit to use <code>ViewController</code> as its delegate for all of these things. Add these lines to the bottom of <code>viewDidLoad</code>:</p>
<pre><code>member = Member(name: &quot;bluemoon&quot;, color: .blue)
messagesCollectionView.messagesDataSource = self
messagesCollectionView.messagesLayoutDelegate = self
messageInputBar.delegate = self
messagesCollectionView.messagesDisplayDelegate = self
</code></pre>
<p>We'll also hard-code a member to use for testing. Later we'll generate a username and color randomly.</p>
<p>If you run the app now, you'll see that you can send new messages!</p>
<img alt="iOS Chat Tutorial: Building A Realtime Messaging App" src="https://www.scaledrone.com/blog/content/images/2018/10/ios-chat-bubbles-2.png" srcset="/blog/content/images/2018/10/ios-chat-bubbles-2@2x.png 2x" class="mobile-screenshot">
<p>Okay, you you're not <em>actually</em> sending the messages to the internet, but you do have a working chat UI. In just a few more minutes, you'll have a fully working chat app.</p>
<h2 id="arandomuser">A Random User</h2>
<p>In your production app, you'll probably have a login screen which will authenticate your users. For the purposes of this tutorial, we'll assign a random name and a random color to a user when they launch the app.</p>
<p>We'll implement this as extensions on <code>String</code> and <code>UIColor</code>. Create a new Swift file called <strong>Extensions.swift</strong>. In the file, add the following extension to get a random name.</p>
<pre><code class="language-swift">import Foundation
import UIKit

extension String {
  static var randomName: String {
    let adjectives = [&quot;autumn&quot;, &quot;hidden&quot;, &quot;bitter&quot;, &quot;misty&quot;, &quot;silent&quot;, &quot;empty&quot;, &quot;dry&quot;, &quot;dark&quot;, &quot;summer&quot;, &quot;icy&quot;, &quot;delicate&quot;, &quot;quiet&quot;, &quot;white&quot;, &quot;cool&quot;, &quot;spring&quot;, &quot;winter&quot;, &quot;patient&quot;, &quot;twilight&quot;, &quot;dawn&quot;, &quot;crimson&quot;, &quot;wispy&quot;, &quot;weathered&quot;, &quot;blue&quot;, &quot;billowing&quot;, &quot;broken&quot;, &quot;cold&quot;, &quot;damp&quot;, &quot;falling&quot;, &quot;frosty&quot;, &quot;green&quot;, &quot;long&quot;, &quot;late&quot;, &quot;lingering&quot;, &quot;bold&quot;, &quot;little&quot;, &quot;morning&quot;, &quot;muddy&quot;, &quot;old&quot;, &quot;red&quot;, &quot;rough&quot;, &quot;still&quot;, &quot;small&quot;, &quot;sparkling&quot;, &quot;throbbing&quot;, &quot;shy&quot;, &quot;wandering&quot;, &quot;withered&quot;, &quot;wild&quot;, &quot;black&quot;, &quot;young&quot;, &quot;holy&quot;, &quot;solitary&quot;, &quot;fragrant&quot;, &quot;aged&quot;, &quot;snowy&quot;, &quot;proud&quot;, &quot;floral&quot;, &quot;restless&quot;, &quot;divine&quot;, &quot;polished&quot;, &quot;ancient&quot;, &quot;purple&quot;, &quot;lively&quot;, &quot;nameless&quot;]
    let nouns = [&quot;waterfall&quot;, &quot;river&quot;, &quot;breeze&quot;, &quot;moon&quot;, &quot;rain&quot;, &quot;wind&quot;, &quot;sea&quot;, &quot;morning&quot;, &quot;snow&quot;, &quot;lake&quot;, &quot;sunset&quot;, &quot;pine&quot;, &quot;shadow&quot;, &quot;leaf&quot;, &quot;dawn&quot;, &quot;glitter&quot;, &quot;forest&quot;, &quot;hill&quot;, &quot;cloud&quot;, &quot;meadow&quot;, &quot;sun&quot;, &quot;glade&quot;, &quot;bird&quot;, &quot;brook&quot;, &quot;butterfly&quot;, &quot;bush&quot;, &quot;dew&quot;, &quot;dust&quot;, &quot;field&quot;, &quot;fire&quot;, &quot;flower&quot;, &quot;firefly&quot;, &quot;feather&quot;, &quot;grass&quot;, &quot;haze&quot;, &quot;mountain&quot;, &quot;night&quot;, &quot;pond&quot;, &quot;darkness&quot;, &quot;snowflake&quot;, &quot;silence&quot;, &quot;sound&quot;, &quot;sky&quot;, &quot;shape&quot;, &quot;surf&quot;, &quot;thunder&quot;, &quot;violet&quot;, &quot;water&quot;, &quot;wildflower&quot;, &quot;wave&quot;, &quot;water&quot;, &quot;resonance&quot;, &quot;sun&quot;, &quot;wood&quot;, &quot;dream&quot;, &quot;cherry&quot;, &quot;tree&quot;, &quot;fog&quot;, &quot;frost&quot;, &quot;voice&quot;, &quot;paper&quot;, &quot;frog&quot;, &quot;smoke&quot;, &quot;star&quot;]
    
    return adjectives.randomElement()! + nouns.randomElement()!
  }
}
</code></pre>
<p>We are creating random usernames by combining randomly selected adjectives and nouns. This will give usernames like &quot;hiddensun&quot; or &quot;redstar&quot;.</p>
<p>Now we'll do a similar thing, but for getting a random color.</p>
<pre><code class="language-swift">extension UIColor {
  static var random: UIColor {
    return UIColor(
      red: CGFloat.random(in: 0...1),
      green: CGFloat.random(in: 0...1),
      blue: CGFloat.random(in: 0...1),
      alpha: 1)
  }
}
</code></pre>
<p>To get a random color, we simply select a random value for red, green and blue. Both of these extensions are using Swift 4.2's new random number generation.</p>
<p>Now that we have those in place, it's time to actually use them. Open up <strong>ViewController.swift</strong> and in <code>viewDidLoad</code>, change the line where you create the member to the following:</p>
<pre><code class="language-swift">member = Member(name: .randomName, color: .random)
</code></pre>
<p>Run your app a few times and send some messages. You should see a different color each time you run the app.</p>
<img alt="iOS Chat Tutorial: Building A Realtime Messaging App" src="https://www.scaledrone.com/blog/content/images/2018/10/ios-chat-bubbles-1.png" srcset="/blog/content/images/2018/10/ios-chat-bubbles-1@2x.png 2x" class="mobile-screenshot">
<h2 id="preparingyourmembers">Preparing Your Members</h2>
<p>We'll be sending and receiving messages and members as JSON dictionaries, which means we need a way to create them from a dictionary, as well as get a dictionary representation from the model structs. We'll add this to <code>Member</code> by extending it with two new things: An initializer that will create it from JSON, as well as a computed property to get a dictionary from its properties.</p>
<p>Before we do that, we have one problem. <code>Member</code> has a <code>UIColor</code> property, which can't be easily represent as a JSON value. So, we'll use <strong>hexadecimal color codes</strong> to represent color. A hexadecimal color code looks like this: <code>#FF5733</code>. It has six digits. The first two represent the value of <strong>red</strong> between 0 and 255 in hex. The second two represent <strong>green</strong>, while the final two represent <strong>blue</strong>.</p>
<p>Knowing that, we can convert <code>UIColor</code> back and forth to hex codes with a simple extension. Add this extension to <strong>Extensions.swift</strong>:</p>
<pre><code class="language-swift">extension UIColor {
  convenience init(hex: String) {
    var hex = hex
    if hex.hasPrefix(&quot;#&quot;) {
        hex.remove(at: hex.startIndex)
    }
    
    var rgb: UInt64 = 0
    Scanner(string: hex).scanHexInt64(&amp;rgb)

    let r = (rgb &amp; 0xff0000) &gt;&gt; 16
    let g = (rgb &amp; 0xff00) &gt;&gt; 8
    let b = rgb &amp; 0xff
    
    self.init(
      red: CGFloat(r) / 0xff,
      green: CGFloat(g) / 0xff,
      blue: CGFloat(b) / 0xff, 
      alpha: 1
    )
  }
  
  var hexString: String {
    var r: CGFloat = 0
    var g: CGFloat = 0
    var b: CGFloat = 0
    var a: CGFloat = 0
    
    self.getRed(&amp;r, green: &amp;g, blue: &amp;b, alpha: &amp;a)
    
    return String(
      format: &quot;#%02X%02X%02X&quot;,
      Int(r * 0xff),
      Int(g * 0xff),
      Int(b * 0xff)
    )
  }
}
</code></pre>
<p>For this tutorial, the details of how this works are not really important. The gist is that we take the hex code as a single number, and filter out only the two digits that we need for the color (red, green or blue). We then convert that number into a number between 0 and 1, since that's what <code>UIColor</code> uses.</p>
<p>Now that we can convert our color, let's add the ability to convert between a JSON dictionary and <code>Member</code>. In <strong>Message.swift</strong>, add the following extension to the bottom of the file:</p>
<pre><code class="language-swift">extension Member {
  var toJSON: Any {
    return [
      &quot;name&quot;: name,
      &quot;color&quot;: color.hexString
    ]
  }
  
  init?(fromJSON json: Any) {
    guard
      let data = json as? [String: Any],
      let name = data[&quot;name&quot;] as? String,
      let hexColor = data[&quot;color&quot;] as? String
    else {
      print(&quot;Couldn't parse Member&quot;)
      return nil
    }
    
    self.name = name
    self.color = UIColor(hex: hexColor)
  }
}
</code></pre>
<p>This is typical dictionary parsing code. The json contains two keys, <code>name</code> and <code>color</code>, which contain the username and the hex code for the color. We convert those into Swift-friendly objects and voilà, we have our member.</p>
<h2 id="connectingtoscaledrone">Connecting To Scaledrone</h2>
<p>We prepped all of our ingredients, now it's time to get cooking! Thanks to Scaledrone's iOS SDK, sending and receiving chat messages is really easy.</p>
<p>Before we start making network requests, we need to make sure iOS doesn't stop us. We'll do this by disabling <strong>App Transport Security</strong>. Open <strong>Info.plist</strong> in your main target. Add a new key called &quot;<code>App Transport Security Settings</code>&quot;. Inside of that key, add one more key called &quot;<code>Allow Arbitrary Loads</code>&quot; and set its value to &quot;<code>YES</code>&quot;.</p>
<p><img src="https://www.scaledrone.com/blog/content/images/2018/10/xcode-settings.png" alt="iOS Chat Tutorial: Building A Realtime Messaging App"></p>
<p>If you don't have a Scaledrone account yet, open up <a href="https://www.scaledrone.com">Scaledrone.com</a> and create a new free account. To successfully connect to Scaledrone you need to get your own channel ID from the Scaledrone's dashboard. To do that go to the dashboard and click the big green <strong>+Create Channel</strong> button to get started. You can choose <strong>Never require authentication</strong> for now. Note down the channel ID from the just created channel, you'll need it in a bit.</p>
<p>Create a new Swift file called <strong>ChatService.swift</strong>. In this file, add a new class called <code>ChatService</code>:</p>
<pre><code class="language-swift">import Foundation
import Scaledrone

class ChatService {
  private let scaledrone: Scaledrone
  private let messageCallback: (Message)-&gt; Void
  
  private var room: ScaledroneRoom?
  
  init(member: Member, onRecievedMessage: @escaping (Message)-&gt; Void) {
    self.messageCallback = onRecievedMessage
    self.scaledrone = Scaledrone(
      channelID: &quot;YOUR-CHANNEL-ID&quot;, 
      data: member.toJSON)
    scaledrone.delegate = self
  }
  
  func connect() {
    scaledrone.connect()
  }
}
</code></pre>
<p>In the initializer, we'll receive the current member as well as a closure that we'll call each time a new message arrives. We'll use that callback to update our view controller with new messages.</p>
<p>We'll create a new instance of <code>Scaledrone</code>, which manages connecting to the service. Remember that channel ID from above? Make sure to pass it here so Scaledrone knows which channel to connect to.</p>
<p>We'll also pass it the data for the currently logged in member. If you have additional data about the user or the client, this is a good way to supply that data, instead of having to send it with each message.</p>
<p>Connecting to Scaledrone is simply a matter of calling <code>connect</code> on the <code>Scaledrone</code> instance. To know what's going on after connecting to Scaledrone, we'll implement the <code>ScaledroneDelegate</code> protocol.</p>
<p>Once Scaledrone connects, we need to enter a <strong>room</strong>. A room is a group of users that we can send messages to. You listen to those messages by subscribing to a room of a specific name.</p>
<pre><code class="language-swift">extension ChatService: ScaledroneDelegate {
  func scaledroneDidConnect(scaledrone: Scaledrone, error: NSError?) {
    print(&quot;Connected to Scaledrone&quot;)
    room = scaledrone.subscribe(roomName: &quot;observable-room&quot;)
    room?.delegate = self
  }
  
  func scaledroneDidReceiveError(scaledrone: Scaledrone, error: NSError?) {
    print(&quot;Scaledrone error&quot;, error ?? &quot;&quot;)
  }
  
  func scaledroneDidDisconnect(scaledrone: Scaledrone, error: NSError?) {
    print(&quot;Scaledrone disconnected&quot;, error ?? &quot;&quot;)
  }
}
</code></pre>
<p>Once Scaledrone connects, we'll subscribe to a room. If there's an error, we'll print it out to the console.</p>
<blockquote>
<p>Note: You might have noticed that we named our name Scaledrone room <code>observable-room</code>. You can name the room anything you want, a single user can actually connect to an infinite amount of rooms for all sorts of application scenarios. However, in order for messages to contain the info of the sender, you need to prefix the room name with &quot;<code>observable-</code>&quot;. <a href="https://github.com/ScaleDrone/scaledrone-swift#observable-rooms">Read more...</a></p>
</blockquote>
<p>To listen to new messages, there's one more protocol we need to implement: <code>ScaledroneRoomDelegate</code>.</p>
<pre><code class="language-swift">extension ChatService: ScaledroneRoomDelegate {
  func scaledroneRoomDidConnect(room: ScaledroneRoom, error: NSError?) {
    print(&quot;Connected to room!&quot;)
  }
  
  func scaledroneRoomDidReceiveMessage(
    room: ScaledroneRoom, 
    message: Any, 
    member: ScaledroneMember?) {
    
    guard
      let text = message as? String,
      let memberData = member?.clientData,
      let member = Member(fromJSON: memberData)
    else {
      print(&quot;Could not parse data.&quot;)
      return
    }
    
    let message = Message(
      member: member, 
      text: text, 
      messageId: UUID().uuidString)
    messageCallback(message)
  }
}
</code></pre>
<p>When we receive a new message, we'll try to convert it into a <code>String</code>. We then create a <code>Member</code> from the data we received in the function, using the initializer we created earlier. With those two pieces we construct the message, giving it a unique ID. Finally we call the callback so our view controller knows a new message arrived.</p>
<p>Now that we can <em>receive</em> messages, we also need to <em>send</em> them. With Scaledrone, this is really easy: it's just one line of code. Add this function to the bottom of the <code>ChatService</code> class:</p>
<pre><code class="language-swift">func sendMessage(_ message: String) {
  room?.publish(message: message)
}
</code></pre>
<p>That's all there is to connecting with Scaledrone! Now let's hook this all up to our view controller.</p>
<h2 id="finaltouches">Final Touches</h2>
<p>We'll need to modify <strong>ViewController.swift</strong> to use the new <code>ChatService</code> we created. First of all, add a property to the top of the class to store the <code>ChatService</code>.</p>
<pre><code class="language-swift">var chatService: ChatService!
</code></pre>
<p>Next, add the following code to the bottom of <code>viewDidLoad</code>:</p>
<pre><code class="language-swift">chatService = ChatService(member: member, onRecievedMessage: {
  [weak self] message in
  self?.messages.append(message)
  self?.messagesCollectionView.reloadData()
  self?.messagesCollectionView.scrollToBottom(animated: true)
})

chatService.connect()
</code></pre>
<p>When we get a new message, we'll refresh the UI. We'll also connect to Scaledrone as soon as our screen gets loaded.</p>
<p>Finally, in the <code>MessageInputBarDelegate</code> extension, change the contents of the <code>messageInputBar(_:didPressSendButtonWith:)</code> method to just the following two lines:</p>
<pre><code class="language-swift">chatService.sendMessage(text)
inputBar.inputTextView.text = &quot;&quot;
</code></pre>
<p>Run the app and send a few messages. If it looks the same as before: that's good! It looks the same, but this time it's actually sending and receiving the message to and from Scaledrone.</p>
<p>Open up another simulator or run the app on a device, and try to send messages. Congrats! You are now talking to yourself. :)</p>
<img alt="iOS Chat Tutorial: Building A Realtime Messaging App" src="https://www.scaledrone.com/blog/content/images/2018/10/ios-chat-phones-1.png" srcset="/blog/content/images/2018/10/ios-chat-phones@2x-1.png 2x" class="mobile-screenshot">
<h2 id="andweredone">And We're Done!</h2>
<iframe src="https://player.vimeo.com/video/291376589" width="320" height="639" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen></iframe>
<p>There you go! With Scaledrone and MessageKit, adding chat to your app is incredibly easy, so you have no excuses left! <strong>You can find the full source code <a href="https://github.com/ScaleDrone/ios-chat-tutorial">on GitHub</a>.</strong></p>
<p>Here are some ideas on features you can add, all using Scaledrone's APIs:</p>
<ul>
<li>Who's online</li>
<li>Currently typing status</li>
<li>Chat history</li>
<li>Sending images and attachments</li>
<li>Authentication</li>
</ul>
<p>You can find the full source code or run the working prototype on GitHub. If you have any questions or feedback feel free to <a href="https://www.scaledrone.com/contact">contact us</a>.</p>
<p>This tutorial only scratched what Scaledrone can do for you and is the ideal basis for any of your future realtime needs.</p>
<h2 id="wheretonow">Where to now?</h2>
<p>In <a href="https://www.scaledrone.com/blog/ios-chat-tutorial-part-2/">Part 2</a> of the tutorial we learn how to add <strong>typing indicators</strong> and a <strong>list of online members</strong> to your app. <a href="https://www.scaledrone.com/blog/ios-chat-tutorial-part-2/">Check it out!</a></p>
</div>]]></content:encoded></item><item><title><![CDATA[SOLID Principles for Becoming a Better iOS/Swift Developer]]></title><description><![CDATA[Become a better Swift developer by learning how to apply SOLID principles in your own code! SOLID stands for five guiding principles for OOP programming.
]]></description><link>https://www.scaledrone.com/blog/solid-principles-for-becoming-a-better-ios-swift-developer/</link><guid isPermaLink="false">5b7c105de14c9423cd8d759d</guid><dc:creator><![CDATA[Marin Bencevic]]></dc:creator><pubDate>Thu, 06 Sep 2018 15:45:16 GMT</pubDate><media:content url="https://www.scaledrone.com/blog/content/images/2018/09/solid-1.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://www.scaledrone.com/blog/content/images/2018/09/solid-1.png" alt="SOLID Principles for Becoming a Better iOS/Swift Developer"><p>While SOLID might be old (at least in <em>internet years</em>), they're one of those evergreen pieces of advice that go beyond language or platform specifics. SOLID is an acronym that stands for five guiding principles for OOP programming.</p>
<ul>
<li><strong>S</strong>ingle Responsibility Principle</li>
<li><strong>O</strong>pen/closed Principle</li>
<li><strong>L</strong>iskov Substitution Principle</li>
<li><strong>I</strong>nterface Segregation Principle</li>
<li><strong>D</strong>ependency Inversion Principle</li>
</ul>
<p>It's not just good advice for OOP, but programming in general.</p>
<h2 id="singleresponsibilityprinciplesrp">Single Responsibility Principle (SRP)</h2>
<p>The single responsibility sounds deceivingly simple: One class should only have one responsibility. A <code>UserFetcher</code> class that fetches new users. A <code>Payment</code> class that processes purchases in your app. A <code>LocationTracker</code> app that tracks your users location. These are all examples of classes with a single responsibility.</p>
<p>Because they do one thing, these classes are very simple. Just by reading their names, you know exactly what they do. They're also easy to write: knowing exactly what the class is for means you know which methods you need and which of them need to be exposed as public methods.</p>
<p>They're also much easier to maintain. The code of large classes with lots of things to do often gets intertwined like earbuds that have been in your pocket for too long. In order to change or fix something, you first have to meticulously untangle each of the responsibilities just to start thinking about where the problem is.</p>
<p>We often violate SRP because it's <em>easy</em>. <strong>The biggest problem is the phenomenon of <em>one little feature</em>.</strong> It's a trap we constantly keep falling into: I'll just add this <em>one</em> little feature to the class. If you add that <em>one</em> little feature five times, suddenly it's five little features. With each feature you are exponentially adding more and more complexity. Before you know, you created a huge, tangled mess.</p>
<p>iOS developers are one of the worst classes of programmers when it comes to following SRP. Case and point: our good old friend, <strong>the view controller</strong>.</p>
<p>What does a <code>UIViewController</code> <em>do</em>? Well, it assembles the views on the screen. It also prepares table view cells. Oh, it also navigates to another screen. Sometimes it also calls network requests. It also, also, keeps track of the state of our screen. A typical <code>UIViewController</code> is about 12 responsibilities removed from SRP. (This problem is often referred to as <em>Massive View Controller</em>.)</p>
<p>This is why view controllers often become those classes that everyone is afraid to touch, and nobody understands what's going on. There are just too many of those <em>one little features</em>.</p>
<h3 id="whatcanido">What can I do?</h3>
<p>First off, stop adding <em>little features</em>. Shift your brain to thinking about modules, components and APIs. Get out of the mindset of patching and hacking, and into the mindset of creating libraries. Write small classes that do one thing. If you have a large problem, break it down. Write classes for each subset of the problem, and then write another one that will use those classes.</p>
<p>Identify the responsibilities of your view controller, and split them up. The easiest candidates are table view delegates and data sources. Make a separate class that does that thing. <strong>The data source can be any class, not just the view controller.</strong></p>
<p>And always pay attention to the size of your classes. The number of lines can be a very good warning sign that some things should probably be split up.</p>
<h2 id="openclosedprinciple">Open/closed Principle</h2>
<p>While SRP is deceivingly simple, the open/closed principle sounds more complex than it really is. It goes as follows: software entities should be open for extension, but closed for modification. Sounds pretty fancy, right?</p>
<p>What that sentence is trying to say is that it should be <strong>easy to add new features to your class</strong>. Earlier, I mentioned an example of a <code>UserFetcher</code> class as an example of SRP. Let's say that class has one method, <code>fetchUsers</code>, which fetches a JSON of all your users from a database, parses it, and returns it.</p>
<pre><code class="language-swift">class UserFetcher {
  func fetchUsers(onComplete: @escaping ([User])-&gt; Void) {
    let session = URLSession.shared
    let url = URL(string: &quot;&quot;)!
    session.dataTask(with: url) { (data, _, error) in
      guard let data = data else {
        print(error!)
        onComplete([])
        return
      }
      
      let decoder = JSONDecoder()
      let decoded = try? decoder.decode([User].self, from: data)
      onComplete(decoded ?? [])
    }
  }
}
</code></pre>
<p>But hold on! A super-intelligent race of beings from Mars has invaded earth and taken you hostage! They want you to make the app work with <code>Martian</code> objects, instead of <code>User</code> objects. And they want it fast!</p>
<p>With our <code>UserFetcher</code> class, we need to change the whole implementation, even to the point of renaming the class, for it to work with Martians. If we took a different approach, this change would be <em>a lot</em> easier. What if instead of a <code>UserFetcher</code>, we wrote a generic <code>Fetcher</code>, for any <code>Decodable</code> list of things?</p>
<pre><code class="language-swift">class Fetcher&lt;T: Decodable&gt; {
  
  func fetch(onComplete: @escaping ([T])-&gt; Void) {
    let session = URLSession.shared
    let url = URL(string: &quot;&quot;)!
    session.dataTask(with: url) { (data, _, error) in
      guard let data = data else {
        print(error!)
        onComplete([])
        return
      }
      
      let decoder = JSONDecoder()
      let decoded = try? decoder.decode([T].self, from: data)
      onComplete(decoded ?? [])
    }
  }
  
}

typealias UserFetcher = Fetcher&lt;User&gt;
</code></pre>
<p>The implementation is almost the same, except we changed where we mention <code>User</code> to a generic <code>T</code>, which conforms to decodable. With this approach, we only need to change that single line at the bottom to support Martians.</p>
<pre><code class="language-swift">typealias MartianFetcher = ListFetcher&lt;Martian&gt;
</code></pre>
<p>As you can see, following the open/closed principle might just save your life one day!</p>
<p>Okay, the example with the Mars attack <em>might</em> be a <em>little</em> over the top. But if there's one thing that's constant about requirements, it's that they change. You need to help your future self and allow for easy ways to respond to design changes in the future.</p>
<h2 id="liskovsubstitutionprinciple">Liskov Substitution Principle</h2>
<p>Here's one principle that sounds very mathy and academic. If you think that name is bad, it has another one: substitutability. As in, the ability to be substituted with something else. And that's exactly what this principle means.</p>
<p>LSP says that any function working with a class should work also with any of those class' subclasses. This principle is a note of how you override methods. The user of that method shouldn't be able to see a difference between your version and the base class' method.</p>
<p>Let's go back to our <code>MartianFetcher</code>. The Martians are happy with how the code works on Earth, but they don't like how it works when they go back to Mars: there's no internet there, so they don't see any data. They want offline mode.</p>
<p>So, you go ahead and make a subclass of the <code>Fetcher</code>, which fetches data from a file on the device which contains cached Martians. This is a good idea since creating a subclass means you don't have to change code that works with <code>Fetcher</code>, since the API stays the same.</p>
<p>You're also really scared, so you take your keyboard and you quickly hack that feature into the app.</p>
<pre><code class="language-swift">class FileFetcher&lt;T: Decodable&gt;: ListFetcher&lt;T&gt; {
  override func fetch(onComplete: @escaping ([T])-&gt; Void) {
    let json = try? String(contentsOfFile: &quot;martians.json&quot;)
    guard let data = json?.data(using: .utf8) else {
      return
    }
    
    let decoder = JSONDecoder()
    let decoded = try? decoder.decode([T].self, from: data)
    onComplete(decoded ?? [])
  }
}
</code></pre>
<p>In your rush, you made a mistake. Here's how the base class' method works: if there is an error, the method calls the completion handler with an empty array. Your version works a bit differently. If there is an error, nothing happens. This means your UI won't update if there is an error. The Martians are angry now.</p>
<p>There are two ways you can fix it, and one of them is wrong. You can go into your view controller and check whether your <code>Fetcher</code> is actually a <code>FileFetcher</code> and update the UI.</p>
<pre><code class="language-swift">fetcher.fetch { martians in
  self.martians = martians
  self.tableView.reloadData()
}
if fetcher is FileFetcher {
  tableView.reloadData()
}
</code></pre>
<p>This is <strong>wrong</strong>. It's wrong because the whole point of a subclass is that you don't have to change the <em>user of the class</em>, but only the subclass. If for every subclass we had to have a different way of using it, then inheritance would be completely pointless. (Same goes for protocols and composition!)</p>
<p>The correct way to fix this would be to align the overridden method to work like the base class' method, or create a whole new class.</p>
<p>Whenever you're checking if an instance is of a specific type, it's probably code smell, and a violation of LSP. And most likely there's an easier way to do things.</p>
<h2 id="interfacesegregationprinciple">Interface Segregation Principle</h2>
<p>The Martians are generally happy with your app but they want more features. They want to see the details of a Martian when they click on it. Being the protocol-oriented programmer you are, you create a protocol to deal with this problem.</p>
<pre><code class="language-swift">protocol MartianFetcher {
	func getMartians(onComplete: ([Martian])-&gt; Void)
	func getMartian(id: String, ([Martian])-&gt; Void)
}
</code></pre>
<p>You create a class that implements this protocol, and hook it up to your app. But here's the problem: The list screen doesn't <em>need</em> <code>getMartian</code>, and the details screen doesn't need <code>getMartians</code>.</p>
<p>Having methods available that you don't need adds clutter and noise, making it harder to work with the API. It also leads to all the problems discussed in the Single Responsibility Principle section.</p>
<p>You can make a very easy fix that solves this problem. Just make two different protocols.</p>
<pre><code class="language-swift">protocol MartiansFetcher {
	func getMartians(onComplete: ([Martian])-&gt; Void)
}

protocol MartiansFetcher {
	func getMartian(id: String, onComplete: ([Martian])-&gt; Void)
}
</code></pre>
<p>You don't actually have to change your implementation, the same class could implement both of these protocols. But in your list view controller you will use an instance of <code>MartiansFetcher</code>, without extra clutter. This lets you add functionality to the class that fetches Martians without complicating things for the users of the class.</p>
<p>This is the reason why Swift has <code>Decodable</code>, <code>Encodable</code> and <code>Codable</code>. Not everyone can conform to all, and not everyone needs all functionality. Apply SRP to your protocols as well as classes.</p>
<h2 id="dependencyinversionprinciple">Dependency Inversion Principle</h2>
<p>Here's another principle with a scary name. This principle says that high level things in your app, like your view controller, should not depend directly on low level things, like a networking component. Instead, it should depend on an <em>abstraction</em> of that component. In practice, this means that the view controller should not use a <code>Fetcher</code> class, but a <code>Fetchable</code> protocol.</p>
<p>The reason is to reduce coupling. String coupling occurs when your class depends heavily on the implementation of another class. It might be calling a lot of methods, making assumptions about the inner working of that class, or use variable names that tie it to that specific class.</p>
<p>Strong coupling is bad because it makes it harder to change the code base. If you're using a <code>CoreDataService</code>, and suddenly want to switch to a <code>RealmService</code>, you best hope your view controller doesn't rely heavily on the former.</p>
<p>The way to go around this issue is to use a <code>DatabaseService</code> <strong>protocol</strong>, that the <code>CoreDataService</code> will implement.</p>
<pre><code class="language-swift">protocol DatabaseService {
  func getUsers()-&gt; [User]
}

class CoreDataService: DatabaseService {
  // ...
}
</code></pre>
<p>In your view controller, you pretend that the class doesn't exist, and just use an instance of that protocol.</p>
<pre><code class="language-swift">let databaseService: DatabaseService = CoreDataService()
</code></pre>
<p>You do this because protocols are less specific than classes. A class has a specific name and specific methods you can use. On the other hand, protocols are, by definition, <em>abstract</em>. More than one class can implement a protocol, making them perfect for reducing coupling.</p>
<p>To change to Realm, all you need to do is make a new class that conforms to the same protocol. Because you didn't rely on a specific implementation, you don't need to change code in your view controller. It's a huge time saver!</p>
<p>If you think about coupling from the beginning it will save your butt in the long run.</p>
<h2 id="morewhatyoudcallguidelines">More What You'd Call Guidelines</h2>
<p>We went through all the SOLID letters!</p>
<p>One thing to remember is that these principles, while very useful, are not <em>rules</em>. They're tools. As their creator, Robert C. Martin puts it, &quot;They are statements on the order of 'An apple a day keeps the doctor away.'&quot;. So keep them in mind, but allow for compromises.</p>
<p>Now you know a few principles to make you a better coder, as well as what to do in the event of an attack from Mars. Happy coding!</p>
</div>]]></content:encoded></item><item><title><![CDATA[Getting to know Server-Sent Events (SSE)]]></title><description><![CDATA[<div class="kg-card-markdown"><p><strong>Internet technology uses a request/response paradigm that enables a client to request for information and a server to respond. WebSockets, polling and Server-Sent Events are three different technologies that define how browser and client can communicate with each other. In this blog post, we'll focus primarily on the last</strong></p></div>]]></description><link>https://www.scaledrone.com/blog/getting-to-know-server-sent-events-sse/</link><guid isPermaLink="false">5b5950d6e14c9423cd8d7558</guid><category><![CDATA[SSE]]></category><dc:creator><![CDATA[Eric van Rees]]></dc:creator><pubDate>Wed, 29 Aug 2018 05:46:20 GMT</pubDate><media:content url="https://www.scaledrone.com/blog/content/images/2018/08/ServerSent-Events-EvR-1.jpg" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://www.scaledrone.com/blog/content/images/2018/08/ServerSent-Events-EvR-1.jpg" alt="Getting to know Server-Sent Events (SSE)"><p><strong>Internet technology uses a request/response paradigm that enables a client to request for information and a server to respond. WebSockets, polling and Server-Sent Events are three different technologies that define how browser and client can communicate with each other. In this blog post, we'll focus primarily on the last category, also known as SSE or EventSource. We'll explain what they are, how they work and how they are used in practice.</strong></p>
<h2 id="whatareserversentevents">What are Server-Sent Events?</h2>
<p>Before we dive into the specifics of SSE, let's start by examining what SSE is and what it's used for. The name Server-Sent Events is mostly self-explanatory: it’s a technology standard that allows server-to-client streaming of text-based event data over a HTTP connection. The idea is to have the browser receive data automatically from the server without explicitly having to poll for it. SSE makes working with real-time data simple and efficient, using just one, long-lived HTTP connection.</p>
<p>A specific feature of SSEs is that they’re mono-directional, which means that data communication only happens one-way, in this case from the server to the client. This feature comes in handy for use cases where the client doesn't need to send any data and only requires updates through server actions. In this sense, SSE differs from WebSockets that are bi-directional and enable near real-time updates in both directions.</p>
<p>SSE forms part of the HTML5 specification: the official API is named Server-Sent Events EventSource API, hence the popular term ‘EventSource’. It is natively supported in almost all browsers, however Internet Explorer and Edge being the main exceptions. SSEs are sent over traditional HTTP, so there’s no need for additional protocols or server implementations. The SSE standard provides multiple out-of-the-box features that WebSockets lack, such as reconnection handling.<br>
There are many use cases for SSE. As stated above, SSE is generally used for displaying data updates, such as price information or the availability of products and services on a website. It’s also used for chat applications, news updates and automated push mechanisms.</p>
<h2 id="componentsofsses">Components of SSEs</h2>
<p>After this quick introduction, let's have a better look at the two components that make up SSEs. Next, we’ll describe how they work together using some coding examples. In general, we can distinguish two components that, when combined, enable SSE technology:</p>
<ol>
<li>The EventSource API</li>
<li>The Event Stream Protocol</li>
</ol>
<p>The EventSource browser interface enables the client to receive push notifications from the server as DOM events, while individual updates are delivered through an “event stream” data format.</p>
<p>These components both enable an efficient and cross-browser implementation of XHR streaming, but letting the browser handle all connection management and message parsing.</p>
<p>In the browser, the EventSource API that performs the following tasks: manage the server connection, parse the received data incrementally, identify message boundaries and let the browser fire a DOM event to notify the application. A data stream can be terminated when finished, or resumed using the API’s auto-connect functionality in case the connection is dropped unexpectedly.</p>
<p>Event-stream data (or Server-Sent Events messages) consists of a simple stream of text data encoded in UTF-8. Its specification defines different data fields that are separated by a pair of newline characters: even, data, ID and retry. The data field holds the actual message content that is pushed to the client. Because the data format is well-defined, the EventSource API can do all the work in the browser.</p>
<h2 id="gettingstartedwithsse">Getting started with SSE</h2>
<p>Let’s start with a quick example of an application that uses SSEs. We’ll look at how SSEs are implemented on the client side. The client can be a mobile app and developed in any language that can handle HTTP connections, while the server side can be coded in any language. But first, we'll look at some examples of Server-Sent Events Messages.</p>
<p><em>Server-Sent Events Messages</em><br>
Here is a template for single event messages:</p>
<pre><code>id: &lt;messageId&gt;\n
event: &lt;eventType&gt;\n
data: &lt;event data - plain text, JSON, ... &gt;\n
\n
\n
</code></pre>
<p>Using this template, we can create the following message:</p>
<pre><code>id: 99\n
event: price\n
data: 99.99\n
\n
\n
</code></pre>
<p><strong>Client-side</strong><br>
Here’s a simple client-side implementation of SSE, taken from MDN Web Docs:</p>
<pre><code>const evtSource = new EventSource('sse.php');
const eventList = document.querySelector('ul');

evtSource.onmessage = function(event) {
  const newElement = document.createElement(&quot;li&quot;);

  newElement.textContent = &quot;message: &quot; + event.data;
  eventList.appendChild(newElement);
}
</code></pre>
<p>In the first line, a new EventSource instance is created, that receives server-sent events from a specified URL: in this case, a page called 'sse.php'. In case of an event, an event handler is run after which the message received is written to a new list element and appended to a list already present in the document.</p>
<h2 id="ssesforhighperformancerealtimedatastreams">SSEs for high-performance real-time data streams</h2>
<p>In this blog post, we discussed Server-Sent Events, or simply SSEs. They are used for high-performance real-time data streams from server to client. We described what they are and mentioned different use cases where they come in handy: since SSEs are mono-directional, they are best used for applications that don't need to send data from the client to the server. Those applications are better served by using WebSockets. SSEs are sent over traditional HTTP and come with a set of handy features for developers. SSEs consist of an EventSource client API that manages the messages sent by the server that are defined in the Event Stream Protocol. Some short examples showed how a template message can be used for a real example showing real-time data. Finally, a code example illustrated SSE implementation on the client side.</p>
</div>]]></content:encoded></item></channel></rss>