<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: JullSanders</title>
    <description>The latest articles on Forem by JullSanders (@jullsanders).</description>
    <link>https://forem.com/jullsanders</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F334578%2F8f1678f8-880e-4c0a-abbe-294e1421b15d.png</url>
      <title>Forem: JullSanders</title>
      <link>https://forem.com/jullsanders</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jullsanders"/>
    <language>en</language>
    <item>
      <title>A Simple Telegram Time Tracker Bot Creation</title>
      <dc:creator>JullSanders</dc:creator>
      <pubDate>Mon, 30 Mar 2020 13:01:48 +0000</pubDate>
      <link>https://forem.com/jullsanders/a-simple-telegram-time-tracker-bot-creation-2ncb</link>
      <guid>https://forem.com/jullsanders/a-simple-telegram-time-tracker-bot-creation-2ncb</guid>
      <description>&lt;p&gt;Everyone has Telegram nowadays. All of us use this messenger daily — it’s convenient, easy to use, intuitive, secure, and of course we all love stickers. Besides the personal messaging, we often use group chats — with family, friends and colleagues. In addition to live ordinary users, there are also bots in Telegram. They are created to automate replies, meaning the bot responds to specific messages — commands and performs certain actions. An action can be either a simple greeting in response, or a chain of certain questions and answers to perform specific logic operations.&lt;/p&gt;

&lt;p&gt;Personally I, for instance, constantly use @vkmusic_bot_news chatbot. It’s a simple bot for music search and listening. A user sends a message with the name of a composition or the author and the bot provides variants, which match the request.&lt;/p&gt;

&lt;p&gt;In my example I asked for a song «show must go on» and received several variants to choose from. Having chosen the first variant, I received a track in the next message. It’s convenient that you can listen to music right in the chat with the bot. In such a way a bot can fully substitute a third-party app, like a player on your phone. You just search and listen to the music right away.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HXtIX-aW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585572389417/WGtkgIw0J.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HXtIX-aW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585572389417/WGtkgIw0J.png" alt="image_Teleg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So we can see that the telegram chatbots are rather convenient and multifunctional feature of the messenger. That’s why this article is devoted to a simple chatbot creation. I decided to make a primitive time tracker to show you an example. It’s the simplest case of interaction with the telegram API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simple Telegram chat bot creation
&lt;/h2&gt;

&lt;p&gt;So let’s start.&lt;/p&gt;

&lt;p&gt;You can find the docs here: &lt;a href="https://core.telegram.org/bots/api"&gt;https://core.telegram.org/bots/api&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, you should create and register the bot in the Telegram. Let’s find the all-bots Father &lt;a class="comment-mentioned-user" href="https://dev.to/botfather"&gt;@botfather&lt;/a&gt;
. It’s a bot for bot registration.&lt;/p&gt;

&lt;p&gt;We inform him that we want to create our own chatbot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/newbot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With the next request he asks to create a name for the bot. Since my bot will track time, I give him the corresponding name and write:&lt;/p&gt;

&lt;p&gt;timeTrackerBot&lt;/p&gt;

&lt;p&gt;The next step is to create bot’s username (it will be used for search @username). The condition is — the username must end in bot or _bot. I write:&lt;/p&gt;

&lt;p&gt;my_time_tracker_bot&lt;/p&gt;

&lt;p&gt;It’s ready! We receive a token in response, it means that we have registered a new chat-bot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Development
&lt;/h2&gt;

&lt;p&gt;To start I decided to look what libraries the internet offers to us. The variants are multiple. The full list of the proposed options can be found  &lt;a href="https://core.telegram.org/bots/samples"&gt;here&lt;/a&gt; . My choice was  &lt;a href="https://github.com/borsaco/TelegramBotApiBundle"&gt;TelegramBotApiBundle&lt;/a&gt;  for Symfony. We install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer require borsaco/telegram-bot-api-bundle
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and proceed.&lt;/p&gt;

&lt;p&gt;The bundle supports the work with several bots at a time, besides, there is the option of adjustment (sending only to the developer) and working through proxy. For the test example, we do not need much, so we remove what we do not need.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;config/packages/telegram.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lRq-qT41--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585572791520/zkOC4i-Jy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lRq-qT41--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585572791520/zkOC4i-Jy.png" alt="telegr 3.png"&gt;&lt;/a&gt;&lt;br&gt;
I also carried the token out into the variable APP_TELEGRAM_TOKEN в .env&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bzBQCylx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585572838557/yGnrARy4C.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bzBQCylx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585572838557/yGnrARy4C.png" alt="telegr 4.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  A bit of Theory
&lt;/h2&gt;

&lt;p&gt;Telegram API, so to say works in two modes: you can get updates from the server through getUpdates() method or adjust the webHook. The first variant is good, because it’s extremely simple, but the drawback of it that you should constantly request server if there are any updates. The webHook variant allows not to think about how to get the updates, but to focus on their rendering. In this case Telegram will send the updates itself to the URL we’ll indicate.&lt;/p&gt;

&lt;p&gt;The advantage of the second approach is obvious).&lt;/p&gt;

&lt;p&gt;To register a Webhook we’ll do the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$bot = $botService-&amp;gt;getBot('timeTracker');
$bot-&amp;gt;setWebhook(
    [
'url'=&amp;gt;'https://myWebSite.com/webhook'
]
);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Webhook in the url value is a route, which we’ll make a bit later.&lt;/p&gt;

&lt;p&gt;$botService — is an object of the service Borsaco\TelegramBotApiBundle\Service\Bot, which can be injected in any place of the project.&lt;/p&gt;

&lt;p&gt;Important: telegram API only supports sending to https!&lt;/p&gt;

&lt;p&gt;Let’s now check our Webhook: if you send a message to our telegram bot, it will send us such a data set in response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "update_id": 21406673,
  "message": {
    "message_id": 24,
    "from": {
      "id": 701891111,
      "is_bot": false,
      "first_name": "aleksei",
      "language_code": "ru"
    },
    "chat": {
      "id": 701891111,
      "first_name": "aleksei",
      "type": "private"
    },
    "date": 1580672814,
    "text": "/help",
    "entities": [
      {
        "offset": 0,
        "length": 5,
        "type": "bot_command"
      }
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We are interested in the part with the message text. In this case, it is clear that I sent the bot «/ help».&lt;/p&gt;

&lt;p&gt;Naturally, he does not answer.&lt;/p&gt;

&lt;p&gt;The presence of entities in the response indicates that the message was perceived by the bot as a command (everything that starts with a slash and is written in Latin is a command for Telegram).&lt;/p&gt;

&lt;p&gt;Let’s create a controller. It will be the entry point for the requests from the Telegram API where to the updates will come.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bHSoFELK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585572897890/kvYS61Nue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bHSoFELK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585572897890/kvYS61Nue.png" alt="telegr 5.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For convenience, I put all the logic into the MessageProcessor service, where I pass the string received from Telegram.&lt;/p&gt;

&lt;p&gt;The essence is simple — there are 4 commands: help, start, stop, report.&lt;/p&gt;

&lt;p&gt;To the «help» message or any other one that does not fit into this list, the bot should return a message with a list of the commands available.&lt;/p&gt;

&lt;p&gt;Each command presupposes specific behavior.&lt;/p&gt;

&lt;p&gt;There are also 2 situations when the answer will tell the user that this action is prohibited at the moment (until the time tracker is stopped — you can’t start a new one. Accordingly, while there is no activity — you can’t stop anything either).&lt;/p&gt;

&lt;p&gt;I have added the following entities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User, which has the fields name, telegramId and collection timeLines,&lt;/li&gt;
&lt;li&gt;TimeLine, which has a starting date and an end date: startedAt, stopedAt.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is, a user can have multiple timelines (which have a start time and a stop time).&lt;/p&gt;

&lt;p&gt;We get a json string, and the first thing I do is decode the string and get the object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$response = \json_decode((string)$telegramUpdate);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Further on, I check if there is such a user in the database (the data about the user who wrote to the bot is taken from response-&amp;gt; message-&amp;gt; from). If there is no such a user, we create it.&lt;/p&gt;

&lt;p&gt;Since we want that «help» and «/ help» commands to be perceived by the bot in the same way, we translate the text of the message from the user into lower case and delete the normal and the back slashes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$messageText = mb_strtolower($response-&amp;amp;gt;message-&amp;amp;gt;text);
$messageText = str_replace([&amp;amp;rsquo;\\&amp;amp;rsquo;, &amp;amp;rsquo;/&amp;amp;rsquo;], &amp;amp;rsquo;&amp;amp;rsquo;, $messageText);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we check the received command — if it’s included in the list of commands that we can process. And now, based on what we’ve got, we send a reply.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$messageCommand = $this-&amp;amp;gt;isSupports($messageText) ? $messageText : false;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If it’s either a «start» or «stop» command, we create a TimeLine for this user or end the current one accordingly and inform the user about it.&lt;/p&gt;

&lt;p&gt;If this is a «report», we calculate the time in all timeline for today and send the total number of hours and minutes to the user. We send the messages with the sendMessage method, which accepts an array of parameters. Required parameters are: ’chat_id’ and ’text’.&lt;/p&gt;

&lt;p&gt;A complete list of commands can be found  &lt;a href="https://core.telegram.org/bots/api"&gt;here&lt;/a&gt; .&lt;/p&gt;

&lt;p&gt;Since this is a test case, I limited myself to a simple switch / case for command selection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;switch ($messageCommand) {
  case self::HELP_COMMAND :
     $this-&amp;gt;bot-&amp;gt;sendMessage(['chat_id' =&amp;gt; $user-&amp;gt;getTelegramId(),      'text' =&amp;gt; self::ANSWERS[self::HELP_COMMAND]]);
     break;
  case self::START_COMMAND :
     if ($this-&amp;gt;timelineService-&amp;gt;doesActiveExist($user)) {
        $this-&amp;gt;bot-&amp;gt;sendMessage(['chat_id' =&amp;gt; $user-&amp;gt;getTelegramId(), 'text' =&amp;gt; self::BAD_ANSWERS['existNotStoppedTimeLine']]);
        break;
     }
     $this-&amp;gt;telegramService-&amp;gt;startTimeForUser($user);
     $this-&amp;gt;bot-&amp;gt;sendMessage(['chat_id' =&amp;gt; $user-&amp;gt;getTelegramId(), 'text' =&amp;gt; self::ANSWERS[self::START_COMMAND]]);
     break;
  case self::STOP_COMMAND :
     if ($this-&amp;gt;timelineService-&amp;gt;doesActiveExist($user)) {
        $this-&amp;gt;bot-&amp;gt;sendMessage(['chat_id' =&amp;gt; $user-&amp;gt;getTelegramId(), 'text' =&amp;gt; self::ANSWERS[self::STOP_COMMAND]]);
        break;
     };
     $this-&amp;gt;bot-&amp;gt;sendMessage(['chat_id' =&amp;gt; $user-&amp;gt;getTelegramId(), 'text' =&amp;gt; self::BAD_ANSWERS['timeLineNotFound']]);
     break;
  case self::REPORT_COMMAND :
     $timeForToday = $this-&amp;gt;timelineService-&amp;gt;getTodayTotalByUser($user);
     $this-&amp;gt;bot-&amp;gt;sendMessage(['chat_id' =&amp;gt; $user-&amp;gt;getTelegramId(), 'text' =&amp;gt; \sprintf(self::REPORT_COMMAND, $timeForToday)]);
     break;
  default:
     $this-&amp;gt;bot-&amp;gt;sendMessage(['chat_id' =&amp;gt; $user-&amp;gt;getTelegramId(), 'text' =&amp;gt; self::ANSWERS[self::HELP_COMMAND]]);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;General view of the service is like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G8B8wImV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585573046057/8J85ndvj1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G8B8wImV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585573046057/8J85ndvj1.png" alt="telegr 6.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jbPfcuQ4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585573058940/G-86uJ7W6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jbPfcuQ4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585573058940/G-86uJ7W6.png" alt="telegr 7.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thus, we’ve got a bot, which can help you track the time in course of the day.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--l_ZJqKvV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585573079121/yaN2zS1tX.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--l_ZJqKvV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585573079121/yaN2zS1tX.png" alt="image_Teleg2.png"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;To draw a line, we can safely say that the Telegram API is quite functional and easy to master. A big advantage is good documentation and the ready-made libraries available for the usage.&lt;/p&gt;

&lt;p&gt;Originally published at  &lt;a href="https://stfalcon.com/en/blog/post/telegram-time%20tracking-bot"&gt;Stfalcon.com&lt;/a&gt; .&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Postman: a Quick Start for Development and Testing</title>
      <dc:creator>JullSanders</dc:creator>
      <pubDate>Fri, 27 Mar 2020 14:19:42 +0000</pubDate>
      <link>https://forem.com/jullsanders/postman-a-quick-start-for-development-and-testing-1m5e</link>
      <guid>https://forem.com/jullsanders/postman-a-quick-start-for-development-and-testing-1m5e</guid>
      <description>&lt;p&gt;This article is intended to speed up and simplify the process of mastering the Postman tool basic functionality.&lt;/p&gt;

&lt;p&gt;The company positions its product as an API development platform.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;But why does everyone love Postman platform?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;— This is a powerful API testing and development tool, which at the same time has a simple and intuitive interface. If this is the first time you open this program and know the difference between POST and GET, then you can easily send your first request. However, there are some nontrivial tricks which make life much easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Postman requests
&lt;/h2&gt;

&lt;p&gt;So let’s start ( &lt;a href="https://www.postman.com/downloads/"&gt;get postman app&lt;/a&gt; ).&lt;/p&gt;

&lt;p&gt;We look for the necessary button (1) in the upper central part and create a workspace (or select from the existing ones as I do). The notion of a workspace comprises grouping of your projects —it may be a folder or desktop, if you want.&lt;/p&gt;

&lt;p&gt;Then we click plus (2) and create a request tab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--A-lL1kL---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585317597185/tkQ_tcwU7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A-lL1kL---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585317597185/tkQ_tcwU7.png" alt="Postman 2 (1).png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everything is extremely simple with a GET request: we set the url, add parameters (you can «turn on» and «turn off» the parameters), press send and see the answer in the bottom half of the screen (on the server, I just return the html page and present all the incoming parameters in json format). Switching between tabs in the response area, we can find any information and even switch between views and output formats.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Kz6RDA8m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585317624791/VcJRwuUTc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Kz6RDA8m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585317624791/VcJRwuUTc.png" alt="Postman 3 (1).png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To the right of the Send button there is a Save button. At the attempt of saving, postman will ask you to specify/create a collection. A collection is a group of requests in the workspace (sorted, for example, by project). I created a test_collection collection. We save your request there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Postman environment
&lt;/h2&gt;

&lt;p&gt;Let’s proceed further.&lt;/p&gt;

&lt;p&gt;There is the concept of environment in postman — it’s a set of variables that can be assigned a value and used during your work. The environments may be multiple (for sales, for stages, for different versions — for anything). The environment is tied to the operational environment: each operational environment has its own postman environment.&lt;/p&gt;

&lt;p&gt;We click on the gear in the upper right corner and create an environment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BYUbu15t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585317652442/lhNqDHR8h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BYUbu15t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585317652442/lhNqDHR8h.png" alt="Postman 4 (1).png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have created a local_test_env environment with 2 variables: host and username. There are 2 columns for the variable value. The only difference is that if you share your project with your team, then the value from the first column will be shared, and the queries that you make on your PC will use the second column. Your postman account synchronization also occurs from the first column.&lt;/p&gt;

&lt;p&gt;I usually duplicate them — it’s better to make it a habit to fill everything in, and replace the data in case of necessity.&lt;/p&gt;

&lt;p&gt;We select our environment as active (there is a drop-down list to the left of the gear).&lt;/p&gt;

&lt;p&gt;Let’s send POST using variables. We should add a tab and select the type of POST request. We want to use the host variable from our environment. Variables are written in double curly braces. We put the braces and begin to write the name of the variable, postman will suggest us to select a variable from the current environment. Let’s choose. Since this is a POST request, we proceed to the body tab (under the address bar) and add the username parameter, the value for which will be a variable. I added several more parameters for illustrative purposes, the server will return the sum of them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8PPKxS4I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585317671927/6vjNOOWBg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8PPKxS4I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585317671927/6vjNOOWBg.png" alt="Postman 5 (1).png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The server returned a json line to us.&lt;/p&gt;

&lt;p&gt;In Postman, we can interact with the received result, programmatically send requests, and write tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some words about tests
&lt;/h2&gt;

&lt;p&gt;We open the console with a combination of cmd + alt + c (ctrl + alt + c for Windows). I made one request and see its logs. In addition, you can output something to the console with the following command:&lt;/p&gt;

&lt;p&gt;console.log("something")&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bh_r_uHd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585317799534/HoPOqJrGu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bh_r_uHd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585317799534/HoPOqJrGu.png" alt="Postman 6 (1).png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tests are written on the Tests tab. I will give you a few examples of simple tests, and then we’ll see how to work with them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is a common anonymous function. In this case, we say that the answer should have a status of 200.&lt;/p&gt;

&lt;p&gt;Due to the fact that we get a json line in the server response, we can parse this into a variable. Let’s do it with the following line:&lt;/p&gt;

&lt;p&gt;var jsonData = pm.response.json();&lt;br&gt;
Then we use a new variable:&lt;/p&gt;

&lt;p&gt;pm.test("check number equal 9 ", function () {&lt;br&gt;
    pm.expect(jsonData.total).to.eql(9);&lt;br&gt;
});&lt;br&gt;
In this example, we expect that total in jsonData will be equivalent to 9.&lt;/p&gt;

&lt;p&gt;Now let’s try to compare the received value of the field «name» from jsonData with the username variable we have in our environment.&lt;/p&gt;

&lt;p&gt;You can get a variable from the environment like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pm.environment.get('username')
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now an equivalence test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pm.test("check name equal variable username", function () {
    pm.expect(jsonData.name).to.eql(pm.environment.get('username'));
});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can not only read, but also write in the environment.&lt;/p&gt;

&lt;p&gt;We are trying to get the variable “total” from the environment (it is not there yet).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var sum = pm.environment.get('total');
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Since we did not get anything from the environment, there will be nothing in «sum» either. We create a «total» in the environment and write in there the value obtained from jsonData.total.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (!sum) {
    pm.environment.set('total', jsonData.total);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now let's see what has happened:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Gb69OZMw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585317946612/YitJ2RXil.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Gb69OZMw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585317946612/YitJ2RXil.png" alt="Postman 7 (1).png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All the tests except the first one have passed. The server returns the code 201 to us, while we expect 200. Our variable is in the environment, so everything works.&lt;/p&gt;

&lt;p&gt;Now, when you have some understanding of postman, let’s try to send a request, or rather a few.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Task&lt;/em&gt;&lt;/strong&gt;: to send a certain number of requests, each time changing a parameter.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First we create a new collection of loop requests,&lt;/li&gt;
&lt;li&gt;Then we create a POST request,&lt;/li&gt;
&lt;li&gt;After that body we add the parameter «digit», which will change into the request (we take the value from the environment).&lt;/li&gt;
&lt;li&gt;In the pre-request Script tab, we add the following code:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var digits = pm.environment.get('digits');

if (!digits) {
    digits = ["2", "4", "5", "22", "13", "6"];
}

console.log("digits: " + digits);

var currentDigit = digits.shift();

pm.environment.set('digit', currentDigit);
pm.environment.set('digits', digits);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;(We check if there is a variable «digits» in the environment, and create it, if there is no such a variable. We output the array «digits» to the console before the request. Next, we take the first element from «digits», removing the value from the array itself. And set the value in the environment for «digit» and «digits». There is one first element less in digits already.)&lt;/p&gt;

&lt;p&gt;In the tab «Tests», we write the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var digits = pm.environment.get('digits');
var digit = pm.environment.get('digit');

console.log("server got digit = " + pm.response.json().digit);

if (digits &amp;amp;&amp;amp; digits.length &amp;gt; 0) {
    postman.setNextRequest("loop request");
} else {
    postman.setNextRequest(null);
    console.log("stop");
    pm.environment.unset("digits");
    pm.environment.unset("digit");
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;(We take «digit» and «digits» from the environment and put some information into the console. If «digits» is not empty, we specify the following request setNextRequest and pass the current request as a parameter. Otherwise, we say that nothing else should be done, clear the environment variables and print the line «stop» in the console.)&lt;/p&gt;

&lt;p&gt;After all that we open Runner (in the upper left corner), select our new collection and our loop request, select the environment and click Run loop.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w-xvMZRt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585317955659/WhH_9vqwC.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w-xvMZRt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585317955659/WhH_9vqwC.png" alt="Postman 8 (1).png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the runner, we see that 6 requests have been completed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7Uj9LsiW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585318141688/fpDpddjDL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7Uj9LsiW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585318141688/fpDpddjDL.png" alt="Postman 9 (1).png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We then open the console and watch the logs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jDtWw4pZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585318309971/PgD-IEl27.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jDtWw4pZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1585318309971/PgD-IEl27.png" alt="Postman 10.png"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;We tested some of the of Postman’s features in practice, made sure that it’s really a very convenient and multi-functional development platform that simplifies the processes of development and API testing.&lt;/p&gt;

&lt;p&gt;Comprehensive documentation can be found  &lt;a href="https://learning.postman.com/docs/postman/launching-postman/introduction/"&gt;here&lt;/a&gt; .&lt;/p&gt;




&lt;p&gt;Originally published at  &lt;a href="https://stfalcon.com/en/blog/post/pstman"&gt;Stfalcon.com&lt;/a&gt; &lt;/p&gt;

</description>
    </item>
    <item>
      <title>A Quick API Platform Hands On Review for Symfony App Development</title>
      <dc:creator>JullSanders</dc:creator>
      <pubDate>Tue, 10 Mar 2020 15:37:37 +0000</pubDate>
      <link>https://forem.com/jullsanders/a-quick-api-platform-hands-on-review-for-symfony-app-development-4kmh</link>
      <guid>https://forem.com/jullsanders/a-quick-api-platform-hands-on-review-for-symfony-app-development-4kmh</guid>
      <description>&lt;p&gt;There are many ways of REST API implementation in a  &lt;a href="https://stfalcon.com/en/services/symfony"&gt;symfony app&lt;/a&gt; . One of them is REST API Patform. We get REST API protocol support, documentation and Swagger UI with the possibility to test endpoints out of the box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;

&lt;p&gt;We add API platform into the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer req api
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Swagger UI is active by default and available at &lt;a href="http://localhost/api:"&gt;http://localhost/api:&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BZ_qlt3j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zf6r3azqmursx8q6pg3v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BZ_qlt3j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zf6r3azqmursx8q6pg3v.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While no end point is defined (the operation in API Platform terms), it’s simple to do it. You should only add annotation @ApiResource to the entity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&amp;lt;?php

declare(strict_types=1);

namespace App\Entity;

...

/**
 * Customer.
 *
 * @ORM\Entity()
 *
 * @ApiResource()
 */
class Customer
{
    /**
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6HWSDMFr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rc32766ag3kwmjc9795u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6HWSDMFr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rc32766ag3kwmjc9795u.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A full set of CRUD operations has appeared. All the operations are divided into 2 groups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;collection operations — operations with the collection of elements&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;item operations — operations with a data element.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Collection operations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;GET - to get a list of elements;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;POST - to add an element to the collection.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Item operations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;GET - to get an element by ID;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PUT - to change an element;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DELETE - to delete an element from the collection;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PATCH - to partly substitute the element.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Element configuration can be described in the annotations, xml or yaml files. The yaml files location is defined by the path parameter of the configuration &lt;em&gt;file config/packages/api_platform.yaml&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;api_platform:
    mapping:
        paths:
            - '%kernel.project_dir%/src/Entity'
            - '%kernel.project_dir%/config/api_platform'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If it’s needed, we can restrict the set of the operations with a list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php

declare(strict_types=1);

namespace App\Entity;

...

/**
 * Customer.
 *
 * @ORM\Entity()
 *
 * @ApiResource(collectionOperations={"get","post"},itemOperations={"get","delete"})
 */
class Customer
{
    /**
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;or in a yaml file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# config/api_platform/customer.yaml
App\Entity\Customer:
  itemOperations:
    get: ~
    delete: ~
  collectionOperations:
    get:
    post: ~
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I0MHI7ot--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vtzpjtrtfm6og858ipqb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I0MHI7ot--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vtzpjtrtfm6og858ipqb.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Per-page collection display
&lt;/h2&gt;

&lt;p&gt;The elements collections support per-page layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validation
&lt;/h2&gt;

&lt;p&gt;If it is necessary to check the input data, it is enough to add an entity property validator — the API platform generates an error message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
    /**
     * @var string
     *
     * @ORM\Column(type="string", length=50, nullable=false)
     *
     * @Assert\NotBlank()
     * @Assert\NotNull()
     * @Assert\Length(max="50")
     */
    private $name;
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;with a line of more than 50 characters we’ll get an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "@context": "/api/contexts/ConstraintViolationList",
  "@type": "ConstraintViolationList",
  "hydra:title": "An error occurred",
  "hydra:description": "name: This value is too long. It should have 50 characters or less.",
  "violations": [
    {
      "propertyPath": "name",
      "message": "This value is too long. It should have 50 characters or less."
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Related Objects
&lt;/h2&gt;

&lt;p&gt;To provide the uniqueness of object identifiers API platform uses IRI (Internationalized Resource Identifier). Each object’s IRI coincides with the GET request for this object /api/customer/123. The same type of identifier is used for the requests of objects adding and editing.&lt;/p&gt;

&lt;p&gt;Let’s add a new entity related to the Customer entity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
/**
 * Message.
 *
 * @ORM\Entity()
 *
 * @ApiResource()
 */
class Message
{
    ...

    /**
     * @var Customer
     *
     * @ORM\ManyToOne(targetEntity="App\Entity\Customer", inversedBy="messages")
     * @ORM\JoinColumn(nullable=false)
     *
     * @Assert\NotBlank()
     * @Assert\Type(type="App\Entity\Customer")
     */
    private $createdBy;
    ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The body of the request to add a new item will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
   ...
   “createBy”: “/api/customer/123”,
   ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The list of the collection items will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "@context": "/api/contexts/Customer",
  "@id": "/api/customers",
  "@type": "hydra:Collection",
  "hydra:member": [
    {
      "@id": "/api/customers/1",
      "@type": "Customer",
      "id": 1,
      "name": "name 1",
      "email": "test@test.com",
      "messages": [
        "/api/messages/3",
        "/api/messages/2",
        "/api/messages/1"
      ]
    },
  ],
  "hydra:totalItems": 1
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As you can see the list of messages is presented as an array of IRI strings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   "messages": [
        "/api/messages/3",
        "/api/messages/2",
        "/api/messages/1"
      ]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The request http: // localhost / api / messages / 1 will provide such result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "@context": "/api/contexts/Message",
  "@id": "/api/messages/1",
  "@type": "Message",
  "id": 1,
  "createdBy": "/api/customers/1",
  "title": "title 1",
  "text": "message 1"
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Using serialization groups, we can display not only the IRI of the object, but also the data of the nested object in case of necessity. To do this, for the item operation GET, we’ll specify the serialization group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# config/api_platform/message.yaml
App\Entity\Message:
  itemOperations:
    get:
      normalization_context:
        groups:
          - message
    put: ~
    delete: ~
  collectionOperations:
    get: ~
    post: ~
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We’ll add the group to the properties of the Message and Customer objects. Now the result of the same query will be different.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "@context": "/api/contexts/Message",
  "@id": "/api/messages/1",
  "@type": "Message",
  "id": 1,
  "createdBy": {
    "@id": "/api/customers/1",
    "@type": "Customer",
    "id": 1,
    "name": "customer 1"
  },
  "title": "title 1",
  "text": "message 1"
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Filters
&lt;/h2&gt;

&lt;p&gt;API platform has a built-in filter mechanism:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Doctrine ORM&lt;/strong&gt; and &lt;strong&gt;Mongo ODM&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Search filter (string fields);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Date filter;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Boolean filter;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Range filter (numeric fields);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Exists filter (nullable values);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Order filter (collection’s sort order changes).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Elasticsearch&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Ordering filter;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Matching filter;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Term filter.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Serialization filters&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Group filter (defines a list of serialization groups for the query result);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Property filter (defines the list of displayed properties).&lt;br&gt;
There is also a possibility to define the user filters types.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s add the string fields filter for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
/**
 * Message.
 *
 * @ORM\Entity()
 *
 * @ApiResource()
 * @ApiFilter(SearchFilter::class, properties={"title": "iend", "text": "partial"})
 */
class Message
{
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We have added 2 filters&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;by title field — it will search for all the elements, which have the title field ending as in the search sample. The search is case independent (symbol i in the beginning of the filter type name).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;by text field — will search for all the elements having a search sample in the text field. The search depends on case.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Data Transfer Objects (DTO) Usage&lt;/strong&gt;&lt;br&gt;
In case of necessity we can use not the entities directly, but DTOs. We have to take 3 steps for this.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;To describe DTOs input and output classes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Indicate the classes as input and output in our entity parameters.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Describe Data Transformer classes for entity into DTO transformation and back.&lt;br&gt;
Input DTO:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
/**
 * MessageInputDto.
 */
class MessageInputDto
{
...
}
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Output DTO:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
/**
 * MessageOutputDto.
 */
class MessageOutputDto
{
...
}
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Adding DTO classes into entity description:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
 * Message.
 *
 * @ORM\Entity()
 *
 * @ApiResource(
 *     input=MessageInputDto::class,
 *     output=MessageOutputDto::class
 * )
 */
class Message
{
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Data Transformer for input DTO:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
 * MessageInputTransformer.
 */
class MessageInputTransformer implements DataTransformerInterface
{
    /**
     * {@inheritDoc}
     */
    public function transform($object, string $to, array $context = []): Message
    {
        $message = new Message();
        ... 

        return $message;
    }

    /**
     * {@inheritDoc}
     */
    public function supportsTransformation($data, string $to, array $context = []): bool
    {
        return Message::class === $to &amp;amp;&amp;amp; $data instanceof MessageInputDto;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Data Transformer for output DTO:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
 * MessageOutputTransformer.
 */
class MessageOutputTransformer implements DataTransformerInterface
{
    /**
     * {@inheritDoc}
     */
    public function transform($object, string $to, array $context = []): MessageOutputDto
    {
        $dto = new MessageOutputDto();
        ... 

        return $dto;
    }

    /**
     * {@inheritDoc}
     */
    public function supportsTransformation($data, string $to, array $context = []): bool
    {
        return MessageOutputDto::class === $to &amp;amp;&amp;amp; $data instanceof Message;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Such an approach has a drawback – DTO validation won’t work. To correct this, you should change the transformer class input DTO.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
 * MessageInputTransformer.
 */
class MessageInputTransformer implements DataTransformerInterface
{
    private $validator;

    /**
     * @param ValidatorInterface $validator
     */
    public function __construct(ValidatorInterface $validator)
    {
        $this-&amp;gt;validator = $validator;
    }

    /**
     * {@inheritDoc}
     */
    public function transform($object, string $to, array $context = []): Message
    {
        $this-&amp;gt;validator-&amp;gt;validate($object);

        $message = new Message();
        ... 

        return $message;
    }

    /**
     * {@inheritDoc}
     */
    public function supportsTransformation($data, string $to, array $context = []): bool
    {
        return Message::class === $to &amp;amp;&amp;amp; $data instanceof MessageInputDto;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  User controllers
&lt;/h2&gt;

&lt;p&gt;In case none of the methods described above allows us to get the desirable result, we have an opportunity to use our own controller.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Variant 1&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
 * CustomerAction.
 */
class CustomerAction
{
    private $customerService;

    /**
     * @param CustomerService $customerService
     */
    public function __construct(CustomerService $customerService)
    {
        $this-&amp;gt;customerService = $customerService;
    }

    /**
     * @param Customer $data
     *
     * @return Customer
     */
    public function __invoke(Customer $data): Customer
    {
        $this-&amp;gt;customerService-&amp;gt;handle($data);

        return $data;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The name of the method parameter is __invoke, there should obligatory be $data. Otherwise it would be empty. Let’s add the controller into the entity configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
 * Customer.
 *
 * @ORM\Entity()
 *
 * @ApiResource(itemOperations={
 *     "get",
 *     "delete",
 *     "customer_action"={
 *          "method"="POST",
 *          "path"="/api/customer/{id}/action",
 *          "controller"=CustomerAction::class,
 *     }
 * })
 */
class Customer
{
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Variant 2&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
 * Customer.
 *
 * @ORM\Entity()
 *
 * @ApiResource(itemOperations={
 *     "get",
 *     "delete",
 *     "customer_action"={"route_name"="customer_action_route"}
 * })
 */
class Customer
{
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And we add the controller class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
 * CustomerController.
 */
class CustomerController
{
    /**
     * @Route("/api/customer/{id}/action", name="customer_action_route", methods={"POST"})
     *
     * @param Customer $customer
     *
     * @return Response
     */
    public function executeAction(Customer $customer): Response
    {
        ...


        return new Response(...);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  User data sources
&lt;/h2&gt;

&lt;p&gt;Data access with Doctrine ORM and Elasticsearch-PHP are built in as standard in the API Platform. Doctrine ORM is active immediately after installation; Elasticsearch-PHP can be switched on in the configuration file.&lt;/p&gt;

&lt;p&gt;For the other data sources options, we can define our own data source by adding a new class. We should implement the CollectionDataProviderInterface, RestrictedDataProviderInterface interfaces for the collection provider or ItemDataProviderInterface, RestrictedDataProviderInterface for the collection item provider. Another provider is involved in data modification, it implements the ContextAwareDataPersisterInterface interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Impressions, findings...
&lt;/h2&gt;

&lt;p&gt;API Platform is much more extensive than it was revealed in this review. We did not mention here the automated generation of documentation and json schemes, serialization management, security and access control, platform own extensions, integration with Symfony Messenger and other features. Full description can be found in the official documentation at &lt;a href="https://api-platform.com/docs/"&gt;https://api-platform.com/docs/&lt;/a&gt;. The purpose of this article was the hands-on review of the tool.&lt;/p&gt;

&lt;p&gt;API Platform includes a large toolkit for writing a REST API which will allow you implement most of the tasks that arise during the REST API Backend writing with a minimum amount of code. At the same time all the actions, which are beyond CRUD should be realized independently in the form of standard controllers.&lt;/p&gt;

&lt;p&gt;Originally published at &lt;a href="https://stfalcon.com/en/blog/post/api-platform"&gt;Stfalcon.com&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Awareness API: What Is It and How It May Help?</title>
      <dc:creator>JullSanders</dc:creator>
      <pubDate>Sun, 01 Mar 2020 12:44:24 +0000</pubDate>
      <link>https://forem.com/jullsanders/awareness-api-what-is-it-and-how-it-may-help-jhg</link>
      <guid>https://forem.com/jullsanders/awareness-api-what-is-it-and-how-it-may-help-jhg</guid>
      <description>&lt;p&gt;Every year our life and daily routine are more and more closely connected with mobile phones. Modern life is extremely dynamic and that’s why mobile apps should match the users’ activity. Awareness API exists just for the purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is API in app development and how it may help?
&lt;/h2&gt;

&lt;p&gt;Google Awareness API allows us to monitor the user activity, it’s possible at a certain moment as well as while the app operates.&lt;/p&gt;

&lt;p&gt;API allows us to get the data as to the:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Headphones state (plugged/unplugged);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Location;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Activity (stands, drives, runs, walks);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Time (local user’s time o set certain frames);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reaction to beacons.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are plenty of situations, where these features can be used. If it’s a sports app for running, for instance, it’s possible to find out when the user started his training, and if it’s a restaurant app, there is an opportunity to notify the user about special offers or invite him to visit the restaurant when he is somewhere nearby.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Awareness API is divided into two parts, namely:&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Snapshot API *&lt;/em&gt;— it allows to get the live info at a certain moment&lt;br&gt;
*&lt;em&gt;Fence API *&lt;/em&gt;— it registers one or several triggers, which inform us if the user is now active or not.&lt;br&gt;
To better understand it, please find the example of an app code here:  &lt;a href="https://github.com/stfalcon-studio/GoogleAwarenessDemo"&gt;https://github.com/stfalcon-studio/GoogleAwarenessDemo&lt;/a&gt; .&lt;/p&gt;

&lt;p&gt;To begin we should get an API key, here is a perfect guide from Google - &lt;a href="https://developers.google.com/awareness/android-api/get-a-key"&gt;https://developers.google.com/awareness/android-api/get-a-key&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As soon as we’ve got the key, we should add it to the manifest file of your project. It’s like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;meta-data
    android:name="com.google.android.awareness.API_KEY"
     android:value="YOUR_API_KEY"/&amp;gt;
&amp;lt;/application&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you have only to add dependency to gradle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;implementation 'com.google.android.gms:play-services-awareness:{last_version}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don’t forget about the permissions, you should add the following to the manifest for the purpose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION"/&amp;gt;
    &amp;lt;uses-permission android:name="android.permission.ACTIVITY_RECOGNITION"/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;*Don’t also forget about the permissions checking in the app.&lt;/p&gt;

&lt;p&gt;That’s all, now you can use the possibilities of Awareness API in full.&lt;/p&gt;

&lt;h2&gt;
  
  
  Snapshot API
&lt;/h2&gt;

&lt;p&gt;As it was already mentioned Snapshot API informs about the live status.&lt;/p&gt;

&lt;p&gt;You should first initialize the SnapshotClient.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;al snapshotClient = Awareness.getSnapshotClient(this)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can get data about the user’s activity right at the moment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Headphones
&lt;/h2&gt;

&lt;p&gt;With a Snapshot API we can find the current state of headphones, whether they are plugged in or unplugged. It will be useful for sports apps development, for instance, to notify the user about his goal achievement, if his headphones are plugged in.&lt;/p&gt;

&lt;p&gt;To get to know the headphones state, you should just call the function &lt;code&gt;getHeadphoneState()&lt;/code&gt; and to get the result to add &lt;code&gt;addOnSuccessListener&lt;/code&gt;, which will turn us back the &lt;code&gt;HeadphoneStateResponse&lt;/code&gt;, with the &lt;code&gt;headphoneState&lt;/code&gt;. Further on &lt;code&gt;getState&lt;/code&gt; - will return us «1» or «2», which correspond to the status &lt;code&gt;PLUGGED_IN&lt;/code&gt; or &lt;code&gt;UNPLUGGED&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It’s also reasonable to add &lt;code&gt;addOnFailureListener&lt;/code&gt;, which returns &lt;code&gt;Exception&lt;/code&gt; if something has gone wrong.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private fun snapshotCheckHeadphones() {
        snapshotClient.headphoneState
            .addOnSuccessListener {
                snapshotHeadphonesContainer.text = it.headphoneState.toString(this)
            }
            .addOnFailureListener {
              handleSnapshotError(snapshotHeadphonesContainer, it)
            }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;toString&lt;/code&gt;&lt;/strong&gt; - this is the expanding feature to make the code look simpler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun HeadphoneState.toString(context: Context): String {
    return context.getString(
        R.string.headphones_state,
        if (state == HeadphoneState.PLUGGED_IN) {
            context.getString(R.string.headphones_state_connect)
        } else {
            context.getString(
                R.string.headphones_state_disconnected
            )
        }
    )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Location
&lt;/h2&gt;

&lt;p&gt;With the help of Snapshot API we can also find out the current user’s location.&lt;/p&gt;

&lt;p&gt;The examples of the usage are numerous in this case, if for instance, the app has a list of some places, we can offer the user to visit this or that place if he is interested in it and is somewhere nearby.&lt;/p&gt;

&lt;p&gt;To get location you should call &lt;code&gt;getLocation()&lt;/code&gt; in &lt;code&gt;OnSuccess&lt;/code&gt; we will get &lt;code&gt;LocationResponse&lt;/code&gt;, which contains information about the user’s locale. We can get the info with &lt;code&gt;getLocation()&lt;/code&gt;, which contains latitude, longitude, altitude and so on.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private fun snapshotGetLocation() {
        snapshotClient.location
            .addOnSuccessListener {
                with(it.location) {
                    snapshotLocationContainer.text = getString(
                        R.string.user_location,
                        latitude, longitude, altitude, accuracy
                    )
                } 
            }
            .addOnFailureListener {
                handleSnapshotError(snapshotLocationContainer, it)
            }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;*&lt;code&gt;Location&lt;/code&gt; *- it’s a standard class of the Android SDK, so you can check the manual for the detailed description of all the available functions.&lt;/p&gt;

&lt;p&gt;**Note: **don’t forget to add &lt;code&gt;android permission ACCESS_FINE_LOCATION&lt;/code&gt; to the manifest as well as permission for checking.&lt;/p&gt;

&lt;h2&gt;
  
  
  User Activity
&lt;/h2&gt;

&lt;p&gt;As it was already mentioned, Awareness API also allows to check the current user activity. It can be extremely useful in the fitness app development or some app with news. When the user does not move he can be offered to read some article.&lt;/p&gt;

&lt;p&gt;The realization approach is the same: we need to call &lt;code&gt;getDetectedActivity&lt;/code&gt; feature, which in case of success will turn us &lt;code&gt;DetectedActivityResponse&lt;/code&gt; back. From it we can take data about the user activity. We have the following features available: &lt;code&gt;getMostProbableActivity&lt;/code&gt;, which will return us the most probable activity and &lt;code&gt;getProbableActivities&lt;/code&gt;, which will return us the list of possible activities sorted from the most probable one.&lt;/p&gt;

&lt;p&gt;Activity has two parameters, namely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Activity type — we use &lt;code&gt;getType()&lt;/code&gt; for getting the type of activity, the example will be below.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Probability of the activity — which is got due to the &lt;code&gt;getConfidence()&lt;/code&gt; feature — it will return int value from 0 to 100.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Example:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;snapshotClient.detectedActivity
            .addOnSuccessListener {
                with(it.activityRecognitionResult) {
                    snapshotActivityContainer.text = getString(
                        R.string.user_activity,
                        mostProbableActivity.stateString(),
                        mostProbableActivity.confidence
                    )
                }
            }
            .addOnFailureListener {
                handleSnapshotError(snapshotActivityContainer, it)
            }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;stateString&lt;/code&gt; - it’s the function of extension, which returns the text value of the activity type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun DetectedActivity.stateString(): String {
    return when (type) {
        0 -&amp;gt; "IN_VEHICLE"
        1 -&amp;gt; "ON_BICYCLE"
        2 -&amp;gt; "ON_FOOT"
        3 -&amp;gt; "STILL"
        4 -&amp;gt; "UNKNOWN"
        5 -&amp;gt; "TILTING"
        6, 9, 10, 11, 12, 13, 14, 15 -&amp;gt; type.toString()
        7 -&amp;gt; "WALKING"
        8 -&amp;gt; "RUNNING"
        16 -&amp;gt; "IN_ROAD_VEHICLE"
        17 -&amp;gt; "IN_RAIL_VEHICLE"
        18 -&amp;gt; "IN_TWO_WHEELER_VEHICLE"
        19 -&amp;gt; "IN_FOUR_WHEELER_VEHICLE"
        else -&amp;gt; type.toString()
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Fence API
&lt;/h2&gt;

&lt;p&gt;Awareness API allows to put certain conditions and «listen» when the activity of the users coincides with it.&lt;/p&gt;

&lt;p&gt;We can check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The location of a user and check if he entered a certain area.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The user activity when he or she, for instance, has started/continues/stopped to walk, run, drive, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Headphones state, when they have been plugged in or unplugged.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When the user has entered the area of beacons operation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Time frames, when the user is in a certain timeframe of a certain weekday.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can also combine several features, using the operators AND, OR or NOT. We can for instance check the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The user runs with headphones plugged in.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The user drives and has entered a certain area.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The headphones are plugged in in an evening time,without activity (so the user is likely in bed).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;How to start using Fence.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Firstly, to get the information about the fence state change we should create our own BroadcastReceiver.&lt;/p&gt;

&lt;p&gt;Like this for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;inner class FenceReceiver : BroadcastReceiver() {

        override fun onReceive(context: Context, intent: Intent) {
            val fenceState = FenceState.extract(intent)
            if (fenceState.fenceKey == FENCE_KEY) {
                when (fenceState.currentState) {
                    FenceState.TRUE -&amp;gt; {
// We've entered the fence                    
}
                    FenceState.FALSE -&amp;gt; {
                        // We're not in the fence
                    }
                    FenceState.UNKNOWN -&amp;gt; {
                        // Something went wrong
                    }
                 }
            }
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will receive all the changes which refer to our registered fences. For instance, we wait till the user plugs the headphones in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We check if the FENCE_KEY, which came coincides with the one we have registered (what’s it and where it’s got from we’ll see below).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Then we check the state, if it’s TRUE, then the headphones are plugged in if it’s FALSE, then vice versa and the UNKNOWN state means an error has occurred and the system fails to understand what the headphones’ status is.&lt;br&gt;
**Note: **don’t forget to register FenceReceiver in &lt;code&gt;onCreate&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;registerReceiver(fenceReceiver, IntentFilter(FENCE_RECEIVER_ACTION))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And to disconnect it at &lt;code&gt;onDestroy&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;unregisterReceiver(fenceReceiver)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the system to send us changes, we should create PendingIntent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val intent = Intent(FENCE_RECEIVER_ACTION)
        val mPendingIntent =
            PendingIntent.getBroadcast(this, 0, intent, 
PendingIntent.FLAG_UPDATE_CURRENT)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we should define which activity we are interested in. Let’s take the headphones plugged in and running for example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val fence = AwarenessFence.and(HeadphoneFence.during(HeadphoneState.PLUGGED_IN), DetectedActivityFence.during(DetectedActivityFence.RUNNING))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we gather it all together and register.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fenceClient.updateFences(
            FenceUpdateRequest.Builder().addFence(
                FENCE_KEY,
                fence,
                mPendingIntent
            ).build()
        )
            .addOnSuccessListener {
                //Fence created
            }
            .addOnFailureListener {
// Error while creating
            }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;FENCE_KEY is necessary for us to distinguish various registered fences.&lt;/p&gt;

&lt;p&gt;You should also not forget about fence deleting when we have finished working with it. We use &lt;code&gt;removeFence()&lt;/code&gt; for the purpose, where the necessary FENCE_KEY is rendered as a parameter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fenceClient.updateFences(
            FenceUpdateRequest.Builder()
                .removeFence(FENCE_KEY).build()
        )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;*if it’s necessary, you can also add successListener and failureListener for deleting.&lt;/p&gt;

&lt;p&gt;Now knowing how to create and delete fence we can look at what types of fence exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Headphone Fence
&lt;/h2&gt;

&lt;p&gt;With headphones everything is alike and we have the following fences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;AwarenessFence headphonesPluggedInFence = HeadphoneFence.during(HeadphoneState.PLUGGED_IN;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AwarenessFence headphonesUnpluggedFence = HeadphoneFence.during(HeadphoneState.UNPLUGGED;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Location Fence
&lt;/h2&gt;

&lt;p&gt;We can create the so-called «locations» and as soon as the user enters them we’ll know about it. We can check if he has entered/left a certain area or stays in it.&lt;/p&gt;

&lt;p&gt;You need to specify the data about the latitude, longitude, and radius of the location (for checking if the user is inside the area, you should also indicate how many milliseconds should a user stay in it).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AwarenessFence inLocationFence = LocationFence.in(
latitude, longitude, radius, timeInMillis); 
AwarenessFence exitingLocationFence = LocationFence.exiting(
latitude, longitude, radius); 
AwarenessFence enteringLocationFence = LocationFence.entering(
latitude, longitude, radius);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Activity Fence
&lt;/h2&gt;

&lt;p&gt;We can also get to know when a user has started, continues or finished a certain activity. The class &lt;code&gt;DetectedActivityFence&lt;/code&gt;, is used for the purpose, it has the &lt;code&gt;starting&lt;/code&gt;, &lt;code&gt;during&lt;/code&gt; and &lt;code&gt;stopping&lt;/code&gt; features. The type of activity is added to these features as a parameter. Let’s for example create fences for all running states:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AwarenessFence running = DetectedActivityFence.during(DetectedActivity.RUNNING);
 AwarenessFence startRunning = DetectedActivityFence.starting(DetectedActivity.RUNNING);
 AwarenessFence stopRunning = DetectedActivityFence.stopping(DetectedActivity.RUNNING);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Time Fence
&lt;/h2&gt;

&lt;p&gt;We can create fence for a certain period of time or a certain day.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AwarenessFence workingHoursFence = TimeFence.inInterval(startTime, endTime);
AwarenessFence fridayFence = TimeFence.inFridayInterval(
                                      timeZone, startTime, endTime);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s all.&lt;/p&gt;

&lt;p&gt;We have had an overlook what an API is and how to use Fences and Snapshots. Thanks to them we can create more flexible apps, which facilitate the user’s usage of the application.&lt;/p&gt;

&lt;p&gt;Originally published at  &lt;a href="https://stfalcon.com/en/blog/post/awareness_api"&gt;https://stfalcon.com&lt;/a&gt; .&lt;/p&gt;

</description>
      <category>android</category>
      <category>mobiledevelopment</category>
    </item>
    <item>
      <title>What’s New in Watch OS 6?
Stand-Alone WatchOS Application with SwiftUI.</title>
      <dc:creator>JullSanders</dc:creator>
      <pubDate>Mon, 17 Feb 2020 12:22:36 +0000</pubDate>
      <link>https://forem.com/jullsanders/what-s-new-in-watch-os-6-stand-alone-watchos-application-with-swiftui-7bk</link>
      <guid>https://forem.com/jullsanders/what-s-new-in-watch-os-6-stand-alone-watchos-application-with-swiftui-7bk</guid>
      <description>&lt;p&gt;Last year on the WWDC 2019 Apple has announced iOS 13, watchOS 6, macOS X Catalina and a lot more. Until now a lot has been said about all those shiny new things. A lot of people suggest the latest WWDC be an as big step forward as the one during which Swift was introduced.&lt;/p&gt;

&lt;p&gt;To my mind, the biggest changes do affect watchOS. Let’s face it, SwiftUI is available only since iOS 13, watchOS 6 and macOS Catalina and most of the developers would like to preserve compatibility with the previous version or two at least. Moreover, SwiftUI is not quite mature yet. There are still many problems during development as well as quite poor flexibility of the new framework. This means that we won’t see many SwiftUI iOS or macOS apps in the near future.&lt;/p&gt;

&lt;p&gt;However, SwiftUI is just brilliant for watchOS. First of all, we usually do not need much functionality on a watch. Furthermore, the level of customization of views in WatchKit is not as high as in UIKit so moving to SwiftUI is not that painful. Finally, even now (November 2019) there are very few apps in watchOS AppStore which means that there’s enough space for you to implement your ideas.&lt;br&gt;
In this article, I’d like to take a quick look at two major features of new watchOS update and create a stand-alone watchOS app using SwiftUI.&lt;/p&gt;
&lt;h2&gt;
  
  
  Application
&lt;/h2&gt;

&lt;p&gt;Apple Watch is a great device that can currently track a lot of things: the distance you walk/run/swim, heart rate, standing hours, even volume level of the environment! But what about tracking something more enjoyable than sport? What about TV series for example? No kidding, it’s sometimes big trouble for me to remember which episode was the last and as soon as I watch series on different platforms I would like to have a third-party app to track this. Moreover, sometimes it’s just vital to remember which episode was last to avoid spoilers! :)&lt;br&gt;
So let’s try to create a SeriesTracker app that will help you to remember at which season and episode you’ve left a particular TV series.&lt;br&gt;
Let’s start with the project configuration. One should create a new Xcode project by choosing File:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IJFB0ExI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.stfalcon.com/uploads/images/3e4b5b4141247a01eb587081add47873.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IJFB0ExI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.stfalcon.com/uploads/images/3e4b5b4141247a01eb587081add47873.png" alt="Pic.1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;New -&amp;gt; Project than switch to &lt;code&gt;watchOS&lt;/code&gt; tab and select &lt;code&gt;Watch App&lt;/code&gt;.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--snjMC0z_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.stfalcon.com/uploads/images/dd9ba39ad6f062f56023642523c74491.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--snjMC0z_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.stfalcon.com/uploads/images/dd9ba39ad6f062f56023642523c74491.png" alt="Pic.2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then it’s important to choose SwiftUI for User interface in project configuration window.&lt;/p&gt;

&lt;p&gt;Now we’ve got some basic project structure. Let’s clean it up a little by dividing all the files into different directories.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--biU81uUH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.stfalcon.com/uploads/images/535de116b56d237470c9417b22952353.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--biU81uUH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.stfalcon.com/uploads/images/535de116b56d237470c9417b22952353.png" alt="Pic.3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We won’t need Notifications, Complication and Resources so far in this article. We may surely use Assets.xcassets to add some pretty application icons or complications though :)&lt;/p&gt;

&lt;p&gt;Let’s start with the acknowledgement of what an app should be able to do. We’ll store and display a list of series and some basic information about them. We’ll also provide the possibility to update information, remove series from the app and add new.&lt;/p&gt;
&lt;h2&gt;
  
  
  Model
&lt;/h2&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{struct Series: Identifiable, Codable, Equatable {
    let id = UUID().uuidString
    let name: String
    var seasonNumber: Double = 1
    var episodeNumber: Double = 1
    var finishedWatching = false
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;First of all, we need to define a model.&lt;/p&gt;

&lt;p&gt;Series structure contains id which is also required by the Identifiable protocol, name of the show, season and episode numbers and a bool value saying whether we’ve already finished watching it. Codable protocol is needed in order to store our shows in json format and Identifiable will soon be explained when it comes to the SwiftUI List structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;final class DataStorage: ObservableObject {
    // MARK: - Singleton
    static let shared = DataStorage()
    // MARK: - Properties
    // Internal
    private let seriesPersistanceIdentifier = "com.stfalcon.series_tracker.series"
    // Data
    @Published var series: [Series]
    // MARK: - (Private) Init
    private init() {
        let storedSeriesData = UserDefaults.standard.data(forKey: seriesPersistanceIdentifier)
        self.series = DataStorage.decodedFromJson(storedSeriesData) ?? exampleSeries ?? []
    }
    // MARK: - Private logic
    func saveAll() {
        self.save(series, forKey: seriesPersistanceIdentifier)
    }
    private func save&amp;lt;T: Codable&amp;gt;(_ data: T, forKey key: String) {
        let rawData = DataStorage.encodedJsonRepresentation(of: data)
        UserDefaults.standard.set(DataStorage.encodedJsonRepresentation(of: rawData),
                                  forKey: key)
    }
    // MARK: - Private static utils
    private static func encodedJsonRepresentation&amp;lt;T: Codable&amp;gt;(of data: T) -&amp;gt; Data? {
        return try? JSONEncoder().encode(data)
    }
    private static func decodedFromJson&amp;lt;T: Codable&amp;gt;(_ data: Data?) -&amp;gt; T? {
        guard let data = data else { return nil }
        return try? JSONDecoder().decode(T.self, from: data)
    }
}

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



&lt;p&gt;Now we should create some kind of persistence manager — DataStorage.&lt;/p&gt;

&lt;p&gt;This class will have a &lt;code&gt;shared&lt;/code&gt; singleton. We’ll keep series in the array and store them in user defaults. Sure usually it’s a bad idea to store big amount of data there. However, it’s just good enough for our example.&lt;/p&gt;

&lt;p&gt;There are two unusual things about the DataStorage — ObservableObject and @Published. ObservableObject is a protocol an object has to conform to make itself available for SwiftUI to track and @Published annotation is a property-wrapper that makes particular property of object to be available for observing. We’ll take look at how it works later in this article.&lt;/p&gt;

&lt;p&gt;Let’s also add a line of code to ExtensionDelegate to save our data to UserDefaults each time our app becomes inactive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func applicationWillResignActive() {
    DataStorage.shared.saveAll()
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Presentation
&lt;/h2&gt;

&lt;p&gt;Now when we’ve done with the model let’s dive deeper in the presentation layer. There we have HostingController and ContentView so far. We’ll create our custom views and make one of them a subview of content view and then navigate to all the others. We will use HostingController as the only controller in the app and make our application something like a view-based.&lt;/p&gt;

&lt;p&gt;Let’s start creating views from the most atomic ones. Our application will not be using any Internet connection so far so we won’t be able to download any pictures for our shows. However, we could still create a placeholder view with the first letter of the show’s title — LetterImageView.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;struct LetterImageView: View {
    // MARK: - Data
    var letter: Character
    // MARK: - View
    var body: some View {
        ZStack {
            Rectangle()
                .size(CGSize(width: 200, height: 200))
                .foregroundColor(.gray)
            GeometryReader { geometry in
                Text(String(self.letter))
                    .font(Font.system(size: geometry.size.height / 2))
                    .bold()
            }
        }
        .clipShape(Circle())
    }
}
struct LetterImageView_Previews: PreviewProvider {
    static var previews: some View {
        LetterImageView(letter: "A")
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Every SwiftUI View is a structure conforming to the View protocol. Unlike UIKit all the views are structures in SwiftUI. That leads to having less problems with inappropriate use of OOP principles and helps us to avoid memory-management mistakes (like retain cycles for instance).&lt;/p&gt;

&lt;p&gt;Only one stored property is needed by our view. It’s a character we want to display as the placeholder. Like every View our LetterImageView must provide computed property &lt;code&gt;body&lt;/code&gt; that contains all the nested views. Most of SwiftUI views have an initialiser that has a closure parameter where we put all the nested views.&lt;/p&gt;

&lt;p&gt;In this particular view we’ve used ZStack which is used to overlay the views: gray rectangle of fixed size and a label. Apart of ZStack, SwiftUI has also HStack and VStack (which are horizontal and vertical stacks). One should pay attention to that we wrap label inside of &lt;code&gt;GeometryReader&lt;/code&gt;. This is made in order to adjust label’s font to the view’s size in case the whole view is scaled. Let’s also mark that we clip the stack to the shape of circle. SwiftUI provides us with a variety of shapes we can clip our views to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Text(self.currentSeries.finishedWatching ? "Finished watching" : "In progress")
                    .font(.subheadline)
                    .foregroundColor(self.currentSeries.finishedWatching ? .gray : .green)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3V_rK7PO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.stfalcon.com/uploads/images/02d04895923ef372deb05838b993511f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3V_rK7PO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.stfalcon.com/uploads/images/02d04895923ef372deb05838b993511f.png" alt="Pic.4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looks quite declarative, doesn’t it? Sure and that is a huge advantage of SwiftUI. Another great thing about this framework is that code is now the only source-of-truth and there’s zero possibility that any configurations are overridden elsewhere as that could happen while using Storyboards.&lt;/p&gt;

&lt;p&gt;We should also pay attention to the LetterImageView_Preview structure. It is used by the preview engine and should provide all the mocked data for the view.&lt;/p&gt;

&lt;p&gt;Now we’ve got a placeholder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// MARK: - Data
@EnvironmentObject var store: DataStorage
var seriesId: String
var seriesIndex: Int {
    store.series.firstIndex(where: { $0.id == seriesId })!
}
var currentSeries: Series {
    store.series[seriesIndex]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let’s create a view that will display all the information about the show — SeriesDetailedView. Here we’ll need a &lt;code&gt;storage&lt;/code&gt; as @EnvironmentObject, &lt;code&gt;seriesId&lt;/code&gt; as stored property. We’ll also need some utilities like computed &lt;code&gt;seriesIndex&lt;/code&gt; and &lt;code&gt;currentSeries&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;@EnvironmentObject as well as @ObservableObject and @State are the object-wrappers provided by SwiftUI. If any of @Published values of these objects are modified, changes are automatically tracked by the framework which invalidated view’s layout and launches UI reload. The main difference between these three annotations is that @EnvironmentObject is usually used for the objects shared for the whole application and provided by special method &lt;code&gt;func environmentObject &amp;lt; B &amp;gt; (_ bindable: B) -&amp;gt; some View where B : ObservableObject&lt;/code&gt; of the View protocol; @ObservableObject is used for objects shared between a small number of views and provided via View’s initialiser; @State is used for the objects that are used in the context of single View and also provided via initialiser.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;body&lt;/code&gt; of the SeriesDetailView is quite straightforward so let’s only focus on some SwiftUI features that were not described yet.&lt;/p&gt;

&lt;p&gt;As soon as all the UI is written in closures we can easily use conditional statements to include/exclude some views as well as make any parameters of UI configuration depend on model. It’s easy to see this in &lt;code&gt;In progress&lt;/code&gt; label configuration.&lt;/p&gt;

&lt;p&gt;Based on whether user has finished watching particular show we display different text on the status label and set the text’s color. Great declarative and quite convenient way to set up the UI.&lt;/p&gt;

&lt;p&gt;Next we are going to create a view for editing current season and episode of the show and also the &lt;code&gt;finishedWatching&lt;/code&gt; property. That means that we have to navigate to the next screen somehow. In SwiftUI there’s a simple way to do that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NavigationLink(destination:
    EditSeriesView(
        seriesId: seriesId
    ).environmentObject(self.store)
) {
    Text("Update")
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here we create a NavigationLinkView and provide the destination view which is EditSeriesView in this case. We pass the seriesId and environment right in function parameter inline. We use trailing closure to specify the way NavigationLink should be drawn. In this case that would be a button &lt;code&gt;Update&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var currentSeriesBinding: Binding&amp;lt;Series&amp;gt; {
        $store.series[seriesIndex]
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Although the interface of EditSeriesView is very simple, there is one major point to described. We’ll create another utility as computed property. Binding is used to reference an object or structure we want to update with the use of UI Controls. One should pay attention to that we use special $-syntax to make Binding from our ObservableObject. Now if we pass this binding to Slider, it will automatically update the particular series in our EnvironmentObject which will surely update the UI and keep it up to date. Simply talking as soon as user triggers the slider, the text of the label is automatically updated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Text("Current season: \(Int(currentSeries.seasonNumber))")
                Slider(value: currentSeriesBinding.seasonNumber,
                       in: minValue...maxValue,
                       step: step)
                    .accentColor(.orange)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here’s how it looks like on a device.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uJQqt_PX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.stfalcon.com/uploads/images/c30ecc9ac321b963cde5deaaf3bd0927.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uJQqt_PX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.stfalcon.com/uploads/images/c30ecc9ac321b963cde5deaaf3bd0927.png" alt="Pic.5"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yQZmfWHX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.stfalcon.com/uploads/images/292eed704974225b4a651dcbe0cfbd00.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yQZmfWHX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.stfalcon.com/uploads/images/292eed704974225b4a651dcbe0cfbd00.png" alt="Pic.6"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it’s time to create a list of all series in the app. SwiftUI List is a powerful analogue of UITableViewController from UIKit. It’s the main element in our SeriesListView. Later we’ll make this view the main view of our app providing it as a subview of the ContentView.&lt;/p&gt;

&lt;p&gt;Let’s dive a bit deeper in the implementation of this view. Here we provide @EnvironmentObject that can be treated as some kind of datasource in this particular case. Moreover, we provide two @State properties. &lt;code&gt;showInProgressOnly&lt;/code&gt; is used to filter away the shows that are currently not watched by user. &lt;code&gt;newSeriesName&lt;/code&gt; will soon be used to store the name of newly added show.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;struct SeriesList: View {
    // MARK: - Data
    @EnvironmentObject var store: DataStorage
    @State var showInProgressOnly = true
    @State var newSeriesName = ""
    // MARK: - View
    var body: some View {
        List {
            Toggle(isOn: $showInProgressOnly) {
                Text("In progress")
            }
            ForEach(store.series) { series in
                if !self.showInProgressOnly || !series.finishedWatching {
                    NavigationLink(
                        destination: SeriesDetailView(seriesId: series.id)
                            .environmentObject(self.store)
                    ) {
                        SeriesRow(
                            seriesId: series.id
                        ).environmentObject(self.store)
                    }
                }
            }
        }
        .navigationBarTitle("My Series")
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Usually we do not need to use ForEach for views in List. Passing datasource and closure with nested views to the list initialiser is good enough if we have dynamic cells only. In this case we have both — static cell with toggle that controls filtering and a number of dynamic cells displaying the shows. It’s important to emphasise that our datasource (the array of Series) is Identifiable as soon as Series is Identifiable itself. That makes List or ForEach able to distinguish between different models and enables them to work with reusable views. If Series wasn’t Identifiable we would have to provide an additional parameter &lt;code&gt;id&lt;/code&gt; and specify #keyPath for the object’s primary key. It’s also nice to see how easily we can filter objects with the use of single conditional&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9yemLO5S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.stfalcon.com/uploads/images/7a7499d1b1a9ef0c42830e720a7b0d44.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9yemLO5S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.stfalcon.com/uploads/images/7a7499d1b1a9ef0c42830e720a7b0d44.png" alt="Pic.7"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;statement (line 26).&lt;/p&gt;

&lt;p&gt;Here’s how our list looks like on device.&lt;/p&gt;

&lt;p&gt;Now we can display and modify our shows let’s give user the ability to remove ones.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4-pLFbb6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.stfalcon.com/uploads/images/ac65198dd4b766d7deb760ca487d30e7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4-pLFbb6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.stfalcon.com/uploads/images/ac65198dd4b766d7deb760ca487d30e7.png" alt="Pic.8"&gt;&lt;/a&gt;&lt;br&gt;
To do this we should only create a method in our view that removes value from datasource based on the IndexSet and specify&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.onDelete(perform: self.delete)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;this function in &lt;code&gt;onDelete&lt;/code&gt; for the ForEach element. Yes, that easy!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private func delete(at offsets: IndexSet) {
    store.series.remove(atOffsets: offsets)
}
TextField("Add new series", text: $newSeriesName, onCommit: {
                self.addNewSeries()
            })
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now let’s create an input for adding new series. We create a method that inserts a new value into the datasource and create a TextField which will modify &lt;code&gt;newSeriesName&lt;/code&gt; @State and call &lt;code&gt;addNewSeries&lt;/code&gt; function on commit.&lt;/p&gt;

&lt;p&gt;Now we can add new TV series to our app.&lt;/p&gt;

&lt;p&gt;Last but not least, most of the SwiftUI views are reusable all over the apple devices (watch, iPhone, Mac) and that is beautiful in case we want to develop a cross-platform application.&lt;/p&gt;

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

&lt;p&gt;Although it’s true that SwiftUI is much, much more than these few things covered in this article, this tiny app is a brilliant example of how simple and straightforward the process of creating the stand-alone watchOS app is now. It truly looks like the beginning of the new era in mobile and wear software development.&lt;/p&gt;

&lt;p&gt;The complete version of SeriesTracker can be found on the repo.&lt;/p&gt;

&lt;p&gt;Originally published in &lt;a href="https://stfalcon.com/en/blog/post/watch-os-6-what-s-new"&gt;Stfalcon.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>watchos6</category>
      <category>swiftui</category>
      <category>appdevelopment</category>
    </item>
    <item>
      <title>Chat-app Creation in Real-Time Mode Using Vue.js, Nuxt.js, Node.js (Express), Socket.IO, Vue-Socket.IO, Vuetify.js Technologies.</title>
      <dc:creator>JullSanders</dc:creator>
      <pubDate>Wed, 12 Feb 2020 12:21:45 +0000</pubDate>
      <link>https://forem.com/jullsanders/chat-app-creation-in-real-time-mode-using-vue-js-nuxt-js-node-js-express-socket-io-vue-socket-io-vuetify-js-technologies-2hkj</link>
      <guid>https://forem.com/jullsanders/chat-app-creation-in-real-time-mode-using-vue-js-nuxt-js-node-js-express-socket-io-vue-socket-io-vuetify-js-technologies-2hkj</guid>
      <description>&lt;p&gt;Hello everybody. Recently I’ve got a desire to master the Socket.IO library and create a chat application, to strengthen theoretical knowledge with practice, so to say.&lt;/p&gt;

&lt;p&gt;I actively use the technological stack which is implemented in the application in my work on commercial projects, but for Socket.IO.&lt;/p&gt;

&lt;p&gt;It’s easy to inject the above-mentioned library into the already working project, but today I am going to speak about creating an app from scratch.&lt;br&gt;
Let’s proceed, I do not like long forewords myself.&lt;/p&gt;

&lt;p&gt;Setting up and installation of the Nuxt.js generic template.&lt;/p&gt;

&lt;p&gt;You need to have Node.js installed, otherwise — install it.&lt;/p&gt;

&lt;p&gt;If your NPM version is under 5.2 — install npx globally, using the rights of admin $sudo npm install -g npx.&lt;/p&gt;

&lt;p&gt;After that create a project with the help of the following command:&lt;br&gt;
$npx create-nuxt-app &lt;/p&gt;

&lt;p&gt;Then a project configuration menu will appear (I used my project details):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Project name — “Nuxt-chat-app”&lt;/li&gt;
&lt;li&gt;Description — “Simple chat with Nuxt.js”&lt;/li&gt;
&lt;li&gt;Author’s name — “Petr Borul”&lt;/li&gt;
&lt;li&gt;Package manager — “NPM”&lt;/li&gt;
&lt;li&gt;UI framework — “Vuetify.js”&lt;/li&gt;
&lt;li&gt;Server Framework — “Express”&lt;/li&gt;
&lt;li&gt;Nuxt.js modules — “PWA”&lt;/li&gt;
&lt;li&gt;Linting tools — “ESLint”&lt;/li&gt;
&lt;li&gt;Test framework — “none”
10.Rendering mode — “Universal”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s install SOCKET.IO: $npm install socket.io — save&lt;br&gt;
I also used a wrapper for SOCKET.IO — Vue.SOCKET.IO.&lt;/p&gt;

&lt;p&gt;In this library, you can call the websocket events and subscribe to them through the Vuex store, but for the hands-on review of the library, it is too implicit. That’s why I implemented the logic at the component level.&lt;/p&gt;

&lt;p&gt;$npm install vue-socket.io --save&lt;/p&gt;

&lt;p&gt;The full information on Nuxt.js folders’ structure you can find &lt;a href="https://ru.nuxtjs.org/guide/directory-structure/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The main points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The folder &lt;em&gt;pages&lt;/em&gt; contains views and routes. The framework reads all .vue files inside the folder and creates a router for the application.&lt;/li&gt;
&lt;li&gt;The folder &lt;em&gt;plugins&lt;/em&gt; contains JavaScript-plugins, which are run before the root application Vue.js creation (here our plugin socket.io will be resided).&lt;/li&gt;
&lt;li&gt;The folder middleware contains the intermediate processing functions (the named ones are created in this folder, and if you want to specify the anonymous ones - you can declare them inside the component).&lt;/li&gt;
&lt;li&gt;The file nuxt.config.js contains Nuxt.js user configuration.&lt;/li&gt;
&lt;li&gt;The folder store contains the files of Vuex container. After index.js file creation in this folder, the container is activated automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, we have dealt with the main notions, let’s proceed to the app development itself. The folder contains the file index.js — we’ll change it a bit and take the server configuration to a separate file &lt;strong&gt;app.js&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const app = require('express')();
const server = require('http').createServer(app);
const io = require('socket.io')(server);
We’ll add server configuration to index.js:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;index.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { app, server } = require('./app');
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then we’ll order Node.js to listen to the server configured:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server.listen(port, () =&amp;gt; {
   consola.ready({
     message: `Server listening on http://${host}:${port}`,
     badge: true
   })
 })
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Further on we create the file socket.client.js and add it to the folder plugins, we indicated the file extension ‘client’, because we need it only on the client-side (Here you can find all the info as to the plugin’s adjustments).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;socket.client.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Vue from 'vue'
import VueSocketIO from 'vue-socket.io'

export default function () {
 Vue.use(new VueSocketIO({
   debug: false,
   connection: '/',
 }))
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we’ll register it in the &lt;em&gt;nuxt.config.js&lt;/em&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plugins: [
   { src: '~/plugins/socket.client.js' }
 ],
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;From this point on you can refer to it in any component, using only the name of the file &lt;em&gt;this.$socket.emit()&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In the app.js file we’ll create two models of working with the data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const users = require('../utils/users')();
const Message = require('../utils/message')();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;message.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Message {
 constructor(name, text, id) {
   this.name = name;
   this.text = text;
   this.id = id;
   this.time = new Date().toString().slice(15, 24);
 }
}

module.exports = () =&amp;gt; {
 return Message
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;users.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Users {
 constructor() {
   this.users = [];
 }

 addUser(user) {
   this.users = [...this.users, user]
 }

 getUser(id) {
   return this.users.find(user =&amp;gt; user.id === id);
 }

 getUsersByRoom(room) {
   return this.users.filter(user =&amp;gt; user.room === room);
 }

 removeUser(id) {
   this.users = this.users.filter(user =&amp;gt; user.id !== id);
 }
}

module.exports = () =&amp;gt; {
 return new Users()
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We have finished with the server at this point and now we’ll proceed to the client side. In the folder &lt;em&gt;store&lt;/em&gt; we’ll create &lt;strong&gt;index.js&lt;/strong&gt; file and add the store&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;index.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const state = () =&amp;gt; ({
 user: {},
 messages: [],
 users: []
})

export const mutations = {
 setUser(state, user) {
   state.user = user;
 },
 newMessage(state, msg) {
   state.messages = [...state.messages, msg];
 },
 updateUsers(state, users) {
   state.users = users;
 },
 clearData(state) {
   state.user = {};
   state.messages = [];
   state.users = [];
 },
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Further on we’ll add a layout to the file index.js in folder layouts (I use UI library Vuetify.js because of the Material Design, which I like very much).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;index.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;template&amp;gt;
 &amp;lt;v-layout column justify-center align-center&amp;gt;
   &amp;lt;v-flex xs12 sm8&amp;gt;
     &amp;lt;v-card min-width="370"&amp;gt;
       &amp;lt;v-snackbar v-model="snackbar" :timeout="3000" top&amp;gt;
         {{ message }}
         &amp;lt;v-btn dark text @click="snackbar = false"&amp;gt;Close&amp;lt;/v-btn&amp;gt;
       &amp;lt;/v-snackbar&amp;gt;

       &amp;lt;v-card-title&amp;gt;
         &amp;lt;h1&amp;gt;Login&amp;lt;/h1&amp;gt;
       &amp;lt;/v-card-title&amp;gt;
       &amp;lt;v-card-text&amp;gt;
         &amp;lt;v-form ref="form" v-model="valid" lazy-validation @submit.prevent="submit"&amp;gt;
           &amp;lt;v-text-field
             v-model="name"
             :counter="16"
             :rules="nameRules"
             label="Name"
             required
           &amp;gt;&amp;lt;/v-text-field&amp;gt;
           &amp;lt;v-text-field
             v-model="room"
             :rules="roomRules"
             label="Enter the room"
             required
           &amp;gt;&amp;lt;/v-text-field&amp;gt;
           &amp;lt;v-btn :disabled="!valid" color="primary" class="mr-4" type="submit"&amp;gt;Submit&amp;lt;/v-btn&amp;gt;
         &amp;lt;/v-form&amp;gt;
       &amp;lt;/v-card-text&amp;gt;
     &amp;lt;/v-card&amp;gt;
   &amp;lt;/v-flex&amp;gt;
 &amp;lt;/v-layout&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;
import { mapMutations } from "vuex";

export default {
 name: "index",
 layout: "login",
 head: {
   title: "Nuxt-chat"
 },
 data: () =&amp;gt; ({
   valid: true,
   name: "",
   message: "",
   id: null,
   nameRules: [
     v =&amp;gt; !!v || "Name is required",
     v =&amp;gt; (v &amp;amp;&amp;amp; v.length &amp;lt;= 16) || "Name must be less than 16 characters"
   ],
   room: "",
   roomRules: [v =&amp;gt; !!v || "Enter the room"],
   snackbar: false
 }),
 mounted() {
   const { message } = this.$route.query;
   if (message === "noUser") {
     this.message = "Enter your name and room";
   } else if (message === "leftChat") {
     this.message = "You leaved chat";
   }
   this.snackbar = !!this.message;
 },

 methods: {
   ...mapMutations(["setUser"]),
   submit() {
     if (this.$refs.form.validate()) {
       const user = {
         name: this.name,
         room: this.room,
         id: 0
       };
       this.$socket.emit("createUser", user, data =&amp;gt; {
         user.id = data.id;
         this.setUser(user);
         this.$router.push("/chat");
       });
     }
   }
 }
};
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When the &lt;em&gt;submit ()&lt;/em&gt; method is called, the form is validated, and in case of success, we send the event to the server &lt;em&gt;this.$socket.emit()&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We send a String with the name of the event to the server, and a callback function, after the fulfillment of which we get an ID and assign it to the user’s object, then we write it down to the state and send it to the chat page.&lt;/p&gt;

&lt;p&gt;Let’s describe the event processing on the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;io.on('connection', socket =&amp;gt; {
 socket.on("createUser", (user, cb) =&amp;gt; {
   users.addUser({
     ...user,
     id: socket.id
   })
   cb({ id: socket.id })
 });
})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;1.The event “connection” is called when the user gets the connection with the server.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Then we subscribe to the event received from the client with the help of socket.on().&lt;/li&gt;
&lt;li&gt;This function accepts the String and the callback function.&lt;/li&gt;
&lt;li&gt;We add a new user to the users’ list and assign ID the corresponding ID socket for connection.&lt;/li&gt;
&lt;li&gt;We pass the ID on to the client’s side.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now we’ll create the layout of the default.vue file in the layouts folder, it’s set by default for all the components in the folder pages if the layout is not indicated (here you’ll find the detailed information).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;default.vue&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;template&amp;gt;
 &amp;lt;v-app&amp;gt;
   &amp;lt;v-navigation-drawer app v-model="drawer" mobile-break-point="650"&amp;gt;
     &amp;lt;v-list subheader&amp;gt;
       &amp;lt;v-subheader&amp;gt;Users in room&amp;lt;/v-subheader&amp;gt;

       &amp;lt;v-list-item v-for="(u, index) in users" :key="`user-${index}`" @click.prevent&amp;gt;
         &amp;lt;v-list-item-content&amp;gt;
           &amp;lt;v-list-item-title v-text="u.name"&amp;gt;&amp;lt;/v-list-item-title&amp;gt;
         &amp;lt;/v-list-item-content&amp;gt;

         &amp;lt;v-list-item-icon&amp;gt;
           &amp;lt;v-icon :color="u.id === user.id ? 'primary' : 'grey'"&amp;gt;mdi-account-circle-outline&amp;lt;/v-icon&amp;gt;
         &amp;lt;/v-list-item-icon&amp;gt;
       &amp;lt;/v-list-item&amp;gt;
     &amp;lt;/v-list&amp;gt;
   &amp;lt;/v-navigation-drawer&amp;gt;

   &amp;lt;v-app-bar app&amp;gt;
     &amp;lt;v-app-bar-nav-icon @click="drawer = !drawer"&amp;gt;&amp;lt;/v-app-bar-nav-icon&amp;gt;
     &amp;lt;v-toolbar-title&amp;gt;
       Room
       &amp;lt;v-chip color="grey"&amp;gt;{{ user.room }}&amp;lt;/v-chip&amp;gt;
     &amp;lt;/v-toolbar-title&amp;gt;
     &amp;lt;v-spacer&amp;gt;&amp;lt;/v-spacer&amp;gt;
     &amp;lt;v-btn icon class="mx-1" @click="exit"&amp;gt;
       &amp;lt;v-icon&amp;gt;mdi-exit-to-app&amp;lt;/v-icon&amp;gt;
     &amp;lt;/v-btn&amp;gt;
   &amp;lt;/v-app-bar&amp;gt;

   &amp;lt;v-content&amp;gt;
     &amp;lt;v-container fluid style="height: 100%"&amp;gt;
       &amp;lt;nuxt /&amp;gt;
     &amp;lt;/v-container&amp;gt;
   &amp;lt;/v-content&amp;gt;
 &amp;lt;/v-app&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;
import { mapState, mapMutations } from "vuex";

export default {
 data: () =&amp;gt; ({
   drawer: true
 }),
 sockets: {
   updateUsers(users) {
     this.updateUsers(users);
   },
   newMessage(msg) {
     this.newMessage(msg);
   },
 },
 computed: {
   ...mapState(["user", "users"])
 },
 middleware: "auth",
 methods: {
   ...mapMutations(["clearData", "updateUsers", "newMessage"]),
   exit() {
     this.$socket.emit("userLeft", () =&amp;gt; {
       this.$router.push("/?message=leftChat");
       this.clearData();
     });
   }
 },
 created() {
   this.$socket.emit("joinRoom", this.user)
 }
};
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The tag   is responsible for views on various routes.&lt;/p&gt;

&lt;p&gt;The object &lt;em&gt;sockets&lt;/em&gt; is responsible for processing of the events, which are called on the server side.&lt;/p&gt;

&lt;p&gt;Let’s add subscription for 2 events &lt;em&gt;“updateUsers”&lt;/em&gt; and &lt;em&gt;“newMessage”&lt;/em&gt;. Then we’ll add the method &lt;em&gt;exit()&lt;/em&gt;, which will be called with an exit button click and in which we will send the event &lt;em&gt;“leftChat”&lt;/em&gt; to the server. Then the user will be redirected to the registration form from the query on the route for message display in the snackbar.&lt;/p&gt;

&lt;p&gt;Let’s process this event on the server:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;app.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;socket.on('leftChat', (cb) =&amp;gt; {
   const id = socket.id;
   const user = users.getUser(id);
   if (user) {
     users.removeUser(id);
     socket.leave(user.room);
     io.to(user.room).emit('updateUsers', users.getUsersByRoom(user.room));
     io.to(user.room).emit('newMessage', new Message('admin', `User ${user.name} left chat`))
   }
   cb()
 });
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we’ll create a file &lt;strong&gt;auth.js&lt;/strong&gt; in the &lt;em&gt;middleware&lt;/em&gt; folder and add an ntermediate processing function to the component, so that only an authorized user could get on the chat page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;auth.js&lt;/strong&gt; (open and close on a click on the file name):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default function({ store, redirect }) {
 if(!Object.keys(store.state.user).length) {
   redirect('/?message=noUser')
 }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Also, with the initialization of the component we send the event &lt;em&gt;“joinRoom”&lt;/em&gt; to the server and send the user data as a payload into the feedback function.&lt;/p&gt;

&lt;p&gt;Let’s process this event on the server:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;app.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt; socket.on("joinRoom", user =&amp;gt; {
   socket.join(user.room);
   io.to(user.room).emit('updateUsers', users.getUsersByRoom(user.room));
   socket.emit('newMessage', new Message('admin', `Hello, ${user.name}`));
   socket.broadcast
     .to(user.room)
     .emit('newMessage', new Message('admin', `User ${user.name} connected to chat`));
 });
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We add the user to the room, which he indicated during the authorization;&lt;br&gt;
then we call the event &lt;em&gt;“updateUsers”&lt;/em&gt; for all the users of the room;&lt;br&gt;
and call the event &lt;em&gt;“newMessage”&lt;/em&gt; only for the user, who has called the event &lt;em&gt;“joinRoom”&lt;/em&gt;;&lt;br&gt;
We call the event &lt;em&gt;“newMessage”&lt;/em&gt; for all the users, except for the current user ( notify the other users about the new user, who joined).&lt;br&gt;
Further on we’ll add the chat layout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;chat.vue&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;template&amp;gt;
 &amp;lt;div class="chat-wrapper"&amp;gt;
   &amp;lt;div class="chat" ref="chat"&amp;gt;
     &amp;lt;Message
       v-for="(message,index) in messages"
       :key="`message-${index}`"
       :name="message.name"
       :text="message.text"
       :time="message.time"
       :owner="message.id === user.id"
     /&amp;gt;
   &amp;lt;/div&amp;gt;
   &amp;lt;div class="chat__form"&amp;gt;
     &amp;lt;ChatForm /&amp;gt;
   &amp;lt;/div&amp;gt;
 &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;
import { mapState, mapMutations } from "vuex";
import Message from "@/components/message";
import ChatForm from "@/components/ChatForm";

export default {
 components: {
   Message,
   ChatForm
 },
 head() {
   return {
     title: `Room ${this.user.room}`
   };
 },
 methods: {
   ...mapMutations(["newMessage"])
 },
 computed: {
   ...mapState(["user", "messages"])
 },
 watch: {
   messages() {
     setTimeout(() =&amp;gt; {
       if (this.$refs.chat) {
         this.$refs.chat.scrollTop = this.$refs.chat.scrollHeight;
       }
     }, 0);
   }
 }
};
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I have omitted the section with styles, for you to concentrate on the logic. The component, which is responsible for message rendering is&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Message.vue&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;template&amp;gt;
 &amp;lt;div&amp;gt;
   &amp;lt;div v-if="name === 'admin'" class="system"&amp;gt;
     &amp;lt;p class="text-center font-italic"&amp;gt;{{ text }}&amp;lt;/p&amp;gt;
   &amp;lt;/div&amp;gt;
   &amp;lt;div v-else class="msg-wrapper"&amp;gt;
     &amp;lt;div class="msg" :class="{owner}"&amp;gt;
       &amp;lt;div class="msg__information"&amp;gt;
         &amp;lt;span class="msg__name"&amp;gt;{{ name }}&amp;lt;/span&amp;gt;
         &amp;lt;span class="msg__date"&amp;gt;{{ time }}&amp;lt;/span&amp;gt;
       &amp;lt;/div&amp;gt;
       &amp;lt;p class="msg__text"&amp;gt;{{ text }}&amp;lt;/p&amp;gt;
     &amp;lt;/div&amp;gt;
   &amp;lt;/div&amp;gt;
 &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;
export default {
 props: {
   name: String,
   text: String,
   time: String,
   owner: Boolean
 }
};
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The styles are adjusted in the same way as the previous component.&lt;/p&gt;

&lt;p&gt;The component for message realization and sending is&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ChatForm.vue&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;template&amp;gt;
 &amp;lt;v-text-field
   ref="msg"
   label="Message..."
   outlined
   v-model="text"
   @click:append="send"
   @keydown.enter="send"
   append-icon="mdi-send-circle-outline"
 /&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;
import { mapState } from "vuex";

export default {
 data: () =&amp;gt; ({
   text: "",
   roomRules: [v =&amp;gt; !!v || "Enter the room"]
 }),
 computed: {
   ...mapState(["user"])
 },
 methods: {
   send() {
     if (this.text.length) {
       this.$socket.emit(
         "createMessage",
         {
           text: this.text,
           id: this.user.id
         },
         data =&amp;gt; {
           this.text = "";
         }
       );
     }
   }
 }
};
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When a form is verified - we send an event &lt;em&gt;“createMessage”&lt;/em&gt; to the server, send the message text and the user ID, after the feedback function, we clear the field.&lt;/p&gt;

&lt;p&gt;Now we’ll process this event on the server:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;app.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;socket.on('createMessage', (data, cb) =&amp;gt; {
   const user = users.getUser(data.id);
   if (user) {
     io.to(user.room).emit('newMessage', new Message(user.name,     data.text, data.id))
   }
   cb()
 });
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We’ll add the subscription in case the connection fails and it will be possible to add the reconnect possibility later on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;app.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;socket.on('disconnect', () =&amp;gt; {
   const id = socket.id;
   const user = users.getUser(id);
   if (user) {
     users.removeUser(id);
     socket.leave(user.room);
     io.to(user.room).emit('updateUsers', users.getUsersByRoom(user.room));
     io.to(user.room).emit('newMessage', new Message('admin', `User ${user.name} left chat`))
   }
 });
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;By now it’s the final part of the app. You can launch the local server with the help of the command:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;$npm run dev&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nuxt-chat-app.herokuapp.com/?message=noUser"&gt;Preview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/psborul/nuxt-chat-app"&gt;Github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see the Socket.IO library is very simple and easy to use. After the development had been finished I had a desire to deploy the app and share the demo version of it with you. I spent some time on the search of the suitable free service, which supports WebSockets. Finally, I chose Heroku. Nuxt.js manuals have a detailed guide about how to deploy an app onto this service.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nuxt-chat-app.herokuapp.com/?message=noUser"&gt;Demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for your attention.&lt;/p&gt;

&lt;p&gt;See you next time!&lt;/p&gt;

&lt;p&gt;Originally published in &lt;a href="https://stfalcon.com/en/blog/post/chat-app-creation-vue.js-nuxt.js-node.js-socket.io-vue-socket.io-vuetify.js-technolog"&gt;Stfalcon.com&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>vue</category>
      <category>node</category>
      <category>startup</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
