<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Monstarlab's Engineering Blog</title>
        <link>https://engineering.monstar-lab.com</link>
        <description>We love engaging with the tech community, and we are big consumers of the awesome work that people share online. Here we are going give back a little and share our tech knowledge about all things we do at Monstarlab.</description>
        <lastBuildDate>Tue, 21 Apr 2026 07:09:49 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>Feed for Monstarlab Blog</generator>
        <image>
            <title>Monstarlab's Engineering Blog</title>
            <url>https://engineering.monstar-lab.com/assets/img/logo/ml-logo-yellow.webp</url>
            <link>https://engineering.monstar-lab.com</link>
        </image>
        <copyright>All rights reserved 2026, Monstarlab</copyright>
        <item>
            <title><![CDATA[Serverpod for a Weekend - The Good, The Bad, and The Surprising]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2026/02/03/Serverpod-for-a-Weekend-The-Good-The-Bad-and-The-Surprising</link>
            <guid>https://engineering.monstar-lab.com/en/post/2026/02/03/Serverpod-for-a-Weekend-The-Good-The-Bad-and-The-Surprising</guid>
            <pubDate>Tue, 03 Feb 2026 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>I've been building Flutter apps professionally for years now, and I've seen the backend landscape evolve from Firebase to Supabase to custom Node.js APIs. Each solution came with trade-offs: Firebase locked you in, Supabase was great until you needed custom logic, and maintaining a separate Node.js backend meant context-switching between TypeScript and Dart.</p>
<p>To be honest, I wasn't actively looking for a new backend solution. But Serverpod kept showing up—in my feeds, in Flutter newsletters, in conversations with other developers. The buzz was real.</p>
<p>So I blocked out a weekend to try it. No expectations, no pressure. Just a genuine attempt to see if the hype had any substance. Here's what I found.</p>
<hr>
<h2>Setup: The First Surprise</h2>
<p>I expected the usual setup friction: Docker configs, environment variables, database connection strings, authentication middleware. Sure, I could ask Claude or Cursor to help with boilerplate, but even with AI agents speeding things up, getting a backend stack running still means an hour of setup before writing actual business logic.</p>
<p>Instead:</p>
<pre><code class="hljs language-bash">$ serverpod create weekend_experiment
Creating project...
✓ Server setup complete (packages/weekend_experiment_server)
✓ Client library generated (packages/weekend_experiment_client)
✓ Flutter app created (weekend_experiment_flutter)
✓ Database migrations ready
✓ Docker compose configured
</code></pre>
<p>The project structure was immediately familiar to any Flutter developer:</p>
<pre><code class="hljs language-bash">weekend_experiment/
├── weekend_experiment_server/     <span class="hljs-comment"># Backend code</span>
├── weekend_experiment_client/     <span class="hljs-comment"># Auto-generated API client</span>
├── weekend_experiment_flutter/    <span class="hljs-comment"># Flutter app</span>
└── docker-compose.yaml            <span class="hljs-comment"># PostgreSQL + Redis ready</span>
</code></pre>
<p>I ran <strong>docker compose up</strong> in the server directory. PostgreSQL and Redis spun up. Then <strong>dart run bin/main.dart --apply-migrations</strong> to initialize the database.</p>
<figure>
  <img src="/assets/img/articles/2026-02-03-Serverpod-for-a-Weekend-The-Good-The-Bad-and-The-Surprising/serverpod-architecture.webp">
  <figcaption>Figure 1: Serverpod's unified architecture - one codebase, full type safety</figcaption>
</figure>
<hr>
<h2>The Good: What Actually Impressed Me</h2>
<h3>1. The ORM That Doesn't Fight You</h3>
<p>I've written enough SQL to know what good database tooling feels like. I've also suffered through ORMs that generate horrific queries and make simple things hard.</p>
<p>I created a model file to test Serverpod's ORM:</p>
<pre><code class="hljs language-yaml"><span class="hljs-comment"># user.spy.yaml</span>
<span class="hljs-attr">class:</span> <span class="hljs-string">User</span>
<span class="hljs-attr">table:</span> <span class="hljs-string">user</span>
<span class="hljs-attr">fields:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">String</span>
  <span class="hljs-attr">email:</span> <span class="hljs-string">String</span>
  <span class="hljs-attr">age:</span> <span class="hljs-string">int?</span>
  <span class="hljs-attr">premium:</span> <span class="hljs-string">bool</span>
  <span class="hljs-attr">createdAt:</span> <span class="hljs-string">DateTime</span>

<span class="hljs-attr">indexes:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">fields:</span> <span class="hljs-string">email</span>
    <span class="hljs-attr">unique:</span> <span class="hljs-literal">true</span>
</code></pre>
<p>Ran <strong>serverpod generate</strong>. The framework generated:</p>
<ul>
<li>A Dart class with all fields</li>
<li>JSON serialization/deserialization</li>
<li>Database access methods</li>
<li>Client-side models</li>
<li>Migration files</li>
</ul>
<p>Here's where it got interesting. The query API felt <em>natural</em>:</p>
<pre><code class="hljs language-dart"><span class="hljs-comment">// Find all premium users over 25, ordered by creation date</span>
<span class="hljs-keyword">final</span> users = <span class="hljs-keyword">await</span> User.db.find(
  session,
  where: (t) => t.premium.equals(<span class="hljs-keyword">true</span>) &#x26; t.age.greaterThan(<span class="hljs-number">25</span>),
  orderBy: (t) => t.createdAt.desc(),
  limit: <span class="hljs-number">20</span>,
);

<span class="hljs-comment">// Complex join with relations</span>
<span class="hljs-keyword">final</span> postsWithAuthors = <span class="hljs-keyword">await</span> Post.db.find(
  session,
  where: (t) => t.publishedAt.isNotNull(),
  include: (t) => [t.author, t.comments],
);
</code></pre>
<p><strong>Type-safe. No string column names. No SQL injection possible. IntelliSense shows available fields.</strong></p>
<p>I tested it with a deliberately broken query:</p>
<pre><code class="hljs language-dart">where: (t) => t.nonExistentField.equals(<span class="hljs-keyword">true</span>)  <span class="hljs-comment">// Compile error!</span>
</code></pre>
<p>The Dart analyzer caught it immediately. No runtime surprises. No debugging production issues caused by typos.</p>
<figure>
  <img src="/assets/img/articles/2026-02-03-Serverpod-for-a-Weekend-The-Good-The-Bad-and-The-Surprising/orm-query-flow.webp">
  <figcaption>Figure 2: Type-safe queries from Dart expressions to optimized PostgreSQL</figcaption>
</figure>
<h3>2. Real-Time That Actually Works</h3>
<p>Most real-time implementations involve setting up WebSocket servers, handling connections/disconnections, managing subscriptions, dealing with reconnection logic, and synchronizing state.</p>
<p>I wanted to test Serverpod's streaming capabilities with a simple counter.</p>
<p><strong>Server endpoint:</strong></p>
<pre><code class="hljs language-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CounterEndpoint</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Endpoint</span> </span>{
  Stream watchCounter(Session session) <span class="hljs-keyword">async</span>* {
    <span class="hljs-keyword">for</span> (<span class="hljs-built_in">int</span> i = <span class="hljs-number">0</span>; i &#x3C; <span class="hljs-number">100</span>; i++) {
      <span class="hljs-keyword">await</span> Future.delayed(<span class="hljs-built_in">Duration</span>(seconds: <span class="hljs-number">1</span>));
      <span class="hljs-keyword">yield</span> i;
    }
  }
}
</code></pre>
<p><strong>Client usage:</strong></p>
<pre><code class="hljs language-dart">client.counter.watchCounter().listen((count) {
  <span class="hljs-built_in">print</span>(<span class="hljs-string">'Counter: <span class="hljs-subst">$count</span>'</span>);
});
</code></pre>
<p>That's it. Serverpod handles WebSocket connection management, automatic reconnection on network loss, backpressure when the client is slow, and clean stream disposal.</p>
<p>For database-driven real-time updates:</p>
<pre><code class="hljs language-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ChatEndpoint</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Endpoint</span> </span>{
  Stream watchRoom(Session session, <span class="hljs-built_in">int</span> roomId) <span class="hljs-keyword">async</span>* {
    <span class="hljs-keyword">await</span> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> message <span class="hljs-keyword">in</span> Message.db.watch(
      session,
      where: (t) => t.roomId.equals(roomId),
    )) {
      <span class="hljs-keyword">yield</span> message;
    }
  }
}
</code></pre>
<p>Any database change to messages in that room automatically streams to all connected clients. No manual pub/sub setup. No Redis channels. No message queues.</p>
<h3>3. Code Generation That Makes Sense</h3>
<p>Serverpod's generation is straightforward: models defined in YAML, code generated from your server structure. The output is readable Dart—no magic, no abstractions hiding complexity. When something's wrong, you can actually see what's happening.</p>
<pre><code class="hljs language-bash">$ serverpod generate
Analyzing server...
✓ Found 5 models
✓ Found 3 endpoints
Generating...
✓ Client library (packages/weekend_experiment_client/)
✓ Protocol models
✓ Database migrations

Time: ~5 seconds
</code></pre>
<h3>4. Authentication Without the Tears</h3>
<p>Authentication setup has gotten easier over the years—plenty of libraries handle OAuth flows. But integrating them still means configuration, testing callbacks, and making sure everything works across environments.</p>
<p>Serverpod includes <strong>serverpod_auth</strong> module with support for Google, Apple, and Email authentication.</p>
<p><strong>Server setup:</strong></p>
<pre><code class="hljs language-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:serverpod_auth/server.dart'</span>;

<span class="hljs-comment">// Authentication is configured through the serverpod_auth package</span>
</code></pre>
<p><strong>Client setup:</strong></p>
<pre><code class="hljs language-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:serverpod_auth/client.dart'</span>;

<span class="hljs-comment">// Sign in with Google</span>
<span class="hljs-keyword">final</span> result = <span class="hljs-keyword">await</span> client.auth.signInWithGoogle();
<span class="hljs-keyword">if</span> (result.success) {
  <span class="hljs-built_in">print</span>(<span class="hljs-string">'Welcome <span class="hljs-subst">${result.userInfo.userName}</span>'</span>);
}
</code></pre>
<p>The session automatically includes authenticated user:</p>
<pre><code class="hljs language-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProfileEndpoint</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Endpoint</span> </span>{
  Future getMyProfile(Session session) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> userId = session.userId;
    <span class="hljs-keyword">if</span> (userId == <span class="hljs-keyword">null</span>) <span class="hljs-keyword">throw</span> Exception(<span class="hljs-string">'Not authenticated'</span>);

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> Profile.db.findById(session, userId);
  }
}
</code></pre>
<p>No JWT parsing. No middleware chains. No authorization header management.</p>
<p><em>(Pro tip: If you add Google Sign-In to your iOS app, don't forget Apple Sign-In too, or Apple will happily reject your app.)</em></p>
<figure>
  <img src="/assets/img/articles/2026-02-03-Serverpod-for-a-Weekend-The-Good-The-Bad-and-The-Surprising/authentication-flow.webp">
  <figcaption>Figure 3: Built-in authentication with Google, Apple, and Email</figcaption>
</figure>
<h3>5. The Development Experience</h3>
<p>This is subtle but important: <strong>everything is Dart</strong>.</p>
<ul>
<li>Server code? Dart.</li>
<li>Client code? Dart.</li>
<li>Database models? Dart (via YAML, but feels like Dart).</li>
<li>Tests? Dart.</li>
<li>Tooling? Dart CLI.</li>
</ul>
<p>Writing both client and server in Dart eliminates language context switching. The tooling, patterns, and conventions are consistent across the stack. You can move between frontend and backend work without the cognitive overhead of switching ecosystems.</p>
<hr>
<h2>The Bad: Where It Falls Short</h2>
<p>Here's where things weren't perfect.</p>
<h3>1. Smaller Package Ecosystem</h3>
<p>The third-party package ecosystem is limited compared to Express or Django. While you'll find some community packages (like Stripe integrations), you won't have the same breadth of options. Need a specific CMS integration? Elasticsearch connector? Twilio wrapper? You'll likely be building it yourself or adapting existing Dart packages.</p>
<p>AI coding assistants can help with general Dart patterns, but their Serverpod-specific knowledge is limited. They're useful for standard CRUD operations and basic server patterns, but you'll be reading documentation and source code more than you might with mainstream frameworks.</p>
<p>If your project heavily depends on integrating with specialized services or requires extensive third-party tooling, account for additional implementation time.</p>
<h3>2. The Learning Curve for Complex Features</h3>
<p>The basics are smooth. Creating endpoints, defining models, simple queries - all intuitive.</p>
<p>Advanced features? The documentation assumes you understand the underlying concepts.</p>
<p>Complex authorization patterns, advanced transaction handling, and intricate database relationships require piecing together solutions from Github issue and AI.</p>
<h3>3. Migration Complexity</h3>
<p>Database migrations work well for simple changes—adding nullable fields, creating tables. Complex changes require manual intervention:</p>
<ul>
<li>Data transformations</li>
<li>Renaming columns while preserving data</li>
<li>Splitting tables</li>
<li>Complex constraint changes</li>
</ul>
<p>The migration system doesn't automatically handle these scenarios. You can manually edit the generated SQL migrations, but this reduces some of the type-safe ORM benefits and requires careful testing.</p>
<h3>4. Production Documentation Gaps</h3>
<p>Getting started locally? Perfect.</p>
<p>Deploying to production? The docs cover Docker basics and provide a health check endpoint, but you'll need to figure out the production DevOps details yourself:</p>
<ul>
<li>Kubernetes-specific configurations (liveness/readiness probes, HPA)</li>
<li>Graceful shutdown for zero-downtime deployments</li>
<li>Database backup and disaster recovery strategies</li>
<li>Log aggregation and centralized monitoring</li>
<li>Production-grade security hardening</li>
</ul>
<p>There's a community <a href="https://pub.dev/packages/serverpod_vps">serverpod_vps</a> package that helps, and GitHub discussions fill some gaps, but comprehensive production operations guides would be valuable.</p>
<h3>5. Performance Data</h3>
<p><a href="https://serverpod.dev">User testimonials</a> mention Serverpod handling 100,000+ daily requests in production, and there's a community benchmark comparing it to other frameworks. But comprehensive performance documentation is sparse.</p>
<ul>
<li>Latency distribution under various loads</li>
<li>Horizontal scaling patterns</li>
<li>Memory footprint under load</li>
<li>Database connection pooling best practices</li>
<li>Caching strategy guidelines</li>
</ul>
<p>This makes capacity planning for larger applications more uncertain.</p>
<hr>
<h2>The Surprising: What I Didn't Expect</h2>
<h3>1. The File Upload System Is Actually Good</h3>
<p>Most file upload implementations are painful. Serverpod surprised me here.</p>
<pre><code class="hljs language-dart"><span class="hljs-comment">// Server endpoint</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FileEndpoint</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Endpoint</span> </span>{
  Future getUploadDescription(Session session, <span class="hljs-built_in">String</span> fileName) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> session.storage.createDirectFileUploadDescription(
      storageId: <span class="hljs-string">'public'</span>,
      path: fileName,
    );
  }
}

<span class="hljs-comment">// Client uploads directly to cloud storage</span>
<span class="hljs-keyword">final</span> uploadDescription = <span class="hljs-keyword">await</span> client.file.getUploadDescription(<span class="hljs-string">'photo.jpg'</span>);
<span class="hljs-keyword">await</span> uploader.upload(uploadDescription, fileBytes);
</code></pre>
<p>This generates presigned URLs for direct cloud storage uploads. No file data passing through your server. Supports S3 and Google Cloud Storage.</p>
<p><strong>I didn't expect this level of sophistication in a young framework.</strong></p>
<figure>
  <img src="/assets/img/articles/2026-02-03-Serverpod-for-a-Weekend-The-Good-The-Bad-and-The-Surprising/file-upload-flow.webp">
  <figcaption>Figure 4: Efficient file uploads with presigned URLs</figcaption>
</figure>
<h3>2. The Caching Layer Is Built-In</h3>
<p>Redis integration isn't bolted on—it's part of the core API:</p>
<pre><code class="hljs language-dart"><span class="hljs-comment">// Cache user profile</span>
<span class="hljs-keyword">await</span> session.caches.redis.put(
  <span class="hljs-string">'profile:<span class="hljs-subst">$userId</span>'</span>,
  profile,
  lifetime: <span class="hljs-built_in">Duration</span>(minutes: <span class="hljs-number">5</span>),
);

<span class="hljs-comment">// Retrieve from cache</span>
<span class="hljs-keyword">final</span> cached = <span class="hljs-keyword">await</span> session.caches.redis.<span class="hljs-keyword">get</span>(<span class="hljs-string">'profile:<span class="hljs-subst">$userId</span>'</span>);
</code></pre>
<p>Type-safe caching with automatic serialization. The cache is session-scoped, available everywhere without manual dependency injection.</p>
<h3>3. Future Calls for Async Background Jobs</h3>
<p>I needed to send an email after user registration without blocking the response:</p>
<pre><code class="hljs language-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserEndpoint</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Endpoint</span> </span>{
  Future register(Session session, User user) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> created = <span class="hljs-keyword">await</span> User.db.insertRow(session, user);

    <span class="hljs-comment">// Fire and forget - executes in background</span>
    <span class="hljs-keyword">await</span> session.messages.sendFutureCall(
      <span class="hljs-string">'email'</span>,
      <span class="hljs-string">'sendWelcome'</span>,
      {<span class="hljs-string">'userId'</span>: created.id},
      delay: <span class="hljs-built_in">Duration</span>(seconds: <span class="hljs-number">10</span>),
    );

    <span class="hljs-keyword">return</span> created;  <span class="hljs-comment">// Response sent immediately</span>
  }
}
</code></pre>
<p>These "future calls" are persisted to PostgreSQL, survive server restarts, and provide exactly-once execution semantics. No AWS SQS, No RabbitMQ.</p>
<p><strong>This replaced what would normally require a separate message queue infrastructure.</strong></p>
<hr>
<h2>The Verdict: Would I Use It Again?</h2>
<p>After a weekend of exploration, here's my honest assessment:</p>
<h3>Use Serverpod When:</h3>
<ul>
<li>Building Flutter-first applications where the entire team works in Dart</li>
<li>Type safety across the full stack is a priority</li>
<li>Real-time features are core to the product (chat, collaboration, live updates)</li>
<li>Development velocity matters more than ecosystem maturity</li>
<li>The team prefers staying in a single language ecosystem</li>
</ul>
<h3>Consider Alternatives When:</h3>
<ul>
<li>Third-party integrations are extensive and specialized</li>
<li>The team spans multiple languages and stacks (Python ML services, Go microservices, etc.)</li>
<li>Production deployment requires comprehensive operational documentation</li>
<li>Integration with legacy systems or proprietary APIs is significant</li>
<li>The project needs proven scalability patterns at massive scale</li>
</ul>
<h2>Conclusion</h2>
<p>Serverpod is legitimately impressive for what it is: a modern, full-stack Dart framework that eliminates significant boilerplate while maintaining type safety from client to database. The code generation is clean, the ORM is practical, and real-time features work without managing WebSocket complexity.</p>
<p>The ecosystem is young. Documentation has gaps. You'll write more integration code than with established frameworks. But for Flutter teams building real-time applications, the developer experience is genuinely productive once you're past the initial learning curve.</p>
<p><strong>Would I recommend it to my teams?</strong><br>
Yes, with clear expectations about ecosystem maturity and integration limitations.</p>
<p><strong>Would I choose it over Node.js + Express?</strong><br>
If the team knows Flutter? Absolutely. If they don't? Probably not.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What I Thought Was People Management Wasn't People Management at All]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2025/12/15/PeopleManagementTrainingNotice</link>
            <guid>https://engineering.monstar-lab.com/en/post/2025/12/15/PeopleManagementTrainingNotice</guid>
            <pubDate>Mon, 15 Dec 2025 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>This article is the 15th entry in the <a href="https://qiita.com/advent-calendar/2025/d-plus">D-Plus🐬 Development Productivity Community Advent Calendar 2025</a>.
Day 14's article is... wait, nobody's there???</p>
<p>I had the opportunity to speak at D-Plus in Osaka back in May with a presentation called <a href="https://speakerdeck.com/kuchitama/task-management-of-engineering-manager">Task Management for Engineering Managers</a>. Although I've only attended once, it was such a warm and welcoming community that I'd love to participate again when I can balance it with my household and parenting responsibilities.</p>
<h2>Introduction</h2>
<p>Recently, I attended a training session for middle managers at Monstarlab.
During the training, I had the chance to reflect on my people management experience, and I had a shocking realization.</p>
<p><strong>What I thought was people management turned out to be performance management.</strong></p>
<p>In this article, I'd like to share that realization and what I learned from the training.</p>
<h2>Monstarlab's Four Management Domains</h2>
<p>In the tech industry, when it comes to management, Daichi Hiroki's "<a href="https://levtech.jp/media/article/column/detail_661/">Four Ps</a>" framework is well known. It organizes an engineering manager's areas of activity into four categories: People, Project, Product, and Platform (Technology).</p>
<p>At Monstarlab, we define manager roles across four domains—not limited to engineering:</p>
<ol>
<li><strong>People Management</strong> - Supporting team members' growth</li>
<li><strong>Performance Management</strong> - Managing results and productivity</li>
<li><strong>Strategy Management</strong> - Planning and executing strategy</li>
<li><strong>Philosophy Management</strong> - Instilling organizational culture and values</li>
</ol>
<p>(As a side note, if only we could rename "Strategy" to something starting with "P," we'd have our own Four Ps... How about Portfolio Management?)</p>
<h2>What I Thought Was My People Management</h2>
<p>As an engineering manager, I believed I was genuinely engaging with my team members.</p>
<ul>
<li>Conducting regular 1-on-1s</li>
<li>Checking in on project status</li>
<li>Identifying project challenges and brainstorming solutions together</li>
<li>Making sure no one feels isolated</li>
</ul>
<p>I even gave a presentation titled <a href="https://speakerdeck.com/kuchitama/ren-tozu-zhi-no-en-wojie-bu-shou-tuo-kai-fa-emnojia-zhi-chuang-chu-toqian-zai-li-noyin-kichu-si">Connecting People and Organizations Through "En" - Value Creation and Unlocking Potential as an EM in Contract Development</a>, discussing team management with a focus on three "Ens": Engagement, Empowerment, and Encouragement. I thought I was doing a decent job at people management—drawing out team members' potential and alleviating their anxieties.</p>
<iframe class="speakerdeck-iframe" frameborder="0" src="https://speakerdeck.com/player/983d4dfdffe04b15bc7bf7e72ca49dbd" title="人と組織の&#x22;エン&#x22;を結ぶ - 受託開発EMの価値創出と潜在力の引き出し" allowfullscreen style="border: 0px; background: padding-box padding-box rgba(0, 0, 0, 0.1); margin: 0px; padding: 0px; border-radius: 6px; box-shadow: rgba(0, 0, 0, 0.2) 0px 5px 40px; width: 100%; height: auto; aspect-ratio: 560 / 315;" data-ratio="1.7777777777777777"></iframe>
<p>But as I reflected during the training, I realized <strong>I wasn't actually engaging with the people themselves—I was engaging with their "work."</strong></p>
<p>By focusing on project status and challenges, I was essentially concentrating on improving productivity and performance. In other words, <strong>I was doing performance management.</strong></p>
<h2>What I Learned About True People Management</h2>
<p>People management is about <strong>supporting team members' growth.</strong> To do this effectively, it's crucial to understand each member's <strong>Will (what they want to do), Can (what they can do), and Must (what they need to do).</strong></p>
<figure>
  <img src="/assets/img/articles/2025-12-15-PeopleManagementTrainingNotice/will-can-must.webp" alt="Will Can Must diagram">
</figure>
<p>During the training, a manager from another team brilliantly drew out the Will, Can, and Must from someone playing the role of a team member. This person had a strong background in branding design, and it made sense—branding is all about extracting and organizing strengths and weaknesses.
I found it fascinating that such skills translate so well to people management.</p>
<p>I learned that by truly engaging with team members and helping them articulate their Will, Can, and Must, we can effectively support their growth.
At the same time, I also realized that I'm not particularly good at delving into people's inner thoughts and feelings.</p>
<p>After the training, I found myself buying books on branding design.</p>
<p>I'm hoping to pick up some insights that will help me level up my people management skills—from "not good at it and not doing it" to "not good at it but can manage reasonably well."</p>
<h2>Summary</h2>
<p>So that's the story of how I thought I was focusing on people management as a manager, when I was actually focusing on productivity ≈ performance.</p>
<p>Of course, performance management is important work too. But realizing that I was missing the perspective of supporting team members' growth was a significant takeaway.</p>
<p>During the training, another manager shared their experience about how even if you're not a perfect manager yourself, it's okay to leverage other managers' strengths to bring out the best in your team members.
This was incredibly encouraging, especially since I had just become aware of my own people management challenges.</p>
<p>Going forward, I want to focus more on each team member's Will, Can, and Must during 1-on-1s and practice true people management.</p>
<h2>Finally</h2>
<p>Even though I'm still very much a work-in-progress as an EM, Monstarlab is looking for people who want to grow together with us.</p>
<p>In particular, we're working on launching a new team initiative like this:</p>
<p><a href="https://engineering.monstar-lab.com/jp/post/2025/10/07/FullCycle-Team-is-launched/">A New Form of Engineer for the AI Era - What Full-Cycle Engineers Mean at Monstarlab</a></p>
<p>With the spirit of "Turn Vision into Reality," we're looking for people who want to become full-cycle engineers together. If you're interested, please apply!</p>
<p><a href="https://hrmos.co/pages/monstar-lab-recruit/jobs/0002501">Monstarlab Careers | Full-Cycle Engineer</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building Backend as a Frontend Developer: Experience with Payload CMS]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2025/11/13/Frontend-Dev-Experience-with-PayloadCMS</link>
            <guid>https://engineering.monstar-lab.com/en/post/2025/11/13/Frontend-Dev-Experience-with-PayloadCMS</guid>
            <pubDate>Thu, 13 Nov 2025 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>When I was tasked with implementing a headless CMS to primarily serve a mobile application at scale, I found myself stepping into unfamiliar territory. As a frontend developer, backend development once felt like a daunting mix of database design, API implementation, authentication systems, and deployment infrastructure.</p>
<p>Payload offered an approach that leveraged existing TypeScript skills while providing necessary backend capabilities. After deploying to production and maintaining the system through several high-traffic periods, this post reflects on the practical realities of building backend systems with Payload.</p>
<h2>What is Payload?</h2>
<figure>
  <img src="/assets/img/articles/2025-11-13-Frontend-Dev-Experience-with-PayloadCMS/payloadcms-wordmark.svg">
</figure>
<p><a href="https://payloadcms.com/docs/getting-started/what-is-payload">Payload</a> is an open-source, headless content management system (CMS) built with TypeScript and Next.js. Unlike traditional CMS platforms, such as WordPress or Drupal, a headless CMS delivers content via APIs, allowing any frontend stack to consume the content.</p>
<p>A major selling point of Payload is its code-first philosophy. While other headless CMS, such as Contentful and Strapi, require defining schemas through their admin panels, Payload lets you define everything in TypeScript. This approach appeals to developers who are comfortable with modern Javascript frameworks and want to extend the same development experience to their backend infrastructure.</p>
<p>Built on top of Next.js, Payload takes advantage of the framework's server-side rendering and API routing, while also providing its own database abstraction layer. It supports both PostgreSQL and MongoDB, offers REST and GraphQL APIs, and can be deployed on serverless platforms like AWS Lambda or on traditional servers.</p>
<p>For frontend developers stepping into backend work, this means you can manage content, define data structures, and expose APIs all within a familiar TypeScript environment, without needing learn a completely new backend stack.</p>
<h2>Configuration-as-Code</h2>
<p>Payload differentiates itself through its management of schema definitions as code, as opposed to GUI configurations or SQL statements. In Payload, <code>collections</code> are Typescript configuration objects, with strongly-typed <code>fields</code> that are automatically translated into database table columns.</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> <span class="hljs-title class_">Articles</span>: <span class="hljs-title class_">CollectionConfig</span> = {
  <span class="hljs-attr">slug</span>: <span class="hljs-string">"articles"</span>,
  <span class="hljs-attr">fields</span>: [
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"title"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"text"</span>,
      <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>,
    },
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"body"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"richtext"</span>,
    },
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"category"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"relationship"</span>,
      <span class="hljs-attr">relationTo</span>: <span class="hljs-string">"categories"</span>,
    },
  ],
};
</code></pre>
<p>From a frontend developer’s perspective, this syntax feels familiar — more like defining component props than database schemas. Additionally, because the code lives in version control, changes can be tracked, reviewed collaboratively, and rolled back if necessary.</p>
<p>The collection above automatically generates several artifacts:</p>
<h3>Database Schema</h3>
<p>Database tables are created based on the field configurations. The collection above will produce an <code>articles</code> table with the following columns:</p>
<figure>
  <img src="/assets/img/articles/2025-11-13-Frontend-Dev-Experience-with-PayloadCMS/payloadcms-collection-erd.webp">
</figure>
<h3>REST API Endpoints</h3>
<p>Every collection will also receive a complete REST API without additional configurations:</p>
<pre><code class="hljs language-python">GET    /api/articles          <span class="hljs-comment"># Retrieve list with pagination</span>
GET    /api/articles/:<span class="hljs-built_in">id</span>      <span class="hljs-comment"># Retrieve single entity</span>
POST   /api/articles          <span class="hljs-comment"># Create new</span>
PATCH  /api/articles/:<span class="hljs-built_in">id</span>      <span class="hljs-comment"># Update existing</span>
DELETE /api/articles/:<span class="hljs-built_in">id</span>      <span class="hljs-comment"># Delete entity</span>
</code></pre>
<p>Default query parameters are also provided for easy filtering and sorting. For instance, in order to retrieve the first 10 articles containing "frontend" in their title, sorted by the creation date:</p>
<pre><code class="hljs language-ini">/api/articles?where<span class="hljs-section">[title]</span><span class="hljs-section">[contains]</span>=frontend&#x26;<span class="hljs-attr">sort</span>=createdAt&#x26;limit=<span class="hljs-number">10</span>
</code></pre>
<h3>TypeScript Type Definitions</h3>
<p>Type interfaces are also generated based on the collection configuration, providing type checking and autocomplete support in modern IDEs.</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">Articles</span> {
  <span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>;
  <span class="hljs-attr">title</span>: <span class="hljs-built_in">string</span>;
  body?: {
    <span class="hljs-attr">root</span>: {
      <span class="hljs-attr">type</span>: <span class="hljs-built_in">string</span>;
      <span class="hljs-attr">children</span>: {
        <span class="hljs-attr">type</span>: <span class="hljs-built_in">string</span>;
        <span class="hljs-attr">version</span>: <span class="hljs-built_in">number</span>;
        [<span class="hljs-attr">k</span>: <span class="hljs-built_in">string</span>]: <span class="hljs-built_in">unknown</span>;
      }[];
      <span class="hljs-attr">direction</span>: (<span class="hljs-string">"ltr"</span> | <span class="hljs-string">"rtl"</span>) | <span class="hljs-literal">null</span>;
      <span class="hljs-attr">format</span>: <span class="hljs-string">"left"</span> | <span class="hljs-string">"start"</span> | <span class="hljs-string">"center"</span> | <span class="hljs-string">"right"</span> | <span class="hljs-string">"end"</span> | <span class="hljs-string">"justify"</span> | <span class="hljs-string">""</span>;
      <span class="hljs-attr">indent</span>: <span class="hljs-built_in">number</span>;
      <span class="hljs-attr">version</span>: <span class="hljs-built_in">number</span>;
    };
    [<span class="hljs-attr">k</span>: <span class="hljs-built_in">string</span>]: <span class="hljs-built_in">unknown</span>;
  } | <span class="hljs-literal">null</span>;
  category?: {
    <span class="hljs-attr">relationTo</span>: <span class="hljs-string">"category"</span>;
    <span class="hljs-attr">value</span>: <span class="hljs-built_in">number</span> | <span class="hljs-title class_">Category</span>;
  } | <span class="hljs-literal">null</span>;
  createdAt?: <span class="hljs-built_in">string</span> | <span class="hljs-literal">null</span>;
  updatedAt?: <span class="hljs-built_in">string</span> | <span class="hljs-literal">null</span>;
}
</code></pre>
<p>This automatic artifact generation eliminates a considerable amount of boilerplate code and maintenance, making backend development faster and more reliable.</p>
<h2>Reducing the Backend Learning Curve</h2>
<p>Several Payload features make backend development more approachable for frontend developers:</p>
<h3>Built-in Admin Panel</h3>
<p>Payload includes a pre-built, auto-generated admin panel that leverages Next.js Hot Module Replacement for instant feedback during development. The production-ready admin panel automatically reflects TypeScript schema changes and is highly customizable for projects with complex UI requirements.</p>
<figure>
  <img src="/assets/img/articles/2025-11-13-Frontend-Dev-Experience-with-PayloadCMS/payloadcms-dashboard.webp">
  <figcaption>Payload's default admin panel dashboard</figcaption>
</figure>
<figure>
  <img src="/assets/img/articles/2025-11-13-Frontend-Dev-Experience-with-PayloadCMS/payloadcms-create-new-article.webp">
  <figcaption>"Create New Article" page</figcaption>
</figure>
<h3>Internationalization and Localization</h3>
<p>Supporting multiple languages is handled through configuration rather than complex setup. Fields can be marked as <code>localized</code>, allowing content editors to manage translations directly in the admin panel. The system automatically handles locale-specific queries and provides fallback behavior when translations are missing.</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> <span class="hljs-title class_">Articles</span>: <span class="hljs-title class_">CollectionConfig</span> = {
  <span class="hljs-attr">slug</span>: <span class="hljs-string">"articles"</span>,
  <span class="hljs-attr">fields</span>: [
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"title"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"text"</span>,
      <span class="hljs-attr">localized</span>: <span class="hljs-literal">true</span>, <span class="hljs-comment">// Enable per-locale content</span>
    },
  ],
};
</code></pre>
<p>API requests can specify locale preferences via query parameters, and Payload handles the underlying database queries to retrieve the appropriate content version. For instance, requesting <code>/api/articles?locale=ja&#x26;fallback-locale=en</code> returns Japanese content when available, falling back to English otherwise.</p>
<h3>Data Relationship Modeling</h3>
<p>Creating relationships between data structures uses the <code>relationship</code> field type, as shown in the sample collection above. Payload handles the underlying database joins and provides a functional admin UI for selecting related content.</p>
<h3>Hooks</h3>
<p>Collection-level and field-level hooks (<code>beforeRead</code>, <code>afterRead</code>, <code>beforeChange</code>, <code>afterChange</code>, etc.) mimic React lifecycle methods, allowing you to implement business logic with relative ease.</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> <span class="hljs-title class_">Articles</span>: <span class="hljs-title class_">CollectionConfig</span> = {
  <span class="hljs-attr">slug</span>: <span class="hljs-string">"articles"</span>,
  <span class="hljs-attr">fields</span>: [
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"title"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"text"</span>,
      <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>,
    },
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"slug"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"text"</span>,
    },
    <span class="hljs-comment">//...</span>
  ],
  <span class="hljs-attr">hooks</span>: {
    <span class="hljs-attr">beforeChange</span>: [
      <span class="hljs-function">(<span class="hljs-params">{ data }</span>) =></span> {
        data.<span class="hljs-property">slug</span> = data.<span class="hljs-property">title</span>.<span class="hljs-title function_">toLowerCase</span>().<span class="hljs-title function_">replace</span>(<span class="hljs-regexp">/\s+/g</span>, <span class="hljs-string">"-"</span>);
      },
    ],
  },
};
</code></pre>
<h3>Custom Endpoints</h3>
<p>Beyond the standard CRUD operations, you can define custom endpoints to handle more complex workflows.</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> <span class="hljs-title class_">Articles</span>: <span class="hljs-title class_">CollectionConfig</span> = {
  <span class="hljs-attr">slug</span>: <span class="hljs-string">"articles"</span>,
  <span class="hljs-attr">fields</span>: [
    <span class="hljs-comment">/* ... */</span>
  ],
  <span class="hljs-attr">endpoints</span>: [
    {
      <span class="hljs-attr">path</span>: <span class="hljs-string">"/:id/summary"</span>, <span class="hljs-comment">// Define endpoint</span>
      <span class="hljs-attr">method</span>: <span class="hljs-string">"get"</span>, <span class="hljs-comment">// Define method</span>
      <span class="hljs-attr">handler</span>: <span class="hljs-keyword">async</span> req => {
        <span class="hljs-keyword">if</span> (!req.<span class="hljs-property">user</span>) {
          <span class="hljs-keyword">return</span> <span class="hljs-title class_">Response</span>.<span class="hljs-title function_">json</span>({ <span class="hljs-attr">error</span>: <span class="hljs-string">"Unauthorized"</span> }, { <span class="hljs-attr">status</span>: <span class="hljs-number">401</span> });
        }
        <span class="hljs-keyword">const</span> article = <span class="hljs-keyword">await</span> req.<span class="hljs-property">payload</span>.<span class="hljs-title function_">findByID</span>({
          <span class="hljs-attr">collection</span>: <span class="hljs-string">"articles"</span>,
          <span class="hljs-attr">id</span>: req.<span class="hljs-property">routeParams</span>.<span class="hljs-property">id</span> <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>,
        });

        <span class="hljs-comment">// Execute complex operation, call external API, etc.</span>
        <span class="hljs-keyword">const</span> summary = <span class="hljs-keyword">await</span> <span class="hljs-title function_">generateAISummary</span>(article.<span class="hljs-property">body</span>);

        <span class="hljs-keyword">return</span> <span class="hljs-title class_">Response</span>.<span class="hljs-title function_">json</span>({ <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span>, summary });
      },
    },
  ],
};
</code></pre>
<h3>Access Control</h3>
<p>Authorization can be enforced at both collection and field levels, ensuring security is handled consistently at the data layer rather than relying on UI visibility.</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> <span class="hljs-title class_">Articles</span>: <span class="hljs-title class_">CollectionConfig</span> = {
  <span class="hljs-attr">slug</span>: <span class="hljs-string">"articles"</span>,
  <span class="hljs-attr">access</span>: {
    <span class="hljs-comment">// Allow anyone to read "published" articles</span>
    <span class="hljs-attr">read</span>: <span class="hljs-function">(<span class="hljs-params">{ req: { user } }</span>) =></span> {
      <span class="hljs-keyword">if</span> (user) <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
      <span class="hljs-keyword">return</span> { <span class="hljs-attr">status</span>: { <span class="hljs-attr">equals</span>: <span class="hljs-string">"published"</span> } };
    },
    <span class="hljs-comment">// Only allow authenticated users to create, update, or delete articles</span>
    <span class="hljs-attr">create</span>: <span class="hljs-function">(<span class="hljs-params">{ req: { user } }</span>) =></span> <span class="hljs-title class_">Boolean</span>(user),
    <span class="hljs-attr">update</span>: <span class="hljs-function">(<span class="hljs-params">{ req: { user } }</span>) =></span> <span class="hljs-title class_">Boolean</span>(user),
    <span class="hljs-attr">delete</span>: <span class="hljs-function">(<span class="hljs-params">{ req: { user } }</span>) =></span> <span class="hljs-title class_">Boolean</span>(user),
  },
};
</code></pre>
<p>This declarative approach makes access control simpler and more maintainable than manually checking permissions in every route handler. Security is enforced consistently for every request, giving you confidence that your data layer is protected.</p>
<h2>Caveats and Challenges</h2>
<p>While Payload provides a compelling developer experience, moving to production surfaces several challenges worth considering.</p>
<h3>Database &#x26; Schema Management</h3>
<p>During development, the TypeScript-to-database workflow feels seamless, but in production with PostgreSQL, explicit migration management is required. Payload provides migration scripts, but you need to run <code>payload migrate:create</code> for every schema change and carefully manage the order of migrations across environments. This can add coordination overhead when multiple developers are updating schemas simultaneously.</p>
<h3>Serverless Deployment Complexity</h3>
<p>Deploying Payload as a serverless function can be more complex than expected. Cold starts can impact performance, but in a live production service, they are generally not an issue with typical traffic patterns and proper memory allocation. Additionally, Payload does not natively support scheduled tasks or batch jobs in serverless environments, so features like scheduled publishing require building separate infrastructure using Lambda functions and EventBridge scheduling.</p>
<h3>React and Next.js Integration</h3>
<p>Since Payload is built on Next.js with the App Router, you also inherit React Server Component challenges. Custom admin panel components must carefully navigate the server/client boundary, and hydration mismatches can occur when building custom fields that interact with browser APIs. If you've struggled with Next.js hydration errors before, expect to encounter them here as well.</p>
<h2>Conclusion</h2>
<p>As a frontend developer, working with Payload felt like extending my TypeScript toolset rather than learning an entirely new backend framework. Its familiar patterns, automatic API generation, and strong schema typing made backend development more approachable than expected.</p>
<p>There are still challenges — managing migrations, optimizing serverless deployments, and understanding the underlying database structures take deliberate effort. However, Payload narrows the gap between frontend and backend development by letting you apply existing TypeScript experience to backend systems. Payload doesn't cover everything — advanced database optimization, distributed systems, and infrastructure management require dedicated learning. But it offers a practical entry point.</p>
<p>For developers comfortable with React and modern build pipelines, it proves that backend systems can be built without leaving familiar territory.</p>
<hr>
<p><em>Payload, the Payload design, and related marks, designs, and logos are trademarks or registered trademarks of Payload CMS, Inc. in the U.S. and other countries.</em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Real-time data processing, part 1 - AWS ECS App Mesh and Retry Strategies]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2025/08/26/Real-time-data-processing-Part-1-aws-app-mesh-and-retry-strategies</link>
            <guid>https://engineering.monstar-lab.com/en/post/2025/08/26/Real-time-data-processing-Part-1-aws-app-mesh-and-retry-strategies</guid>
            <pubDate>Tue, 26 Aug 2025 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>In today's fast-paced data-driven world, real-time data processing has become indispensable for businesses across various sectors.</p>
<p>From monitoring system performance to analyzing customer behavior, the ability to process data in real-time offers invaluable insights for timely decision-making.</p>
<p>However, one critical aspect often overlooked is handling failures gracefully in real-time data processing pipelines.</p>
<p>In this part of our series, we will explore the importance of implementing robust retry strategies.</p>
<blockquote>
<p><strong>Important Note</strong>: AWS has announced that AWS App Mesh will be discontinued in September 2026. While App Mesh remains fully supported until then, you should consider AWS Service Connect instead. AWS Service Connect provides similar functionality with improved performance and simplified configuration.</p>
</blockquote>
<h2>Part 1: AWS ECS App Mesh and Retry Strategies</h2>
<p>Amazon Elastic Container Service (ECS) is a fully managed container orchestration service that allows you to run, stop, and manage Docker containers on a cluster. It eliminates the need to install, operate, and scale a cluster management infrastructure.</p>
<p>AWS App Mesh belongs to the category of service meshes, which are specialized infrastructure layers designed to manage communication between services within a distributed application architecture.</p>
<p>Essentially, AWS App Mesh simplifies the networking aspect of your applications by offering features like service discovery, load balancing, encryption, authentication, and observability.</p>
<p>In essence, AWS App Mesh streamlines communication between microservices, allowing developers to focus on building application logic rather than worrying about networking setup.</p>
<p>This approach enhances the reliability, security, and observability of modern distributed systems. Other popular service mesh implementations include Istio and Linkerd.</p>
<p>AWS App Mesh, in conjunction with Amazon ECS, offers a robust platform for deploying microservices architectures that are well-suited for real-time data processing.</p>
<p>While App Mesh offers features like service discovery, traffic management, and observability, its inherent ability to handle failures through retry policies is crucial for ensuring data integrity and system reliability.</p>
<h3>Importance of Retry Strategies</h3>
<p>In real-time data processing, failures are inevitable due to network issues, service disruptions, or transient errors.</p>
<p>Therefore, implementing effective retry strategies becomes crucial. Here are some key aspects to consider:</p>
<ol>
<li>
<p><strong>Determining Retry Attempts</strong>: Deciding the number of retry attempts depends on factors like the criticality of the data, the likelihood of transient failures, and the impact on downstream processes. It's essential to find a balance between ensuring data delivery and avoiding endless retries, which could lead to resource exhaustion.</p>
</li>
<li>
<p><strong>Exponential Backoff</strong>: Adopting exponential backoff strategies can prevent overwhelming downstream systems during high-load scenarios. Gradually increasing the time between retry attempts reduces the likelihood of further failures.</p>
</li>
<li>
<p><strong>Dead Letter Queues (DLQ)</strong>: Implementing DLQs allows you to capture failed messages for further analysis and manual intervention. It's crucial to set up robust monitoring and alerting mechanisms to promptly address issues identified in the DLQ.</p>
</li>
</ol>
<h3>Testing App Mesh Retry Policy</h3>
<p>Let's put the App Mesh retry policy to the test by simulating failure scenarios in a real-time data processing pipeline.</p>
<p>We'll set up two services: a Data Ingestion service and a Data Processing service using Terraform.</p>
<ol>
<li>
<p><strong>Data Ingestion Service</strong>: This service receives data from external sources and forwards it to the processing pipeline.</p>
</li>
<li>
<p><strong>Data Processing Service</strong>: This service analyzes incoming data in real-time, performing tasks such as anomaly detection or aggregation.</p>
</li>
</ol>
<p><img src="/assets/img/articles/2025-08-26-Real-time-data-processing-Part-1-aws-app-mesh-and-retry-strategies/app-mesh-retry-example-architecture-overview.png" alt="Architecture Overview"></p>
<h4>Terraform Configuration</h4>
<p>We'll use Terraform to define the infrastructure for the Data Ingestion service and Data Processing service, along with the App Mesh configuration.</p>
<p>We will omit the full Terraform configuration for brevity, but here's a high-level overview of the key components:</p>
<ol>
<li><strong>App Mesh</strong></li>
</ol>
<p>Let's start with the core components of the app mesh.</p>
<ul>
<li><strong>Service Mesh</strong>: The logical boundary for the services that make up the app mesh itself.</li>
</ul>
<pre><code class="hljs language-hcl">resource "aws_appmesh_mesh" "app-mesh" {
  name = "${var.env}-${var.project}-app-mesh"

  spec {
    egress_filter {
      type = "DROP_ALL" # Allow only egress from virtual nodes to other resources within the mesh
    }
  }
}
</code></pre>
<ul>
<li><strong>Virtual Service</strong>: Abstract representation of the Data Ingestion service and the Data Processing service running in the mesh.</li>
</ul>
<pre><code class="hljs language-hcl"># data-processing virtual service
resource "aws_appmesh_virtual_service" "data-processing-service" {
  name      = "${local.services.data-processing}.${var.env}.${var.internal_domain}"
  mesh_name = aws_appmesh_mesh.app-mesh.id

  spec {
    provider {
      virtual_router {
        virtual_router_name = aws_appmesh_virtual_router.data-processing-service.name
      }
    }
  }
}

# data-ingestion virtual service
# ...
</code></pre>
<p>The virtual router is responsible for handling traffic for the virtual service. It is defined later in the configuration with routing rules and retry policies for the virtual service.</p>
<ul>
<li><strong>Virtual Node</strong>: Concrete implementations behind the abstracted virtual services. Each virtual node points to a specific ECS service, where actual code runs.</li>
</ul>
<pre><code class="hljs language-hcl"># data-ingestion virtual node
resource "aws_appmesh_virtual_node" "data-ingestion-service" {
  name      = "${var.env}-${var.project}-data-ingestion-service"
  mesh_name = aws_appmesh_mesh.app-mesh.id

  spec {
    backend {
      virtual_service {
        virtual_service_name = aws_appmesh_virtual_service.data-processing-service.name
      }
    }

    listener {
      port_mapping {
        port     = var.ecs_services["data-ingestion"].app_port
        protocol = "http"
      }

      timeout {
        http {
          per_request {
            value = var.ecs_services["data-ingestion"].app_mesh_timeout.value
            unit  = var.ecs_services["data-ingestion"].app_mesh_timeout.unit
          }
        }
      }
    }

    service_discovery {
      aws_cloud_map {
        service_name   = aws_appmesh_virtual_service.data-processing-service.name
        namespace_name = aws_service_discovery_private_dns_namespace.internal.name
      }
    }

    logging {
      access_log {
        file {
          path = "/dev/stdout"
        }
      }
    }
  }
}


# data-processing virtual node
# ...
</code></pre>
<p>In this example, we have defined the virtual nodes for the Data Processing service and the Data Ingestion service.</p>
<p>In the Data Ingestion virtual node, we have defined the Data Processing virtual service as the backend to which the virtual node is expected to send outbound traffic.</p>
<p>We have specified the listener for the virtual nodes, which defines the port and protocol for incoming traffic.</p>
<p>We have also specified the service discovery mechanism for the virtual node, which in this case is AWS Cloud Map.</p>
<ul>
<li><strong>Route</strong>: Specifies how traffic flows between services. You can define routing rules based on various criteria like service name, attributes, or weighted distribution.</li>
</ul>
<pre><code class="hljs language-hcl"># data-processing App Mesh virtual router
resource "aws_appmesh_virtual_router" "data-processing-service" {
  name      = "${var.env}-${var.project}-data-processing-service-virtual-router"
  mesh_name = aws_appmesh_mesh.app-mesh.id

  spec {
    listener {
      port_mapping {
        port     = var.ecs_services["data-processing"].app_port
        protocol = "http"
      }
    }
  }
}

# data-processing service App Mesh route
resource "aws_appmesh_route" "data-processing-service" {
  name                = "${var.env}-${var.project}-data-processing-service"
  mesh_name           = aws_appmesh_mesh.app-mesh.id
  virtual_router_name = aws_appmesh_virtual_router.data-processing-service.name

  spec {
    http_route {
      match {
        prefix = "/"
      }

      retry_policy {
        http_retry_events = [
          "gateway-error",
        ]
        max_retries = 12
        per_retry_timeout {
          unit  = "s"
          value = 5
        }
        tcp_retry_events = [
          "connection-error",
        ]
      }

      action {
        weighted_target {
          virtual_node = aws_appmesh_virtual_node.data-processing-service.name
          weight       = 1
        }
      }
    }

    priority = 1
  }
}

# data-ingestion service App Mesh virtual router
# ...

# data-ingestion service App Mesh route
# ...
</code></pre>
<p>We have defined the route to match any incoming HTTP traffic and route it with retries to the Data Processing service virtual node.</p>
<p>The retry policy specifies that the service should retry on gateway errors and connection errors, with a maximum of 12 retries and a per-retry timeout of 5 seconds.</p>
<p>Note that the action specifies the weighted target, which is the virtual node to which the traffic should be routed. We have set the weight to 1, indicating that all traffic should be routed to the Data Processing service virtual node.</p>
<p>Note that when defining virtual services, we could have used a virtual node as the provider for the virtual service and omitted a virtual router, which would not provide granular control over the routing rules.</p>
<pre><code class="hljs language-hcl"># data-processing virtual service
resource "aws_appmesh_virtual_service" "data-processing-service" {
  name      = "${local.services.data-processing}.${var.env}.${var.internal_domain}"
  mesh_name = aws_appmesh_mesh.app-mesh.id

  spec {
    provider {
      virtual_node {
        virtual_node_name = aws_appmesh_virtual_node.data-processing-service.name
      }
    }
  }
}
</code></pre>
<ul>
<li><strong>Cloud Map Service Discovery</strong>: This allows services to discover and communicate with each other using custom DNS names.</li>
</ul>
<pre><code class="hljs language-hcl"># Private DNS Namespace
resource "aws_service_discovery_private_dns_namespace" "internal" {
  name        = "${var.env}.${var.internal_domain}"
  description = "${var.env}-${var.project}-private-dns-namespace"
  vpc         = var.aws_vpc-vpc-id
}

# data-processing
resource "aws_service_discovery_service" "data-processing" {
  name = local.services.data-processing

  dns_config {
    namespace_id = aws_service_discovery_private_dns_namespace.internal.id

    dns_records {
      ttl  = 10
      type = "A"
    }

    routing_policy = "MULTIVALUE"
  }

  health_check_custom_config {
    failure_threshold = 1
  }
  
  lifecycle {
    create_before_destroy = true
  }
}
</code></pre>
<ol start="2">
<li><strong>AWS ECS</strong></li>
</ol>
<p>We'll now define the ECS cluster configuration for the Data Ingestion service and the Data Processing service.</p>
<ul>
<li><strong>ECS Cluster</strong></li>
</ul>
<pre><code class="hljs language-hcl"># ECS cluster
resource "aws_ecs_cluster" "ecs-cluster" {
  name = "${var.env}-${var.project}-ecs-cluster"

  setting {
    name  = "containerInsights"
    value = "enabled"
  }
}

# ECS cluster capacity provider
resource "aws_ecs_cluster_capacity_providers" "ecs-cluster" {
  cluster_name = aws_ecs_cluster.ecs-cluster.name

  capacity_providers = ["FARGATE"]

  default_capacity_provider_strategy {
    weight            = 1
    capacity_provider = "FARGATE"
  }
}
</code></pre>
<ul>
<li><strong>ECS Services</strong></li>
</ul>
<pre><code class="hljs language-hcl"># data-processing
resource "aws_ecs_service" "data-processing-service" {
  name                               = local.services.data-processing
  cluster                            = aws_ecs_cluster.ecs-cluster.id
  task_definition                    = aws_ecs_task_definition.ecs-data-processing-task.arn
  launch_type                        = "FARGATE"
  deployment_maximum_percent         = var.ecs_services["data-processing"].deployment_maximum_percent
  deployment_minimum_healthy_percent = var.ecs_services["data-processing"].deployment_minimum_healthy_percent
  desired_count                      = var.ecs_services["data-processing"].desired_count
  force_new_deployment               = true

  lifecycle {
    ignore_changes = [desired_count, task_definition]
  }

  network_configuration {
    subnets         = var.aws_subnet-protected-ids
    security_groups = [aws_security_group.ecs-service.id]
  }
  
  service_registries {
    registry_arn = aws_service_discovery_service.data-processing.arn
  }
}

# data-ingestion
# ...
</code></pre>
<p>We have created the ECS cluster and defined the ECS services for the Data Processing and Data Ingestion services.</p>
<ul>
<li><strong>Task Definition</strong></li>
</ul>
<pre><code class="hljs language-hcl"># data-processing task definition
resource "aws_ecs_task_definition" "ecs-data-processing-task" {
  family                   = "${var.env}-${var.project}-data-processing-service"
  requires_compatibilities = ["FARGATE"]
  cpu                      = var.ecs_services["data-processing"].task_definition.cpu
  memory                   = var.ecs_services["data-processing"].task_definition.memory
  network_mode             = "awsvpc"
  execution_role_arn       = aws_iam_role.task-exec.arn
  task_role_arn            = aws_iam_role.ecs-task.arn

  container_definitions = jsonencode([
    {
      name      = "data-processing-service"
      image     = "${aws_ecr_repository.data-processing-service.repository_url}:${var.app_version}"
      essential = true
      environment = [
        {
          name  = "APP_PORT"
          value = tostring(var.ecs_services["data-processing"].app_port)
        }
      ]
      portMappings = [
        {
          containerPort = var.ecs_services["data-processing"].app_port
          protocol      = "tcp"
        }
      ]
      healthCheck = {
        command = [
          "CMD-SHELL",
          "curl -s http://localhost:${var.ecs_services["data-processing"].app_port}/health-check || exit 1"
        ]
        interval    = 20
        retries     = 5
        startPeriod = 10
        timeout     = 5
      }
      depends_on = [
        {
          "containerName" : "envoy",
          "condition" : "HEALTHY"
        }
      ]
      logConfiguration = {
        logDriver = "awslogs"
        options = {
          awslogs-group         = aws_cloudwatch_log_group.data-processing-service.name
          awslogs-region        = var.region
          awslogs-stream-prefix = "awslogs-data-processing"
        }
      }
    },
    {
      name      = "envoy"
      image     = "000000000000.dkr.ecr.${data.aws_region.current.name}.amazonaws.com/aws-appmesh-envoy:v1.27.2.0-prod"
      essential = true
      cpu       = var.ecs_services["data-processing"].task_definition.envoy_cpu
      memory    = var.ecs_services["data-processing"].task_definition.envoy_memory
      environment = [
        {
          name  = "APPMESH_RESOURCE_ARN",
          value = aws_appmesh_virtual_node.data-processing-service.arn
        },
        {
          name  = "ENVOY_LOG_LEVEL",
          value = "info"
        },
        {
          name  = "ENVOY_INITIAL_FETCH_TIMEOUT",
          value = "30"
        },
        {
          name  = "ENABLE_ENVOY_XRAY_TRACING",
          value = "1"
        },
      ]
      portMappings = [
        {
          protocol      = "tcp",
          containerPort = 9901
        },
        {
          protocol      = "tcp",
          containerPort = 15000
        },
        {
          protocol      = "tcp",
          containerPort = 15001
        }
      ]
      healthCheck = {
        command = [
          "CMD-SHELL",
          "curl -s http://localhost:9901/server_info | grep state | grep -q LIVE"
        ],
        interval    = 5,
        retries     = 3,
        startPeriod = 60,
        timeout     = 2
      }
      user = "1337"
      logConfiguration = {
        logDriver = "awslogs"
        options = {
          awslogs-group         = aws_cloudwatch_log_group.data-processing-service.name
          awslogs-region        = var.region
          awslogs-stream-prefix = "awslogs-envoy-data-processing"
        }
      }
    },
    {
      name              = "xray-daemon"
      image             = "public.ecr.aws/xray/aws-xray-daemon:latest"
      essential         = false
      cpu               = var.ecs_services["data-processing"].task_definition.xray_cpu
      memoryReservation = var.ecs_services["data-processing"].task_definition.xray_memory
      portMappings = [
        {
          hostPort      = 2000
          containerPort = 2000
          protocol      = "udp"
        }
      ]
      user = "1337"
      logConfiguration = {
        logDriver = "awslogs"
        options = {
          awslogs-group         = aws_cloudwatch_log_group.data-processing-service.name
          awslogs-region        = var.region
          awslogs-stream-prefix = "awslogs-xray-data-processing"
        }
      }
    }
  ])
  proxy_configuration {
    type           = "APPMESH"
    container_name = "envoy"
    properties = {
      AppPorts           = var.ecs_services["data-processing"].app_port
      EgressIgnoredIPs   = "169.254.170.2,169.254.169.254"
      IgnoredUID         = "1337"
      ProxyEgressPort    = 15001
      ProxyIngressPort   = 15000
      EgressIgnoredPorts = 22
    }
  }
}

# data-ingestion task definition
# ...
</code></pre>
<p>The Data Processing and Data Ingestion services task definitions include:</p>
<ul>
<li>
<p>Individual container configurations: This includes specifying the image used, necessary environment variables, port mappings for communication, health checks to ensure proper functioning, and logging setups for analysis.</p>
</li>
<li>
<p>App Mesh integration: The defined proxy configuration enables the Envoy sidecar proxy to intercept and route both incoming and outgoing traffic through the App Mesh service mesh. Envoy acts as a high-performance data plane component, essentially managing the network interactions within the mesh. For deeper understanding, please refer to the <a href="https://www.envoyproxy.io/docs">official Envoy documentation</a>.</p>
</li>
<li>
<p>Distributed tracing: The inclusion of a xray-daemon container allows for tracing the flow of requests across various services, providing valuable insights into system performance and potential issues via AWS X-Ray.</p>
</li>
</ul>
<p>Here's a high-level overview of the Terraform configuration for the Data Ingestion and the Data Processing services.
<img src="/assets/img/articles/2025-08-26-Real-time-data-processing-Part-1-aws-app-mesh-and-retry-strategies/app-mesh-infra.png" alt="App Mesh Infra"></p>
<h4>Backend Services</h4>
<p>Let's proceed to develop the backend applications for the Data Ingestion and Data Processing services using Golang.</p>
<ol>
<li><strong>Data Ingestion Service</strong></li>
</ol>
<pre><code class="hljs language-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
	<span class="hljs-comment">// Create a new router</span>
	router := mux.NewRouter()

	<span class="hljs-comment">// Define routes</span>
	router.HandleFunc(<span class="hljs-string">"/health-check"</span>, HealthCheckHandler).Methods(<span class="hljs-string">"GET"</span>)
	router.HandleFunc(<span class="hljs-string">"/ev"</span>, ElectricVehicleHandler).Methods(<span class="hljs-string">"POST"</span>)

	<span class="hljs-comment">// Start the server</span>
	log.Fatal(http.ListenAndServe(<span class="hljs-string">":3000"</span>, router))
}

<span class="hljs-comment">// ElectricVehicleHandler handles the /ev endpoint</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">ElectricVehicleHandler</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
	log.Printf(<span class="hljs-string">"Request: %s %s"</span>, r.Method, r.URL.Path)

	<span class="hljs-comment">// Read request body</span>
	body, err := io.ReadAll(r.Body)
	<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
		log.Println(err)
		http.Error(w, <span class="hljs-string">"Failed to read request body"</span>, http.StatusInternalServerError)
		<span class="hljs-keyword">return</span>
	}

	<span class="hljs-comment">// Decode JSON payload</span>
	<span class="hljs-keyword">var</span> payload ElectricVehiclePayload
	err = json.Unmarshal(body, &#x26;payload)
	<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
		log.Println(err)
		http.Error(w, <span class="hljs-string">"Failed to decode JSON payload"</span>, http.StatusBadRequest)
		<span class="hljs-keyword">return</span>
	}

	<span class="hljs-comment">// Do whatever pre-processing is required</span>
	<span class="hljs-comment">// ...</span>

	<span class="hljs-comment">// Create a request to the data processing service endpoint</span>
	req, err := http.NewRequest(<span class="hljs-string">"POST"</span>, dataProcessingEndpoint, bytes.NewBuffer(body))
	<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
		log.Println(fmt.Errorf(<span class="hljs-string">"failed to create request to data processing service: %w"</span>, err))
		http.Error(w, <span class="hljs-string">"Failed to POST to data processing service"</span>, http.StatusInternalServerError)
		<span class="hljs-keyword">return</span>
	}

	<span class="hljs-comment">// Clone headers from the incoming request to the outgoing request</span>
	req.Header = r.Header.Clone()

	<span class="hljs-comment">// HTTP POST request to data processing service</span>
	resp, err := http.DefaultClient.Do(req)
	<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
		log.Println(fmt.Errorf(<span class="hljs-string">"failed to create request to data processing service: %w"</span>, err))
		http.Error(w, <span class="hljs-string">"Failed to POST to data processing service"</span>, http.StatusInternalServerError)
		<span class="hljs-keyword">return</span>
	}

	<span class="hljs-comment">// Check if response status code is not 200</span>
	<span class="hljs-keyword">if</span> resp.StatusCode != http.StatusOK {
		msg := fmt.Sprintf(<span class="hljs-string">"Data processing service returned non-200 status code: %d"</span>, resp.StatusCode)
		log.Print(msg)
		http.Error(w, msg, resp.StatusCode)
		<span class="hljs-keyword">return</span>
	}

	<span class="hljs-comment">// Copy the response from the data processing service to the current response writer</span>
	_, err = io.Copy(w, resp.Body)
	<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
		log.Println(err)
		http.Error(w, <span class="hljs-string">"Failed to copy response"</span>, http.StatusInternalServerError)
		<span class="hljs-keyword">return</span>
	}
}
</code></pre>
<p>The Data Ingestion service is a simple HTTP server that listens for incoming POST requests via the <code>/ev</code> endpoint.</p>
<p>This service is supposed to receive data from external sources (Electric Vehicle data in this case).</p>
<p>In this example, we've defined the <code>ElectricVehicleHandler</code> function to handle incoming EV data. It decodes the JSON payload, performs any pre-processing required, and forwards the request to the Data Processing service.</p>
<ol start="2">
<li><strong>Data Processing Service</strong></li>
</ol>
<pre><code class="hljs language-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
	<span class="hljs-comment">// Create a new router</span>
	router := mux.NewRouter()

	<span class="hljs-comment">// Define routes</span>
	router.HandleFunc(<span class="hljs-string">"/health-check"</span>, HealthCheckHandler).Methods(<span class="hljs-string">"GET"</span>)
	router.HandleFunc(<span class="hljs-string">"/ev"</span>, ElectricVehicleDataProcessingHandler).Methods(<span class="hljs-string">"POST"</span>)

	<span class="hljs-comment">// Start the server</span>
	log.Fatal(http.ListenAndServe(<span class="hljs-string">":3000"</span>, router))
}

<span class="hljs-comment">// ElectricVehicleDataProcessingHandler handles EV data</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">ElectricVehicleDataProcessingHandler</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
	<span class="hljs-comment">// Log the request</span>
	log.Printf(<span class="hljs-string">"Request: %s %s"</span>, r.Method, r.URL.Path)

	<span class="hljs-comment">// return 503 if x-503 header is set</span>
	<span class="hljs-keyword">if</span> value, ok := r.Header[<span class="hljs-string">"X-503"</span>]; ok {
		log.Printf(<span class="hljs-string">"X-503 header is set with values: %v"</span>, value)
		w.WriteHeader(http.StatusServiceUnavailable)
		_, err := w.Write([]<span class="hljs-type">byte</span>(<span class="hljs-string">"Data Processing Service will return 503 ==> called with x-503 header set."</span>))
		<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
			log.Println(err)
		}
		<span class="hljs-keyword">return</span>
	}

	<span class="hljs-comment">// Read request body</span>
	body, err := io.ReadAll(r.Body)
	<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
		log.Println(err)
		http.Error(w, <span class="hljs-string">"Failed to read request body"</span>, http.StatusInternalServerError)
		<span class="hljs-keyword">return</span>
	}

	<span class="hljs-comment">// Decode JSON payload</span>
	<span class="hljs-keyword">var</span> payload ElectricVehiclePayload
	err = json.Unmarshal(body, &#x26;payload)
	<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
		log.Println(err)
		http.Error(w, <span class="hljs-string">"Failed to decode JSON payload"</span>, http.StatusBadRequest)
		<span class="hljs-keyword">return</span>
	}

	<span class="hljs-comment">// Process the payload</span>
	<span class="hljs-comment">// ...</span>

	<span class="hljs-comment">// Response back to the client</span>
	w.WriteHeader(http.StatusOK)
	_, err = w.Write([]<span class="hljs-type">byte</span>(<span class="hljs-string">"Payload processed successfully"</span>))
	<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
		log.Println(err)
		<span class="hljs-keyword">return</span>
	}
}

</code></pre>
<p>The Data Processing Service is also a simple HTTP server that listens for incoming POST requests via the <code>/ev</code> endpoint.</p>
<p>To simulate a failure scenario, the <code>ElectricVehicleDataProcessingHandler</code> function returns a 503 status code if the x-503 header is set.</p>
<p>This service is supposed to analyze incoming data in real-time, performing tasks such as anomaly detection or aggregation.</p>
<p>After running <code>terraform apply</code> and deploying the backend services, we can check from the Dashboard that the services are running and healthy.</p>
<p><img src="/assets/img/articles/2025-08-26-Real-time-data-processing-Part-1-aws-app-mesh-and-retry-strategies/ecs-cluster.png" alt="ECS Dashboard"></p>
<p>and the App Mesh configuration is also set up correctly.</p>
<p><img src="/assets/img/articles/2025-08-26-Real-time-data-processing-Part-1-aws-app-mesh-and-retry-strategies/app-mesh-dashboard.png" alt="App Mesh Dashboard"></p>
<h4>Simulating Failure Scenarios</h4>
<p>We'll simulate failure scenarios by passing the x-503 header to the Data Processing service, causing it to return a 503 status code.</p>
<p>Using AWS Session Manager to get inside the protected subnet, let's first confirm that calls to our services are going through the envoy proxy.</p>
<p><img src="/assets/img/articles/2025-08-26-Real-time-data-processing-Part-1-aws-app-mesh-and-retry-strategies/app-mesh-health-check.png" alt="Services Health"></p>
<p>We can see that services are healthy and calls are going through the envoy proxy.</p>
<ul>
<li>
<p><code>server: envoy</code>: Indicates that the request is being handled by the Envoy Proxy.</p>
</li>
<li>
<p><code>x-envoy-upstream-service-time: 1</code>: Indicates the time (in milliseconds) taken by Envoy Proxy to communicate with the upstream service (backend services in this case).</p>
</li>
</ul>
<p>Now, we'll call the Data Ingestion service with the x-503 header set, and observe the behavior of the App Mesh retry policy.</p>
<pre><code class="hljs language-sh">curl --location <span class="hljs-string">'http://data-ingestion.dev.smn-app-mesh-ecs.internal:3000/ev'</span> \
     --header <span class="hljs-string">'Content-Type: application/json'</span> \
     --header <span class="hljs-string">'x-503: true'</span> \
     --data <span class="hljs-string">'{
      "vehicle_id": "EV-001",
      "timestamp": "2024-02-15T10:30:00Z",
      "location": {
        "latitude": 37.7749,
        "longitude": -122.4194
      },
      "battery": {
        "percentage": 75,
        "voltage": 390,
        "temperature": 25.3
      },
      "speed": 60,
      "odometer": 12500
    }
'</span>
</code></pre>
<ul>
<li>Data Ingestion service logs:</li>
</ul>
<p><img src="/assets/img/articles/2025-08-26-Real-time-data-processing-Part-1-aws-app-mesh-and-retry-strategies/ingestion-logs.png" alt="Data Ingestion Logs"></p>
<ul>
<li>Data Processing service logs:</li>
</ul>
<p><img src="/assets/img/articles/2025-08-26-Real-time-data-processing-Part-1-aws-app-mesh-and-retry-strategies/processing-logs.png" alt="Data Processing Logs"></p>
<p>For a single request sent to the Data Ingestion service, we observed that the Data Processing service returned a 503 status code. Consequently, the request was retried 12 times in accordance with the retry policy.</p>
<p>In this case, since the Data Processing service is implemented to return a 503 status code when the x-503 header is set, the request failed after 12 retries.</p>
<p>In a real-world scenario, the Data Processing service would eventually recover from the transient failure, and the request would be successfully processed.</p>
<p>In the absence of the App Mesh retry policy, the request to the Data Processing service would have failed, and the Data Ingestion service would have returned an error to the client.</p>
<p>It's worth noting that in the absence of the App Mesh retry policy, one would have implemented an HTTP retry mechanism in the Data Ingestion Golang code. Utilizing the App Mesh retry policy offers a more robust and centralized approach, allowing developers to focus on application logic rather than network setup concerns.</p>
<h3>Conclusion</h3>
<p>Incorporating retry strategies is imperative for ensuring the resilience and reliability of real-time data communication between microservices.</p>
<p>With AWS ECS App Mesh, coupled with effective retry policies, organizations can mitigate the impact of failures and uphold data integrity in critical business processes.</p>
<p>In the next part of our series, we will explore additional crucial elements of real-time data processing.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introducing Shorebird, code push service for Flutter apps]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2024/11/07/Shorebird-Code-Push-For-Flutter-Apps</link>
            <guid>https://engineering.monstar-lab.com/en/post/2024/11/07/Shorebird-Code-Push-For-Flutter-Apps</guid>
            <pubDate>Thu, 07 Nov 2024 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h1>Update Flutter apps without store review</h1>
<h2>What is Shorebird?</h2>
<p>Shorebird is a service that allows Flutter apps to be updated directly at runtime. Removing the need to build and submit a new app version to Apple Store Connect or Play Console for review for every app update.</p>
<p>Flutter already comes with hot reload/restart that allows developers to quickly test code changes during development.
Shorebird is similarly used for changes in production apps already in the hands of end-users.</p>
<h2>Why?</h2>
<p>Enabling production code to be updated without needing to go through the usual store review process, which greatly reduces the time required to release a critical bug fix. With finer control over app updates it would also let us develop mobile apps with smaller release windows and flexibility similar to web site deployments.</p>
<h2>How?</h2>
<p>Shorebird uses a modified version of the Flutter engine that allows updated code changes to be run at runtime using the Dart Virtual Machine. Since running code in the Virtual Machine is a lot slower then regular compiled code, Shorebird is smart enough to detect only the changed code parts to minimize its usage.</p>
<p>Broadly there are two main concepts.<br>
<strong>Release:</strong>
A full app binary containing the whole Flutter app, the same as you would normally build when releasing an app. With the difference it also contains a custom Flutter engine modified to run code in a virtual machine and functionality to check for updates. The release app needs to be reviewed and distributed through app stores as usual.</p>
<p><strong>Patch:</strong>
The patch only contains the code changes required for a Shorebird code push update. It's created by comparing the changes to the targeted release binary.</p>
<p>Both are created using the Shorebird CLI tool with the <code>shorebird release</code> and <code>shorebird patch</code> commands respectively. Behind the scenes this wraps the usual flutter build command.</p>
<p>Simple usage flow</p>
<ol>
<li>Install Shorebird CLI tools</li>
<li>Register your Flutter app with Shorebird, using the shorebird init command</li>
<li>Create a new app release with the shorebird release command</li>
<li>Distribute the shorebird release app to app stores (or applicable distribution channels)</li>
<li>Modify some dart code in the app (adding new functionality/fixing a bug)</li>
<li>Create a patch with the shorebird patch command</li>
<li>After the patch has been uploaded to Shorebird servers, on the next app start the patch will be downloaded and applied.</li>
</ol>
<p>By default the app will check for any new patches and update the app at startup.
The update check can also be done programmatically for more advanced use cases.</p>
<h3>Example demo</h3>
<p>For this example we will be following the <a href="https://docs.shorebird.dev/">Getting Started</a> guide from the Shorebird documentation.
In short we will be adding Shorebird to the Flutter counter example app, change some code and push a patch to the app.</p>
<ol>
<li>First we need to install the Shorebird CLI tools by using the handy install script.</li>
</ol>
<pre><code class="hljs language-shell">curl --proto '=https' --tlsv1.2 https://raw.githubusercontent.com/shorebirdtech/install/main/install.sh -sSf | bash
</code></pre>
<p>For this demo I'm using a Mac setup but there are also instructions for Windows environments in the documentation. See the Getting Started link above.</p>
<ol start="2">
<li>After the CLI tools have been installed we now have to set up the Flutter project to use Shorebird using the shorebird init command.
You will also be prompted to select a name for your app. It's only used to manage your app in the Shorebird console and won't be shown to end-users.</li>
</ol>
<pre><code class="hljs language-shell">user@my-mac shorebird_test % shorebird init
✓ No product flavors detected. (19.2s)
? How should we refer to this app? (shorebird_test) shorebird_test

🐦 Shorebird initialized successfully!

✅ A shorebird app has been created.
✅ A "shorebird.yaml" has been created.
✅ The "pubspec.yaml" has been updated to include "shorebird.yaml" as an asset.

Reference the following commands to get started:

📦 To create a new release use: "shorebird release".
🚀 To push an update use: "shorebird patch".
👀 To preview a release use: "shorebird preview".

For more information about Shorebird, visit https://shorebird.dev
✓ Shorebird is up-to-date (1.4s)
✓ Flutter install is correct (15.4s)
✓ AndroidManifest.xml files contain INTERNET permission (1 fix applied) (83ms)
✓ Has access to storage.googleapis.com (0.2s)
✓ shorebird.yaml found in pubspec.yaml assets (9ms)
</code></pre>
<p>Checking the Shorebird console we can see our app has been added.</p>
<figure>
<img src="/assets/img/articles/2024-11-07-Shorebird-Code-Push-For-Flutter-Apps/app-registered-to-shorebird-console.png">
<figcaption>The shorebird_test app has been registered in the Shorebird console</figcaption>
</figure>
<ol start="3">
<li>Now we can create our first Shorebird supported release binary using the shorebird release command.
Creating an apk file for Android only just to simplify things. This would need to be run again for the iOS app.</li>
</ol>
<pre><code class="hljs language-shell">user@my-mac shorebird_test % shorebird release android --artifact apk
✓ Downloading patch... (0.4s)
✓ Extracting patch... (0.6s)
✓ Downloading bundletool.jar... (0.7s)
✓ Extracting bundletool.jar... (1.9s)
✓ Downloading aot-tools.dill... (0.2s)
✓ Extracting aot-tools.dill... (1.5s)
✓ Fetching apps (0.4s)
✓ Building app bundle with Flutter 3.24.1 (3f7041c5e9) (75.8s)
✓ Release version: 1.0.0+1 (1.3s)
✓ Fetching releases (0.4s)

🚀 Ready to create a new release!

📱 App: shorebird_test (ab486a37-ft3d-472d-l520-ba1h7e3c3801)
📦 Release Version: 1.0.0+1
🕹️  Platform: android
🐦 Flutter Version: 3.24.1 (3f7041c5e9)

Would you like to continue? (y/N) Yes
✓ Fetching releases (0.9s)
✓ Creating release (0.7s)
✓ Updating release status (0.3s)
✓ Creating artifacts (9.0s)
✓ Updating release status (1.8s)

✅ Published Release 1.0.0+1!
Your next step is to upload the app bundle to the Play Store:
/Users/christofer_henriksson/shorebird_test/build/app/outputs/bundle/release/app-release.aab

For information on uploading to the Play Store, see:
https://support.google.com/googleplay/android-developer/answer/9859152?hl=en

To create a patch for this release, run shorebird patch --platforms=android --release-version=1.0.0+1

Note: shorebird patch --platforms=android without the --release-version option will patch the current version of the app.
</code></pre>
<figure>
<img src="/assets/img/articles/2024-11-07-Shorebird-Code-Push-For-Flutter-Apps/release-added-to-shorebird-console.png">
<figcaption>Release added to Shorebird console</figcaption>
</figure>
<ol start="4">
<li>Installing and running the release shows the default counter example as expected.</li>
</ol>
<figure>
<img src="/assets/img/articles/2024-11-07-Shorebird-Code-Push-For-Flutter-Apps/default-flutter-counter-example-app.png">
<figcaption>The default Flutter counter example app</figcaption>
</figure>
<ol start="5">
<li>Now let's make some small changes to the app.</li>
</ol>
<p>Changing the <code>colorScheme</code> to green.</p>
<pre><code class="hljs language-dart">- colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
+ colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
</code></pre>
<p>Adding a decrement button</p>
<pre><code class="hljs language-dart">+  <span class="hljs-keyword">void</span> _decrementCounter() {
+    <span class="hljs-keyword">if</span> (_counter > <span class="hljs-number">0</span>) {
+      setState(() {
+        _counter--;
+      });
+    }
+  }
</code></pre>
<pre><code class="hljs language-dart">-      floatingActionButton: FloatingActionButton(
-        onPressed: _incrementCounter,
-        tooltip: <span class="hljs-string">'Increment'</span>,
-        child: <span class="hljs-keyword">const</span> Icon(Icons.add),
-      ),

+      floatingActionButton: Row(
+        mainAxisAlignment: MainAxisAlignment.end,
+        children: [
+          FloatingActionButton(
+            onPressed: _decrementCounter,
+            tooltip: <span class="hljs-string">'Decrement'</span>,
+            child: <span class="hljs-keyword">const</span> Icon(Icons.remove),
+          ),
+          <span class="hljs-keyword">const</span> SizedBox(width: <span class="hljs-number">8</span>),
+          FloatingActionButton(
+            onPressed: _incrementCounter,
+            tooltip: <span class="hljs-string">'Increment'</span>,
+            child: <span class="hljs-keyword">const</span> Icon(Icons.add),
+          ),
+        ],
+      ),
</code></pre>
<ol start="6">
<li>Next we will create the patch for the above changes with the <code>shorebird patch</code> command.</li>
</ol>
<pre><code class="hljs language-shell">user@my-mac shorebird_test % shorebird patch android
✓ Fetching apps (0.5s)
✓ Fetching releases (0.3s)
Which release would you like to patch? 1.0.0+1
✓ Fetching aab artifact (0.3s)
✓ Downloading aab (2.2s)
✓ Building patch with Flutter 3.24.1 (3f7041c5e9) (41.2s)
✓ Verifying patch can be applied to release (28ms)
[WARN] Your app contains asset changes, which will not be included in the patch.
    Changed files:
        base/assets/flutter_assets/fonts/MaterialIcons-Regular.otf
Continue anyways? (y/N) Yes
✓ Fetching release artifacts (0.9s)
✓ Downloading release artifacts (4.0s)
✓ Creating patch artifacts (4.2s)

🚀 Ready to publish a new patch!

📱 App: shorebird_test (a1ac6a37-bc3d-459d-b820-bd1f4e3c5391)
📦 Release Version: 1.0.0+1
🕹️  Platform: android [arm32 (87.71 KB), arm64 (84.91 KB), x86_64 (89.81 KB)]
🟢 Track: Production

Would you like to continue? (y/N) Yes
✓ Creating patch (1.7s)
✓ Uploading artifacts (2.8s)
✓ Fetching channels (0.3s)
✓ Promoting patch to stable (0.3s)

✅ Published Patch 1!
</code></pre>
<p>The tool detected that we added a new Material icon that won't be included in the patch.
For our demonstration we choose to continue anyways.</p>
<ol start="7">
<li>Checking the Shorebird console we can see our first patch has been added.</li>
</ol>
<figure>
<img src="/assets/img/articles/2024-11-07-Shorebird-Code-Push-For-Flutter-Apps/patch-added-to-shorebird-console.png">
<figcaption>Patch has been uploaded to Shorebird servers</figcaption>
</figure>
<ol start="8">
<li>Restarting the app to download and apply the patch.
After restarting the app we can confirm that the new changes have been remotely updated successfully.</li>
</ol>
<figure>
<img src="/assets/img/articles/2024-11-07-Shorebird-Code-Push-For-Flutter-Apps/after-patch.gif">
<figcaption>Patched changes has been applied successfully</figcaption>
</figure>
<p>Note that the newly added button's icon is not showing. This is as expected since the Material icon asset is missing.</p>
<h2>Limitations and considerations</h2>
<ul>
<li>
<p>Shorebird can only update Dart code. So native iOS (Objective-C/Swift) and Android (Java/Kotlin) code updates are not supported. For any native code changes like plugins we are still required to prepare a new release build as normal.</p>
</li>
<li>
<p>Currently assets like fonts or images are not supported but are planned to be added. Also some care needs to be considered when changing icons. Since icon assets are tree-shaken when the app is built, you can't patch an app to use a new icon that is not already used somewhere in your app.</p>
</li>
<li>
<p>For now Shorebird only supports Flutter mobile apps (iOS/Android). But Flutter desktop app support is also planned to be added later.</p>
</li>
<li>
<p>Shorebird is not a replacement for existing app stores or other side loading solutions. It is only a tool for making code updates more convenient. You still need to have the app released to your users through an app store or other distribution method.</p>
</li>
<li>
<p>Since new updates need to be downloaded from the Shorebird backend, apps that are exclusively offline are not supported. In fact the shorebird init command will add the required internet permissions to the Android manifest if it's missing.</p>
</li>
<li>
<p>Flutter versions cannot be updated through Shorebird. For example an app released using Flutter 3.24 cannot be patched to use another version of Flutter. Conversely, since Shorebird is using a customized version of Flutter, there might be a small delay until a new Flutter version has been prepared for Shorebird.</p>
</li>
<li>
<p>Both App Store and Play Console have both technical and policy rules that limit what a remote update is allowed to contain. Shorebird is built to comply with the technical limitations, but for policy rules it's up to the developers to make sure they are not broken.
For example we are not allowed to "engage in deceptive behavior" via pushed updates. Like changing the original purpose of the app. Failing to follow the content policies can result in both your Shorebird account and developer account being terminated.
More details can be found at the Shorebird’s <a href="https://docs.shorebird.dev/faq/#does-shorebird-comply-with-play-store-guidelines">Q&#x26;A</a> here.</p>
</li>
</ul>
<p>For more information I recommend checking out the <a href="https://docs.shorebird.dev/">official documentation</a> and the helpful <a href="https://discord.gg/shorebird">Discord server</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introducing UCL Max AltPlay, a turn-by-turn real-time Football simulation]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2024/05/27/Introducing-ucl-max-altplay-a-turn-by-turn-real-time-football-simulation</link>
            <guid>https://engineering.monstar-lab.com/en/post/2024/05/27/Introducing-ucl-max-altplay-a-turn-by-turn-real-time-football-simulation</guid>
            <pubDate>Mon, 27 May 2024 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>At this year's <a href="https://www.linkedin.com/showcase/monstarhacks">MonstarHacks</a>, our goal was to elevate the sports experience to the next level with cutting-edge AI and machine learning technologies. With that in mind, we designed a unique solution for football fans that will open up new dimensions for watching games. We created interactive simulations for live matches that fans will be able to interact with and peer into more details behind the crucial moments in the games.</p>
<h2>Problems We Saw</h2>
<p>Football fans often engage in discussions on “<strong>what-if</strong>” scenarios. They want to explore different scenarios in their heads, talk about them on social media, and even compete with each other. Apart from that, they cherish the memory of their favorite team winning the trophy, feel heartbroken if their team didn’t make it, and ponder what went wrong. These highly emotional experiences can be enhanced in a way that introduces healthy challenges and keeps fans engaged for years to come.</p>
<p>We feel that, as fans, we would like to have a more immersive experience while watching the game and engage in meaningful conversations with our friends. These open up a new social dimension to the sports we enjoy. Being able to explore the “<strong>what-if</strong>” scenario in a novel way is always enjoyable and worth paying for. We believe that it will be particularly interesting to see these interactions between fans, which will drive engagement to the roof.</p>
<h2>Solutions We Found</h2>
<p>We are introducing UCL Max, a companion app concept for fans where not only will they be able to watch the game but also explore the “<strong>what-if</strong>” scenarios. Fans can sign up for a subscription to watch the game, get instant highlights and advanced match facts, interact with AI-powered simulations in <strong>AltPlay</strong>, and compete in the global leaderboard. Let’s dive into more details to see how this works.</p>
<h2>AltPlay</h2>
<p>We use cutting-edge machine learning models to predict when a crucial event happens within a live game. As soon as we get that game footage from the cameras, we can determine the relative positions of the players and various metrics from those moments. All of these data are fed into a pre-trained machine learning model, which can be used to predict alternative behaviors of any player in any moment and position.</p>
<p>We have identified the following parameters to train our models:</p>
<ul>
<li><strong>Timestamp:</strong> represents a single frame of the video.</li>
<li><strong>Object Type:</strong> distinguishes between players and the ball.</li>
<li><strong>Object ID:</strong> unique identifier for each player.</li>
<li><strong>X-Coordinate, Y-Coordinate:</strong> player or ball position on the field relative to the field.</li>
<li><strong>Speed:</strong> the instantaneous speed of the object.</li>
<li><strong>Heading:</strong> direction of movement in degrees.</li>
<li><strong>Ball Possession:</strong> Indicates the player ID with the ball (if any).</li>
<li><strong>Pass Origin:</strong> The player ID of the passer.</li>
<li><strong>Pass Target:</strong> The player ID of the intended receiver.</li>
<li><strong>Pass Type:</strong> Basic classification of the pass.</li>
<li><strong>Action:</strong> Placeholder for basic actions like shots, tackles, etc.</li>
<li><strong>Action Type:</strong> A new column to categorize the actions more specifically: Shot, Tackle, Dribble, Foul</li>
<li><strong>Shot Type:</strong> Ground, volley, header, chip, free kick, etc.</li>
<li><strong>Shot Direction:</strong> Angle relative to the goal or general direction (left, right, center)</li>
<li><strong>Shot Outcome:</strong> Goal, saved, missed, blocked</li>
<li><strong>Tackle Location:</strong> General pitch area (right side, midfield, penalty area, etc.)</li>
<li><strong>Tackle Outcome:</strong> Successful (won possession), unsuccessful, foul</li>
</ul>
<p>We use AWS Lambda, Fargate, and DynamoDB to process the live match events as well as the previous match events. Data aggregations from these services are sent into the Amazon Sagemaker pipeline to generate inferences and train our existing models. We use AWS Lambda to pre-process the video data and perform calculations on the inferred data. AWS Rekognition is used to determine various events in the game, such as passes, tackles, goals, etc.</p>
<p>Here’s a diagram of the AWS stack:</p>
<figure>
<img src="/assets/img/articles/2024-05-27-Introducing-ucl-max-altplay-a-turn-by-turn-real-time-football-simulation/diagram.webp">
</figure>
<p>Using this data, we can predict with confidence when a player makes a successful pass or releases the ball when under pressure. So we incorporate all of this information into a simulation that can be played with each turn based on the actual game. Here’s a formula to determine the pressed player:</p>
<figure>
<img src="/assets/img/articles/2024-05-27-Introducing-ucl-max-altplay-a-turn-by-turn-real-time-football-simulation/calculation.webp">
</figure>
<h2>How It Works</h2>
<p>Users of the UCL Max app can watch the live broadcast of the games and access the moments of the match that can be simulated. When they initiate a simulation, they are presented with a layout of the initial conditions of that moment. The real match path is already highlighted, and the users have the option to play an alternate game. Tapping on the player's position will reveal a possible path that the player can take, and based on the target player’s statistics, we simulate what’s going to happen.</p>
<figure>
<img src="/assets/img/articles/2024-05-27-Introducing-ucl-max-altplay-a-turn-by-turn-real-time-football-simulation/simulation.webp">
</figure>
<p>After every turn, the positions of the players will be updated based on the ML inference, and the user will be able to run the next step of the simulation. Once completed, the user will be able to finalize their simulation and play it back. Based on events, for example, a goal scored, if the user can simulate a goal event on an alternate path, then they will be awarded points. This simulation can be shared on social media outlets.</p>
<p>The more points an individual collects, the higher up they go on the leaderboard. We can decide the winner of the leaderboard after a season and offer prizes in collaboration with the sponsors.</p>
<h2>Technologies We Used</h2>
<p>Here’s a list of the technologies we used to create our simulation pipeline:</p>
<ul>
<li><strong>AWS Lambda -</strong> Data preprocessing, mathematical calculations for events such as player pressure, goal probability, sprint speed, duration, etc.</li>
<li><strong>AWS Fargate -</strong> Serverless cluster computing.</li>
<li><strong>AWS API Gateway -</strong> Provide access points to ingest data and inference output.</li>
<li><strong>AWS DocumentDB -</strong> High-performance JSON database to store parameter data.</li>
<li><strong>AWS DynamoDB -</strong> Database to store various data</li>
<li><strong>AWS S3 -</strong> Model archival, JSON data archival, caching.</li>
<li><strong>Amazon SageMaker -</strong> AI and ML pipeline.</li>
<li><strong>AWS Rekognition -</strong> Computer vision technologies, video analysis, and image recognition.</li>
<li><strong>AWS Amplify -</strong> Web and mobile app solution, access to other AWS services and streaming.</li>
<li><strong>AWS SQS -</strong> Messaging service between other AWS services.</li>
</ul>
<h2>Why It’s Unique</h2>
<p>Our AI-powered AltPlay simulation explores the alternate reality fans often crave. Also, our solution uniquely enhances the user experience and long-term engagement with sports. We believe this novel technology, social aspects, and user retention make our solution innovative.</p>
<p>The main target of our solution is the fans who enjoy sports and are very passionate. Also, the amount and scope of the data we designed to process can be repurposed to enhance team management and player training improvement as well.</p>
<p>We found that the services we are offering to our fans are unique, as we provide a way to watch the game and interact with the highlights in a completely different way. With the emergence of AR/VR platforms, we think that our solution is particularly suited to enhance the experience of watching games and discussing them with friends.</p>
<h2>How It Impacts Us</h2>
<p>Users will be able to satisfy their curiosity about how they feel about the game and participate in healthy challenges among friends. This will significantly improve the sports experience and add a new dimension to social aspects. Users will get to learn more about their favorite teams and how the players work together. We believe that this will be beneficial for the fans of the sport.</p>
<h2>Scalability and Business Opportunities</h2>
<p>We use a myriad of AWS services to ensure that our product remains robust and scalable from a technical perspective. Since the football fanbase is one of the largest in the world and spans across continents, we have a potential userbase that is global.
Official live broadcasts of football matches are already paid for. By subscribing to UCL Max, users will be able to enjoy the live broadcast from anywhere. We believe that fans will pay the right amount of money for live broadcasts and additional perks and features that open up a new dimension of sports. Leaderboard winners of previous seasons may be rewarded with match tickets for the next season and may receive merchandise promoting the sponsors. This also creates scope for targeted advertising for users. A monthly subscription or one-time season pass can be possible payment strategies.</p>
<p>Our platform and ideas are not limited to football. We can implement similar strategies to other sports such as American Football, basketball, etc. Those leagues have strong fanbases; if they are persuaded to realize the value of our simulations, we could even host betting tournaments where it’s legal. The possibilities are endless.</p>
<h2>Challenges We Faced</h2>
<p>Our primary challenge was to figure out how we could satisfy the questions of what-ifs for our fans and, in turn, make that competitive. Also, we needed something economically viable. When deciding on how our simulation will work, we found that if we allow the user to simulate a whole match, this is going to be a completely different thing, like a video game. Then we thought instead of simulating the whole game, we would just play the highlights and let them simulate the specific scenarios. It felt like the right direction since we don’t talk about all the moments in a game but only some interesting moments. The next step was to determine what our simulation would look like inside the app. After a lot of brainstorming sessions, we found our inspiration and designed it.</p>
<h2>Final Words</h2>
<p>MonstarHacks 2024 presented some real challenges this year. We were amazed to learn about the progress in sports that AWS had unlocked. We were so influenced by the BundesLiga Match Facts that we designed our solution based on those ideas, and AWS provided the perfect tools to aid in our solution. Overall, it was really fun and engaging, and we learned a lot from our experience and team efforts. We are looking forward to the next MonstarHacks. Until then, best wishes to all.</p>
<p><em>Article Photo by <a href="https://www.freepik.com/free-photo/victory-excited-happy-family-watching-sport-match-championship-couch-home_17248535.htm#fromView=search&#x26;page=1&#x26;position=6&#x26;uuid=aff3be6d-eba9-489c-a9f6-7559c4cc029a">Image by master1305 on Freepik</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Agorametrics: Data democratisation from Sports Science for the benefit of all ]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2024/05/23/Agorametrics-Measure-Learn-Win-Together</link>
            <guid>https://engineering.monstar-lab.com/en/post/2024/05/23/Agorametrics-Measure-Learn-Win-Together</guid>
            <pubDate>Thu, 23 May 2024 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>As part of Monstarlab's 4th edition of the internal hackathon, <a href="https://www.linkedin.com/showcase/monstarhacks">MonstarHacks</a>, the theme was to leverage cutting-edge technologies to innovate within the sports industry by developing solutions that enhance fan engagement, optimise athletic performance, and transform sports venues into smart environments.</p>
<p>Together with Amazon Web Services, the world’s most comprehensive and broadly adopted cloud, this event allowed us to explore over 200 fully featured services from data centers globally including AWS’s Generative AI and Machine Learning technologies.</p>
<h2>The Team</h2>
<p>![Team Photos](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/TheTeam.webp</p>
<p>As a team of 2 architects, Ross and Graham were like a band with 2 bassists.
They were cool, but they needed some drums.</p>
<p>So they drafted in long-time collaborator and drinking buddy, Ian Gifford from Atom Bank (UK FinTech), who took their vision and ran with it in Figma for mockups and ident.</p>
<p><em>Drum &#x26; Bass.</em></p>
<h3>The Problem</h3>
<p>We decided to focus on <strong>Performance Analytics (Athlete View)</strong>, looking at holistic and historic sports data to try and consider insights that could improve athletic quality and wellbeing.</p>
<ul>
<li>Develop solutions centred around athlete data including tracking for performance, health, and safety.</li>
<li>Employ machine learning to predict in-game strategies and prevent injuries.</li>
<li>Generate real-time insights that can influence game-day decisions and strategies.</li>
<li>Enhance scouting and recruitment processes.</li>
<li>Predictive Analytics and Insights</li>
</ul>
<p>As we’ll see, this proved more difficult in reality… The business case proposed turns this hurdle into an opportunity.</p>
<h3>Staying close to home</h3>
<p>All members of Team 5 grew up within a few miles of Newcastle United Football Club and are all lifelong fans.</p>
<p>![NUFC Crest](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/NUFC.webp</p>
<p>For us, Newcastle as a place can’t be easily separated from Newcastle the club. It’s in our DNA, so choosing football was easy for us.</p>
<p>The 2023/24 season for Newcastle has been a nightmare due to the amount of injuries suffered by players.</p>
<p>This injury problem can probably be directly related to overachieving in the 2022/2023 season and having a
European competition to compete in with a limited squad. Our success has been our demise.</p>
<p>![Data table from BBC Sport showing NUFC incurring the most injuries and days lost this season](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/NUFCInjuries.webp</p>
<p>We thought - is there a way to utilise publicly available data along with historical injury data to enable a way to help predict the likelihood of an injury reoccurring?</p>
<p>Is there a correlation between different sources of data available?</p>
<ul>
<li>Fixtures &#x26; Results</li>
<li>Travel Distances</li>
<li>Weather</li>
<li>Injury history</li>
</ul>
<p>We decided to explore this first.</p>
<h3>Public Data</h3>
<p>Data on fixtures, location, players and injuries is available along with other sources such as Weather which could be ingested - however this data is difficult to obtain in bulk.</p>
<p>In addition, it turns out that injury data publicly available is under-reported compared to underlying data - “The overall injury cumulative incidence of 0.63 injuries per player-season was substantially lower than in reports using other data collecting methods (ranging from 1.5 to 2.5 injuries per player-season)”.</p>
<p>![Publicly available injury history from transfermarkt website](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/InjuryHistory.webp</p>
<p>Predictive modelling of football injuries is possible given sufficient data, however the availability of the data is limited given most clubs who invest heavily in Sports Science have commercial reasons to keep that data private.</p>
<p>Manchester City Academy’s Head of Sports Science, for example, oversees the data collection of nine teams and over 200 players, from Under-9 through to the Under-23s.</p>
<h2>Data Ownership</h2>
<p>So the question becomes - who owns the data? UK Players’ performance data could be considered to be personal data, which requires players’ explicit consent for clubs and leagues to use. The argument is that this consent may not yet been given, and that ownership by clubs and leagues has just been assumed.</p>
<p>![Plot of Goals per Game vs Shot Accuracy in Europe's Top 5 Leagues](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/ShotAccuracy.webp</p>
<p>It’s not yet something clubs or leagues have universally tried to claim ownership of, with a view to commercialising, and this is something which is currently being discussed, however in the US it’s different, as both the national American Football (NFL) and Basketball (NBA) leagues have a level of ownership over this data, more so in the NFL.</p>
<h2>Money is shared, but not data or insights</h2>
<p>While top clubs have the finances to invest heavily into Sports Science, this investment is only available down to tier 4 or 5 of the EFL Pyramid and although TV Revenue does flow down, although only so far, the insights available from Sports Science does not.</p>
<p>![Distribution of TV Revenue by tier in the English Football LEague](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/TVRevenue.webp</p>
<p>This seems like a waste of potential, and likely hampers efforts to grow better footballers at lower levels in the English football community.</p>
<h2>Non-commercial benefits of sharing data</h2>
<p>Given the concerns mentioned around data ownership and commercialisation - how else could the sharing of data benefit the members of the Football Association themselves directly or indirectly?</p>
<p>The French National Agency for Sport, together with the National Institute for Sport, Experience and Performance (INSEP) and the General Directorate for Sport, have developed the Sport Data Hub - FFS.</p>
<p>![French Football Federation Logo](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/FFF.webp</p>
<p>The project was born in 2020 with the idea of boosting the individual and collective performance of French sport in the run-up to the Olympic Games in Paris 2024. It consists of the creation of a collaborative tool for all those involved in the sports movement, (federations, athletes, coaches, technical teams, institutions and researchers), to share data that allows for aggregate comparative analysis at national and international level.</p>
<p>![French Olympic Committee Logo](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/Olympic.webp</p>
<p>Who would benefit from the sharing of data, or at least these insights from data, across the entire English Football League and <em>who would want this</em>?</p>
<h2>Who wants this?</h2>
<p>Below are Mission statements from both the FA (Football Association) and the PFA (Professional Footballer Association). The FA governs the UK National Game with the PFA providing Union and wellbeing support for professional players in the UK.</p>
<blockquote>
<p>Promoting health and wellbeing, and the power of teamwork, with our new strategy for 2020- 2024, we have a plan for all. Positively impacting every community across the country. Everyone can win if we build on the progress made over the previous four seasons. We’ve moved forward in every area, modernising our organisation to serve a game for all. We have a great platform to build on. We will keep pushing forward. It’s time to deliver real change.</p>
</blockquote>
<p><em>The Football Association (FA)</em></p>
<blockquote>
<p>We believe in the unifying power of football in society, and are committed to empowering footballers to recognise their value as people, not just players. We protect players' rights, represent their views and provide support through a wide variety of educational, financial and wellbeing services.</p>
</blockquote>
<p><em>The Professional Footballer Association (PFA)</em></p>
<h2>FA Vision</h2>
<blockquote>
<p>From team managers and club secretaries through to players and parents, Whole Game is for the whole of grassroots football.
<br><br>
Whatever your role in the game and whatever you do, Whole Game can help you to do it faster and more effectively.
<br><br>
You’ll find club data – such as player contacts, qualifications, discipline records and so much more all in one place, which is great news for managers and club secretaries when it comes to club admin and monitoring player availability.
<br><br>
You only have to input a player’s details once – and the system fully integrates with both Full-Time and the Matchday app to make access and management of information simple.</p>
</blockquote>
<p>![The FA Vision](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/Vision.webp</p>
<h2>The problem revealed</h2>
<p>At this point, we realised the problem to be solved wasn’t looking at the correlations between different data sets. This could come later.</p>
<p>The actual problem was how to coordinate an ecosystem of different stakeholders, with different levels of access to data, to ensure that insights from the most privileged could benefit the least privileged.</p>
<blockquote>
<p>How could Professional UK Football and Grass Roots football share insights to benefit the national game in a long-term and sustainable way.</p>
</blockquote>
<p>![Pivot](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/Pivot.webp</p>
<p>Pivoting Early, but read on for our exploration of this problem and a proposition that supports our solution.</p>
<h2>The Solution</h2>
<p>![AgoraMetrics Logo](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/Logo.webp</p>
<p><em>Measure, Learn, Win. Together</em></p>
<blockquote>
<p>At AgoraMetrics<sup>*</sup>, our mission is to democratise the use of advanced data analytics and machine learning in football, making cutting-edge sports science accessible to clubs of all sizes and resources all the way down to grass roots level.
<br><br>
We're committed to levelling the playing field by providing smaller clubs with the insights and tools previously available only to top-tier teams.
<br><br>
By joining our ecosystem of data sharing, clubs can enhance player performance, prevent injuries, and optimise team and individual management strategies, all while supporting the growth and competitiveness of football at every level.</p>
</blockquote>
<p><em><sub>* Inspired by the Greek "agora", the central public space in ancient Greek city-states, symbolising the gathering and sharing of ideas and now, sports data analytics.</sub></em></p>
<h2>The AgoraMetrics Proposition</h2>
<p>![The AgoraMetrics proposal](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/Proposition.webp</p>
<p>The AgoraMetrics proposition is aimed initially at the UK Football Association, the body that holds accountability for governance of both Professional and Grass Roots football in England.</p>
<p>The benefits outlined in this section detail granular positive outcomes for English football at all levels, but at a high level our mission is to Win an International competition by 2030.</p>
<p>We would expect <strong>returns on investment</strong> to include:</p>
<ul>
<li>Positive outcomes at Grass Roots level between years 1-3</li>
<li>Positive Professional Football outcomes from years 2-5</li>
<li>Positive England Team outcomes within 4-7 years</li>
<li>A self-sustaining AgoraMetrics Service within years 1-4</li>
</ul>
<p>![World Cup](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/WorldCup.webp</p>
<p>These outcomes assume 4 considerations to <strong>achieve our mission</strong>:-</p>
<ul>
<li>AgoraMetrics is funded by the FA from 2024-2030</li>
<li>AgoraMetrics is marketed to and adopted by the Grass Roots football community extensively to drive adoption</li>
<li>AgoraMetrics quantitatively improves player quality at every level of the UK game</li>
<li>AgoraMetrics grass roots players take their career to professional level enabling progression ( and outcomes measurement ).</li>
</ul>
<p>![AgoraMetrics Stakeholders](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/Stakeholders.webp</p>
<h2>Solution Design</h2>
<h3>Solution Landscape</h3>
<p>![Solution Landscape](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/SolutionLandscape.webp</p>
<p>While data would initially flow from professional clubs down to grass roots level, data would be fed back into the system to benefit teams and players at all levels.</p>
<h3>Solution Detail</h3>
<p>![Solution Detail](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/SolutionDetail.webp</p>
<p>The solution itself would comprise several Data Science models produced for the benefit of the community along with an app to onboard coaches, players and data which would feed back into the data layer.</p>
<h3>Data Mesh and ML Factory</h3>
<p>![Data Mesh and ML Factory](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/DataMesh.webp</p>
<p>Several Amazon Web Services would be utilised to provide the back end Data mesh and Machine Learning models which would be exposed via secure APIs for use by the client application.</p>
<h3>Serverless Architecture</h3>
<p>![Serverless Architecture](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/Serverless.webp</p>
<p>A Serverless Architecture would be utilised to provide the Web Application layer to prioritise performance and cost effectiveness and allow scalability.</p>
<h2>The Agorametrics Application</h2>
<h3>Design Influence</h3>
<p>We were interested in using a colour scheme that resonated with players, coaches and anyone with a wider interest in football.</p>
<p>With this design principle in mind, we couldn’t think of a better way to start than to picture St. James’ Park (Newcastle United’s Stadium) on a Sunny Day.</p>
<p>![Colour Scheme](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/ColourScheme.webp</p>
<p>This image was used to derive a palette that could be included in a design system and includes tones for components such as alerts (orange / yellow from stewards jackets) as well as primary ident colours taken from the pitch and the sky.</p>
<p>Our logo is intended to convey the notion of team sport in general, with data points composing the “ball”. This ensures we’re not locked into “Football” as a sport.</p>
<h3>User Features</h3>
<p>We included a finite set of App Features to discuss for the prototype. These allowed us to explore both the benefits of possible insights the Machine Learning and Data science might hunt for, and also how players and coaches might use these features.</p>
<p>![User Features](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/Features.webp</p>
<h3>User Interface Design</h3>
<p>![Green user interface](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/UserInterfaceGreen.webp</p>
<p>![Blue user interface](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/UserInterfaceBlue.webp</p>
<h2>Financials &#x26; opportunity</h2>
<p>![Financial forecast](/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/Financials.webp</p>
<p>The AgoraMetrics product aims to provide as wide a range of returns on investment as possible.</p>
<p>In this model, “Data Value” could be compared with the Social Media model of “You are the product” from an end user perspective.</p>
<p>In AgoraMetrics case however, rather than monetising this data for corporate profit, we aim to drive financial gains back into Grass Roots initiatives ensuring Social Return on Investment which will contribute directly to the FA’s Mission.</p>
<p>The decision to pitch this to the FA has a leverage value, in so far as the FA governs Pro Clubs in the UK, and controls the throughput of revenue streams such as TV Rights. We would therefore expect that the FA organisation has the necessary ability to promote and project this initiative.</p>
<p><img src="/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/Opportunity.webp" alt=""></p>
<h2>Future Opportunities</h2>
<p><img src="/assets/img/articles/2024-05-23-Agorametrics-Measure-Learn-Win-Together/FutureOpportunities.webp" alt=""></p>
<h2>Conclusion - The Hackathon Experience</h2>
<p>While we both entered the weekend hoping to create a tangible working prototype utilising some cool functionality from Amazon Web Services - the time spent trying to access and retrieve data for our initial idea meant we had to pivot to a more conceptual approach.</p>
<p>We realised that the main problem with our idea was not the data itself - this exists and is studied in depth, but only by those who have the funds and resources to do so. Top clubs have entire teams dedicated to Data Science and utilising the latest and greatest in sports analytics - but, for good reasons, this data is held onto and not shared. However, the insights and models which can be obtained from this data can benefit lesser teams - and in return, more data can be utilised by the system to benefit all.</p>
<p>It is this democratisation of data, insights and models which we explored and feel fits well into the visions of both the FA and PFA and could potentially be implemented across many different sports communities.</p>
<p>We both look forward to the next Hack!</p>
<p><em>Article Header from Ross Davidson, Article Photos from <a href="https://www.nufc.co.uk/">NUFC</a>, <a href="https://www.transfermarkt.co.uk/">transfermarkt</a>, <a href="https://olympics.com/ioc/france">French Olympic Committee</a>, <a href="https://uk.fff.fr/">French Football Federation</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[AthletePro - Empowering the next generation of champions; parents first approach!]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2024/05/23/AthletePro-Empowering-the-next-generation-of-champions</link>
            <guid>https://engineering.monstar-lab.com/en/post/2024/05/23/AthletePro-Empowering-the-next-generation-of-champions</guid>
            <pubDate>Thu, 23 May 2024 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Imagine you are a parent and want to ensure your child has the right set of guidance to explore different sports and choose any to specialize in. You dream of seeing your child compete in the Olympics in 15 years! The question is where to start!</p>
<p>In today’s day and age, there is hardly any platform where parents can get guidance to ensure their children are equipped to pick up any sports and get better at it. Be it recreational or professional, we don’t see this often. Moreover, we have seen athletes drop out of their game because of a lack of training and guidance. The cost involved and the difficulty in finding the best trainers in their localities further complicate the situation.</p>
<p>We, team Ready Player Three, wanted to address this problem in this year's <a href="https://www.linkedin.com/showcase/monstarhacks">MonstarHacks</a>. Let us tell you readers how to approach the hackathon problem and how we tried to solve the issue.</p>
<p>![AI Generated Image](/assets/img/articles/2024-05-23-AthletePro-Empowering-the-next-generation-of-champions/abstract-intro.webp</p>
<p>MonstarHacks partnered with Amazon Web Services (AWS), to provide our teams with challenges in 3 solution areas: Fan360, Performance Analytics, and Smart Venues. Our team made out of Saad Bin Amjad, Tanveer Hassan, and Faisal Islam, choose the Performance Analytics ("Athlete View") category aiming to help athletes and their families navigate their sports journey more effectively. Leveraging AWS technology, especially Generative AI, we aimed to build a solution that would make a tangible difference in the sports world.</p>
<p>We named our application AthletePro, which plans to revolutionize the sports industry with an AI-driven platform that provides hyper-personalized roadmaps for athletes and parents from recreational to elite levels, empowering the next inclusive generation of champions.</p>
<h2>Solution Overview</h2>
<p><div class="embed-container">
      <iframe
          src="https://www.youtube.com/embed/Ahz_VbisdZk"
          width="700"
          height="480"
          frameborder="0"
          allowfullscreen="">
      </iframe>
    </div>
    </p>
<p>AthletePro offers a comprehensive solution to empower athletes and their families. Even before signing up for the platform, parents are given a tour of the app features and what it can offer them and their children in terms of sports. This includes sneak peeks at coaches, trainers, events, and the financial aspects involved in getting the athletes up to speed. There are curated FAQs that enable parents to make the right call in terms of exploration or being onboarded to the application.</p>
<p>Then we ensure that parents can sign up easily. We first focused on <strong>Personalized Onboarding</strong>. This feature enables parents to create profiles for themselves and their children by answering a series of questions. The questions range from their location, age, sports they want to explore, end goals if any, and budget. This helps the application tailor recommendations accordingly, as this information becomes part of the knowledge base going forward.</p>
<p>Parents of special needs children can provide additional information to receive more personalized recommendations.</p>
<p>Similar to other ed-tech platforms but specifically for sports, we showcase tailored lessons and suggest sporting activities. To support this feature, we introduced the onboarding of coaches, trainers, and organizers into our CMS-styled platform powered by Strapi. We ensure it is easily accessible, allowing users to enter the system with their certifications and facilities, which will also become part of the knowledge base going forward.</p>
<p>Businesses in the sports industry, such as gyms, training facilities, and equipment suppliers, can easily connect with athletes and parents through AthletePro. The bigger plan for this feature is that when governments, sports organizations, and universities are onboarded, they can sponsor athletes and their families so they can pursue their sporting journey.</p>
<p>Additionally, we understand that when AI generates any roadmap for vulnerable groups in society, such as children, there should be accountability and transparency. It is crucial that we provide accurate roadmaps. Therefore, we have proposed and showcased a notification system for connected coaches and trainers to first approve the content before presenting the roadmap to parents or children.</p>
<p>We also recognize that not everything can be taught or practiced indoors. The courses are designed so that AthletePro provides exclusive content and perks unlocked at specific stadiums or training facilities, enhancing engagement. This means the application can detect your location and then unlock the corresponding training guidance or instructions.</p>
<p>We understand that not everything can be taught or practiced indoors. So the courses are designed so that AthletePro provides exclusive content and perks unlocked at specific stadiums or training facilities, enhancing engagement. This means the application can detect your location and then unlock the corresponding training guidance or instructions.</p>
<p>![Mock Design](/assets/img/articles/2024-05-23-AthletePro-Empowering-the-next-generation-of-champions/athele-pro-trainer.webp</p>
<h2>Target Users and Revenue Model</h2>
<p>AthletePro caters to a diverse range of users across the sports ecosystem, including athletes, parents/guardians, coaches/trainers, government sports agencies, universities and colleges, and local businesses.</p>
<p>![Revenue Generation](/assets/img/articles/2024-05-23-AthletePro-Empowering-the-next-generation-of-champions/business.webp</p>
<p>To address the needs of these diverse stakeholders, we have developed a solid revenue model for the app based on subscription-based revenue, partnerships, and sponsorships. Additionally, we engage in ethical data monetization through partnerships with research institutions, sports analytics firms, and market research companies. We believe AthletePro has the potential to expand into new markets domestically and internationally by adapting its platform to cater to different sports, cultures, and regions.</p>
<h2>Engineering Approach: Seamlessly Integrating the RAG Framework for Contextual Guidance</h2>
<p>We emphasized heavily how easy this app should be for parents and athletes alike. We understand that with any GenAI-backed application, most of the time, we resort to chatbots. However, chatbots won't work here, as not everyone is suited to ask the right questions at the right time. Instead, we've innovatively integrated the RAG framework into the app's architecture, embedding contextual guidance seamlessly within the app journey itself. This user-centric approach not only enhances the overall user experience but also reinforces AthletePro's commitment to accessibility and inclusivity in sports technology.</p>
<p>Leveraging the retrieved data from the knowledge bank, AthletePro dynamically generates system messages that resonate with the user's current context and stage in their athletic journey. These messages are strategically woven into the app journey, serving as subtle cues that guide users towards relevant actions and decisions without overwhelming them with explicit prompts or queries.</p>
<p>The final piece of the puzzle involves generating prompts and recommendations based on the contextual cues provided by the system messages. AthletePro's backend formulates queries for our language model systems (LLMs), such as Anthropic's Claude in Amazon Bedrock, drawing upon the user's profile, preferences, and journey stage. These queries are designed to extract actionable insights and deliver tailored guidance that aligns with the user's unique circumstances.</p>
<p>![AI Flow](/assets/img/articles/2024-05-23-AthletePro-Empowering-the-next-generation-of-champions/ai-flow.webp</p>
<h3>Core Technologies</h3>
<ul>
<li>Strapi CMS</li>
<li>Amazon Bedrock</li>
<li>Amazon EC2</li>
<li>Amazon S3</li>
<li>Amazon RDS</li>
<li>Docker</li>
<li>Github</li>
<li>Prototyping Tools</li>
</ul>
<h3>RAG Framework Utilization</h3>
<p>![User Flow](/assets/img/articles/2024-05-23-AthletePro-Empowering-the-next-generation-of-champions/user-flow.webp</p>
<p>In the user flow diagram, the process begins with Sarah and her mother signing up for AthletePro. Sarah shares her interest in volleyball along with her preferences and any constraints. AthletePro’s system then uses this information to create a personalized roadmap recommendation. This recommendation is sent to local trainers, who review and adjust it as needed before returning it to Sarah’s mother, completing the feedback loop. This user flow demonstrates how AthletePro seamlessly integrates user data, AI-generated recommendations, and real-time communication through its backend architecture.</p>
<h2>Challenges Faced</h2>
<p>One significant challenge we encountered during the hackathon was defining the scope of our idea. AthletePro had the potential to benefit various stakeholders, including athletes, parents, coaches, government agencies, and businesses. We wanted to showcase the platform's capabilities while ensuring clarity and focus. Ultimately, we decided to center our solution around the role of parents, recognizing their pivotal role in supporting young athletes.</p>
<p>To overcome this challenge, we proactively engaged with parents, conducting interviews to gain insights into their perspectives, motivations, and pain points regarding their children's athletic pursuits. By listening to their experiences, we were able to refine our solution and tailor it to address the specific needs of parents navigating their children's sports journey.</p>
<p>Additionally, we faced difficulties in accessing Anthropic's Claude2 model, a crucial subscription service, due to a waiting period and regional restrictions. To mitigate this issue, we improvised by using Amazon's Titan instead of our own knowledge base of parents and athletes. AWS Bedrock only supports Anthropic's models for using knowledge bases for now. Although we set it up, we could not test it due to this limitation.</p>
<h2>Conclusion</h2>
<p>AthletePro represents a groundbreaking innovation in the sports industry, offering personalized guidance, resources, and support to athletes and parents of all backgrounds and abilities. By leveraging technology and a commitment to inclusivity, AthletePro aims to empower athletes to reach their full potential and foster a more vibrant sports community. As we continue to iterate and expand our platform, we look forward to revolutionizing the sports industry and creating a brighter future for athletes everywhere.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/man-riding-green-racing-wheelchair-oyZalm8fvS4">Seth Kane</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Bridging the Gap: Revolutionizing the Sports Enjoyment for the Sensory-Disabled Individuals with AWS-Powered SportsBuddy]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2024/05/22/Revolutionizing-Sports-Enjoyment-for-Sensory-Disabled-Individuals-in-MonstarHacks-2024</link>
            <guid>https://engineering.monstar-lab.com/en/post/2024/05/22/Revolutionizing-Sports-Enjoyment-for-Sensory-Disabled-Individuals-in-MonstarHacks-2024</guid>
            <pubDate>Wed, 22 May 2024 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>The excitement of a game highly depends on the commentary that describes the in-game events. However, for individuals with sensory disabilities, especially visual or hearing impairments, enjoying sports events can be challenging. Our solution was developed as part of the 4th Edition of <a href="https://www.linkedin.com/showcase/monstarhacks">MonstarHacks</a> which is an internal hackathon hosted by Monstarlab where one of the goals was to engage fans in sports. We developed a solution to make sports enjoyable for all without any limits, especially for sensory-disabled individuals. By bridging this sensory gap, our solution — <em>SportsBuddy</em> is proposing a new way on how to experience sports, making it both accessible and enjoyable at the same time for everyone, regardless of their abilities.</p>
<h2>Problem Statement</h2>
<p>Sports fans with visual and hearing impairments face challenges in enjoying game events in detail. Visually impaired fans face difficulties in understanding in-game play, and on the other hand, hearing-impaired fans don’t get thrilled by the live commentary. These difficulties not only take out the excitement but also makes them feel that they are not part of the game.</p>
<h2>The Solution</h2>
<p><em>SportsBuddy</em> is a revolutionary platform that opens a new door for sports enjoyment for individuals with sensory disabilities, especially with visual or hearing impairments. <em>SportsBuddy</em> offers detailed commentary for blind individuals and sign language features for deaf individuals, ensuring an immersive sports experience for all.</p>
<p>![<em>SportsBuddy Mobile App</em> Flow Chart](/assets/img/articles/2024-05-22-Revolutionizing-Sports-Enjoyment-for-Sensory-Disabled-Individuals-in-MonstarHacks-2024/sportsbuddy-main-promo.webp</p>
<figcaption>Figure: SportsBuddy Mobile App</figcaption>
<p>From our research, we found the challenges faced by individuals with sensory disabilities when accessing sports commentary. This involved researching the potential users, and their preferences, and identifying key pain points.</p>
<p>![<em>SportsBuddy</em> Mobile App Screens](/assets/img/articles/2024-05-22-Revolutionizing-Sports-Enjoyment-for-Sensory-Disabled-Individuals-in-MonstarHacks-2024/sportsbuddy-all-screens.webp</p>
<figcaption>Figure: SportsBuddy Mobile App Screens</figcaption>
<p>By using the power of AWS, we used a range of cutting-edge technologies to build our solution.</p>
<h2><em>SportsBuddy</em> Flow Chart</h2>
<p>![<em>SportsBuddy</em> Flow Chart](/assets/img/articles/2024-05-22-Revolutionizing-Sports-Enjoyment-for-Sensory-Disabled-Individuals-in-MonstarHacks-2024/sportsbuddy-diagram-flow.webp</p>
<figcaption>Figure: Flow Chart of the Solution</figcaption>
<h2>How does <em>SportsBuddy</em> help visually impaired fans?</h2>
<p><em>SportsBuddy</em> provides a detailed narration of sports events, allowing visually impaired fans to follow the in-game action on the playground through audio commentary which includes descriptions of player movements, game strategies, and key moments, and even any fun moments happening on the fields.</p>
<p>It also offers audio descriptions of visual feeds such as player shirts, team formation, etc., helping visually impaired fans visualize the game in their minds and stay fully connected to the game.</p>
<h3>Smart Switch</h3>
<p><em>SportsBuddy</em> uses descriptive onscreen statistics, including leaderboards, to surpass the limited coverage offered by the regular standard commentary system. Users can disable this feature if they wish to follow the match commentary only. However, enabling this feature ensures that the audience remains informed of all the key moments during the match. If an exciting event occurs, such as a goal, foul, or a red card, SportsBuddy’s smart switch feature seamlessly transitions to that event and resumes the statistics analysis afterward.</p>
<p>![Architectural diagram for generating detailed commentary](/assets/img/articles/2024-05-22-Revolutionizing-Sports-Enjoyment-for-Sensory-Disabled-Individuals-in-MonstarHacks-2024/sportsbuddy-architectural-diagram-blind-tailored-commentary.webp</p>
<figcaption>Figure: Architectural diagram for generating detailed commentary[1][2]</figcaption>
<h2>How does <em>SportsBuddy</em> help auditory-impaired fans?</h2>
<p><em>SportsBuddy</em> offers AI-based sign language commentary for hearing-impaired fans. Those fans will follow the game action through visual interpretation, which includes sign language interpretation of game commentary, match analysis, and key moments, to ensure an immersive sports experience.</p>
<p>Apart from the sign language commentary, <em>SportsBuddy</em> offers text-based commentary for those who prefer to follow the game through written match events with the descriptive text of game events, player performances, match analysis, etc.</p>
<p>It will show notifications on any important match events during the match time, for example, goals, fouls, cards, penalties, halftime, etc. It ensures that a hearing-impaired fan stays informed of all the key moments of a game.</p>
<p>The sign language avatar[3] is movable, so users can drag the avatar to their preferred place for optimal viewing without missing any in-game key moments. Also, users have the ability to disable the sign language avatar if they are not interested. We are planning on localizing the sign language feature to target a broader user base.</p>
<p>![Architectural diagram for generating sign language commentary](/assets/img/articles/2024-05-22-Revolutionizing-Sports-Enjoyment-for-Sensory-Disabled-Individuals-in-MonstarHacks-2024/sportsbuddy-architectural-diagram-deaf-sign-language.webp</p>
<figcaption>Figure: Architectural diagram for generating sign language commentary[4]</figcaption>
<h2>Technology Used</h2>
<p>To develop our solution we have used several technologies including Amazon Services.
We’ve listed them below.</p>
<p><strong>Flutter</strong>: Flutter is an open-source framework by Google for building beautiful, natively compiled, multi-platform applications from a single codebase. We’ve developed our application <em>SportsBuddy</em> using the Flutter framework for Android and iOS mobile devices.</p>
<p><strong>Amazon S3</strong>: Amazon Simple Storage Service (Amazon S3) is an object storage service that offers industry-leading scalability, data availability, security, and performance. We’ve used Amazon S3 for storing user data.</p>
<p><strong>DynamoDB</strong>: Amazon DynamoDB is a fully managed NoSQL database service that provides fast and predictable performance with seamless scalability. We’ve used DynamoDB for storing the session memory, statistics feed, sign language avatar, etc.</p>
<p><strong>AWS Lambda</strong>: AWS Lambda is a compute service that runs your code in response to events and automatically manages the compute resources, making it the fastest way to turn an idea into a modern, production, serverless application. We’ve used this cost-efficient service mostly for the backend processing.</p>
<p><strong>Amazon Rekognition</strong>: Amazon Rekognition is a cloud-based image and video analysis service that makes it easy to add advanced computer vision capabilities to any application. The service is powered by proven deep-learning technology. We’ve used Amazon Rekognition and other services like Kafka for real-time data processing.</p>
<p><strong>Amazon Polly</strong>: Amazon Polly is a cloud service that converts text into lifelike speech. It supports multiple languages and includes a variety of lifelike voices. We’ve used Amazon Polly for generating voice commentary.</p>
<p><strong>Amazon Transcribe</strong>: Amazon Transcribe is an automatic speech recognition service that uses machine learning models to convert audio to text. You can use Amazon Transcribe as a standalone transcription service or add speech-to-text capabilities to any application. We’ve used Amazon Transcribe for extracting text from live commentary.</p>
<p><strong>Amazon Bedrock</strong>: Amazon Bedrock is a fully managed service that makes high-performing foundation models (FMs) from leading AI startups and Amazon available for your use through a unified API. You can choose from a wide range of foundation models to find the model that is best suited for your use case. We’ve leveraged the power of Amazon Bedrock to facilitate our solution better.</p>
<p><strong>Amazon SageMaker</strong>: Amazon SageMaker is a fully managed machine learning (ML) service. With SageMaker, data scientists and developers can quickly and confidently build, train, and deploy ML models into a production-ready hosted environment. We’ve leveraged the power of Amazon SageMaker to facilitate our solution better.</p>
<h2>Impact &#x26; benefits</h2>
<h3>Impact</h3>
<p><em>SportsBuddy</em> has a great impact on the sports community and the society. By addressing the needs of the blind and deaf community, <em>SportsBuddy</em> promotes inclusivity and equality in the sports community. With the help of <em>SportsBuddy</em>, we can help blind and deaf individuals enjoy all types of sports to the fullest. It will uplift their moral spirits and also will provide them with a sense of belonging. They won’t feel left out anymore in the sports community. Our solution will help raise social awareness regarding the problems that blind or deaf individuals face in their day-to-day lives. It will inspire other communities as well to focus on the needs of people with disabilities.</p>
<h3>Benefits</h3>
<p>Our solution is designed to benefit a wide range of stakeholders connected to sports, including fans, athletes, and sports organizations.</p>
<h4>Fans</h4>
<p>Our solution boosts the experience for fans to enjoy sports, particularly those with visual and hearing impairments. By providing accessible features such as real-time captioning, audio descriptions, sign language interpretation, etc, we ensure that all fans can enjoy sporting events to the fullest. This inclusivity helps grow a sense of belonging and engagement among fans, regardless of their abilities, enriching their overall experience and satisfaction.</p>
<h4>Athletes</h4>
<p>For athletes, our solution provides accessibility by ensuring the experience is the same and accessible to all fans, regardless of their sensory capabilities. By reaching a wider range of audience, athletes can amplify their impact and inspire individuals from diverse backgrounds. <em>SportsBuddy</em> provides athletes with chances for greater exposure and recognition, contributing to their personal and professional growth within the sports industry.</p>
<h4>Sports Organizations</h4>
<p>Sports organizations can also benefit from our solution through increased audience engagement, brand loyalty, and revenue generation. By prioritizing accessibility and inclusivity, organizations can showcase their commitment to social responsibility and diversity, growing their reputation and appealing to fans and sponsors alike. Moreover, our solution may open up new revenue streams through subscription services, pay-per-view options, and targeted advertising, driving financial sustainability and growth for sports organizations.
Overall, our solution positively impacts users by promoting inclusivity, accessibility, and engagement within the sports community. By breaking down barriers and embracing diversity, we envision a future where sports experiences are amplified for all individuals, leading to positive changes in their overall enjoyment, participation, and operations within the sports industry.</p>
<h2>Commercial Viability</h2>
<p>In a world where technology is evolving every day, ensuring inclusivity for all individuals, including those with visual and hearing impairments, is crucial. With approximately 253 million[5] visually impaired people and over 430 million[6] people with disabling hearing loss all over the world, there exists a significant market demand for products that address their needs.</p>
<p>![Recent statistics showing 253 million visually impaired and 430 million hearing impaired people](/assets/img/articles/2024-05-22-Revolutionizing-Sports-Enjoyment-for-Sensory-Disabled-Individuals-in-MonstarHacks-2024/sportsbuddy-recent-stats.webp</p>
<figcaption>Figure: Recent statistics showing visual and hearing impaired approximation</figcaption>
<h3>Market Opportunity</h3>
<p>This huge number represents more than just statistics; it points to an immense market opportunity waiting to be captured. Despite the growing number of individuals with visual and hearing impairments, this market remains almost uncaptured, presenting a huge opportunity for innovation and impact. Projections suggest that by 2050, the number of people with disabling hearing loss worldwide will surpass 711 million[7] and vision impairments by 588 million[5].</p>
<h3>Sports Engagement</h3>
<p>Sports is considered a powerful platform for social engagement, and it's no different for the blind and deaf communities. No matter whether they are participants or spectators, a significant part of these communities actively engage in sports. But here's room to enhance their experience further. Imagine the impact of AI-based detailed commentary for blind audiences or real-time sign language translation for deaf audiences at sporting events. By providing these specialized experiences, we can increase their engagement and boost their enjoyment of sports, contributing to a more inclusive sporting environment for everyone.</p>
<h3>Forging Collaborative Partnerships</h3>
<p>In our solution for visually and hearing-impaired individuals, we believe that collaboration is the key. Many government and non-governmental organizations are eager to support initiatives that can benefit these communities. By partnering with these organizations, we can have access to resources, expertise, and funding opportunities that can significantly uplift our efforts. These partnerships not only increase our reach but also demonstrate a collective commitment to improving the lives of individuals with visual and hearing impairments. We can take meaningful steps towards a more inclusive and accessible future.</p>
<h2>Challenges and Learnings</h2>
<p>Our team set out on the hackathon with the dual goals of developing a solution that would be profitable and have a significant social impact. But this admirable goal wasn't without its difficulties, from figuring out the technological environment to understanding the business aspects of our solution.</p>
<h3>Challenge 1: Ideation and Social Impact</h3>
<p>During the ideation phase, we targeted to find a hack that has both commercial viability and substantial social impact. We brainstormed, considering real-life problems around us.</p>
<p><strong>Solution</strong>: Focusing on accessibility in sports enjoyment, we did some collaborative sessions and research and came up with this idea. Serving a neglected audience using our solution inspired us the most.</p>
<h3>Challenge 2: Technological Feasibility</h3>
<p>At first, we were a bit unsure about the feasibility of our chosen technologies. We did some initial research and sorted it out with our mentor.</p>
<p><strong>Solution</strong>: Our mentor, Iaroslav Ustinov, a solutions architect at AWS, guided us to choose the correct technologies. His expertise helped us assess the feasibility of our technologies.</p>
<h3>Challenge 3: Commercialization Strategy</h3>
<p>At first, we were a bit unaware of how can we make this solution more commercially appealing to the judges and investors.</p>
<p><strong>Solution</strong>: Peter Abdelkodos guided us, introduced some terms, and helped us to rethink the commercial viability of the solution.</p>
<h2>Conclusion</h2>
<p>For our team, participating in the hackathon has been an enjoyable experience. We've overcome obstacles, welcomed creativity, and worked well together to develop a solution that has enormous potential to change society, from conception to implementation. Along with improving our technical proficiency, our journey has deepened our understanding of the value of collaboration and goal-driven creativity. Looking back on our hackathon experience, we're appreciative of the chance to make a difference and motivated to keep using technology to achieve important goals.</p>
<h2>Resources</h2>
<ul>
<li>[1]: <a href="https://aws.amazon.com/blogs/media/engage-online-sports-fans-with-live-event-commentary-using-generative-ai-on-amazon-bedrock/">Engage online sports fans with live event commentary using generative AI on Amazon Bedrock</a></li>
<li>[2]: <a href="https://medium.com/@dobeerman/leveraging-aws-bedrock-to-revolutionize-live-sports-commentary-e03a7f7e7a85">Leveraging AWS Bedrock to Revolutionize Live Sports Commentary</a></li>
<li>[3]: <a href="https://www.handtalk.me/en/">HandTalk Avatar</a></li>
<li>[4]: <a href="https://www.youtube.com/watch?v=LZgZ4K3OL80&#x26;ab_channel=AWSEvents">AWS re:Invent 2023 - How to build generative AI–powered American Sign Language avatars</a></li>
<li>[5]: <a href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5820628/#:~:text=In%202015%2C%20there%20were%20an,blind%20and%202.95%25%20have%20MSVI">NIH - World blindness and visual impairment</a></li>
<li>[6]: <a href="https://www.who.int/news-room/fact-sheets/detail/deafness-and-hearing-loss">WHO - Deafness and Hearing Loss</a></li>
<li>[7]: <a href="https://www.statista.com/statistics/888569/number-of-people-with-hearing-loss-worldwide-projections/">Projected number of people with disabling hearing loss worldwide in 2019, 2030, 2040, and 2050</a></li>
</ul>
<p><em>Article Photo by <a href="https://unsplash.com/photos/o8G05bDqYf0?utm_content=creditCopyText&#x26;utm_medium=referral&#x26;utm_source=unsplash">Chaos Soccer Gear on Unsplash</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Implementing Amazon Connect to deliver an optimised Patient Transport Advice Service]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2024/05/17/Amazon-Connect-Contact-Center-For-Patient-Transport</link>
            <guid>https://engineering.monstar-lab.com/en/post/2024/05/17/Amazon-Connect-Contact-Center-For-Patient-Transport</guid>
            <pubDate>Fri, 17 May 2024 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>A major UK Healthcare provider commissioned Monstarlab to elevate the Contact Centre experience for an NHS-funded Patient Transport Advice Centre (PTAC), acknowledging the critical need for a digitally-enabled transformation to modernise and broaden service delivery via multiple digital channels.</p>
<p>The main drivers were the need to improve efficiency of staff to offset a reduction in resource, reduce average wait time for patients and replace existing disparate legacy systems which were lacking support.</p>
<p>The Monstarlab team conducted a discovery phase, laying the groundwork for a compelling business case to pilot the selected platform. Upon demonstrating an improvement over current technology, the decision was made to rollout Amazon Connect to the PTAC service.</p>
<p><img src="/assets/img/articles/2024-05-17-Amazon-Connect-Contact-Center-For-Patient-Transport/AmazonConnect.webp" alt=""></p>
<h2>Amazon Connect Features for Pilot Phase</h2>
<p>Identified features to explore during the pilot phase were as follows:</p>
<h3>Telephony</h3>
<p>The incumbent telephony system provided no queue information or expected wait times to users and suffered from frequent system disconnects if no active agents were available.</p>
<p>Amazon Connect offers the ability to configure Comprehensive IVR with better call routing, handling and queuing. We were also able to enable callback requests along with a separate callback queue with opening hours to ensure callbacks were handled at low peak usage times and within normal operating hours.</p>
<h3>Real-time and historical metrics dashboards and reports</h3>
<p>Obtaining reports from the incumbent system took days or weeks and required manual mapping between separate systems which meant any insights may be up to a week old.</p>
<p>Amazon Connect offers real time and historical reports with data visualisation which provided the ability to manage staff scheduling and feedback in real time without manual intervention. The available metrics by default also provided additional insights which simply weren't possible before.</p>
<p><img src="/assets/img/articles/2024-05-17-Amazon-Connect-Contact-Center-For-Patient-Transport/analytics.webp" alt=""></p>
<h3>Contact Lens</h3>
<p>In addition to the improved reporting, Contact Lens also offered real-time contact center analytics and quality management powered by natural language processing. This included sentiment analysis and the ability to categorise calls based on the content of the call without the need to manually review the call at a later date. These insights were only possible with significant manual resource investment previously which would only be available to resolve significant issues. By utilising contact Lens further, similar issues could be prevented or prioritised in real time.</p>
<p><img src="/assets/img/articles/2024-05-17-Amazon-Connect-Contact-Center-For-Patient-Transport/sentiment.webp" alt=""></p>
<h2>Outcome of Pilot Phase</h2>
<p>Through the delivery of Amazon Connect handling all phone lines and agents for a period of 3 weeks, the following improvements were realised:</p>
<ol>
<li>Average waiting time reduced by 45%</li>
<li>Calls serviced per agent increased by 24%</li>
<li>Average contact talk time reduced by 5%</li>
</ol>
<p>Along with drastically improved management information visibility and agent satisfaction it was decided to roll out Amazon Connect to the full live service to replace the existing telephony.</p>
<h2>Next steps and Additional Amazon Connect Features</h2>
<p>During the pilot phase, the following additional services provided by Amazon Connect were to be considered and discussed for future phases:</p>
<h3>Omni-channel customer experience</h3>
<p>The current service is only available during office hours by phone.</p>
<p>Amazon Connect Flows can be utilised to offer patients their channel of choice, while saving them time and effort. Telephony and Website Chat Widget is provided out of the box but integrations with other chat clients, (such as Messenger, Slack, Teams etc). SMS, Email can also be configured and utilised to provide out of hours support and signposting.</p>
<h3>Amazon Lex</h3>
<p>Limited Patient Data is provided "up-front" to the Agent handling the contact.</p>
<p>By utilising Amazon Lex, an ML Powered conversational engine, pre-eligibility questions could be provided to pre-populate case details for the agent so the beginning of a call isn’t spent gathering details. This will enable the agent to move straight to the eligibility assessment phase to provide a more streamlined service</p>
<h3>Amazon Customer Profiles and Cases</h3>
<p>The current service tracks customer cases in a separate system to that handling the telephony.</p>
<p>Utilising Amazon Connect Profiles and Cases would allow agents to track and manage customer issues including those which require multiple interactions, follow-up tasks, and teams in the contact center. Agents can document customer issues with all the relevant case details, such as date/time opened, issue summary, customer information, and status, in a single unified view.</p>
<h3>Guides</h3>
<p>The current eligibility assessment is completed in an in house system by an agent, supported by documentation in several formats along with team support via chat.</p>
<p>By implementing and utilising Step by Step Guides and Flows for the agents, they can be guided through the process during the call to populate the Amazon Connect case with the outcome.</p>
<h3>Amazon Q</h3>
<p>In addition to the Step by Step Guides, real time information and assistance based on the content of the contact can be supplied to agents from existing knowledge repositories.</p>
<p><img src="/assets/img/articles/2024-05-17-Amazon-Connect-Contact-Center-For-Patient-Transport/amazonq.webp" alt=""></p>
<h3>API integrations and Tasks</h3>
<p>API Integrations with 3rd party systems and APIs can be provided via Lambda functions within Flows, or the utilisation of Amazon Connect Tasks to automate agent tasks such as emails or bookings.</p>
<h3>Forecasting and Scheduling</h3>
<p>Real time forecasting, capacity planning, and scheduling capabilities can be enabled to efficiently forecast or plan resources automatically based on historical metrics.</p>
<p><em>Article Photos from <a href="https://aws.amazon.com/connect/">Amazon</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Revolutionizing Fan Engagement: Introducing Talos from Team Argonauts]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2024/05/16/Revolutionizing-Fan-Engagement-Introducing-Talos-from-Team-Argonauts</link>
            <guid>https://engineering.monstar-lab.com/en/post/2024/05/16/Revolutionizing-Fan-Engagement-Introducing-Talos-from-Team-Argonauts</guid>
            <pubDate>Thu, 16 May 2024 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>In the dynamic world of sports, the connection between fans and their favorite teams is more than just fandom. It’s an emotional roller-coaster, a shared journey of success and failure. Because we saw such commitment was often overlooked by sports organizations, our team Argonauts participated in <a href="https://www.linkedin.com/showcase/monstarhacks">MonstarHacks</a> 2024 challenges with a mission to transform fan engagement. Our solution not only rewards superfans for their dedication but also fosters positive online communities by providing valuable insights to teams through our comprehensive dashboard.</p>
<h2>How we envisioned TALOS</h2>
<p>We believe that by gamifying fan engagement, we can encourage and reward supporters for their unwavering support for their club. Through our platform, followers earn points for various interactions such as social media engagement, event attendance, and participation in club-related activities. These points act as currency, offering fans exciting prizes ranging from team jerseys and caps to the most desired tickets to the finals, as well as the chance to meet their favorite players which we think is the ultimate reward for a fan.</p>
<p>But our innovation doesn’t stop there. In today’s digital age, social media has become an integral part of the sports fans's lives. However, it’s not uncommon for this to turn toxic and spoil the spirit of the game. That’s where our solution steps in with its unique characteristic, it uses data from social platforms to identify and prevent toxic behaviour. By implementing a system that deducts points for toxic behavior, we aim to create a healthy and inclusive online community by biasing towards positive attitudes and encourage true sportsmanship among the followers.</p>
<p>For sports teams, understanding fan behavior and emotions is essential for improving their strategies both on and off the field. That’s why we created a robust dashboard that gives teams access to valuable insights gained from fan interactions. From monitoring engagement metrics to monitoring sentiment analytics, teams gain a holistic understanding of their fan base, allowing them to tweak their marketing efforts, improve fan experience, and deepen their relationships with their supporters.</p>
<p>Essentially, our solution is a win-win for all stakeholders. Followers are rewarded for their fandom and teams gain valuable insights of their fans and business.</p>
<h2>Technical architecture insights</h2>
<p>The architecture that supports our solution is serverless, which enables a pay-as-you-go service model. This approach will prove highly cost-effective in the long term as demand increases for our app. We utilize AWS Fargate to seamlessly deploy our three services. These services connect our app with social media platforms, extract data from social media posts, and securely store it in an S3 bucket. Additionally, AWS Step Functions orchestrate the smooth processing of steps for our AI/ML tools, ensuring efficient and reliable execution of tasks.</p>
<p>At the core of our architecture, we embrace the cutting-edge capabilities of three powerful AI/ML services offered by AWS. Firstly, AWS Personalizes categorizes sports fans into loyal and seasonal groups, allowing for tailored recommendations based on their preferences. Next, Amazon Comprehend analyzes social media posts to understand fan sentiments, entities, and key phrases, providing valuable insights into fan emotions and preferences. Lastly, Amazon Rekognition deciphers visual content from images and videos shared by fans, identifying objects, people, text, scenes, and activities, enriching our understanding of fan engagement. Together, these services form the foundation of our solution, enhancing the overall fan experience in the APAC region and beyond.</p>
<p>As we will analyze large sets of data that are coming from the users' social media accounts, we need to ensure every piece finds its perfect place. To generate precise insights we will collect, extract, and organize the raw data using S3 Bucket, AWS DynamoDB, AWS Glue, AWS Athena and AWS RDS.</p>
<p>Last but not least, to ensure that our analysis is thorough and insightful, we employ AWS Location Service to pinpoint regions with the highest social media activity, helping us target specific demographics effectively. Additionally, AWS QuickSight provides visual data analytics for our B2B services, enabling us to gain valuable insights and make informed decisions based on the data collected and analyzed from social media platforms.</p>
<figure>
<img src="/assets/img/articles/2024-05-16-Revolutionizing-Fan-Engagement-Introducing-Talos-from-Team-Argonauts/MainDiagram.webp">
<figcaption>Main Architecture Diagram</figcaption>
</figure>
<p>Let's simplify our large AWS diagram by breaking it into smaller pieces for easier explanation.</p>
<h3>User Authentication And Data Extraction</h3>
<p>In this step, users sign up using AWS Cognito for authentication. Then, they answer questions to identify their favorite club/league, with AWS Personalize guiding the process. Next, users add their social media accounts to our app, which is managed by AWS Fargate. After adding their social media accounts, AWS Personalize again analyzes users' social media activity to identify their true fan identity and develop personalized recommendations. Additionally, another AWS Fargate service fetches public data from users'social media accounts and saves the data to an S3 bucket for further use.</p>
<figure>
<img src="/assets/img/articles/2024-05-16-Revolutionizing-Fan-Engagement-Introducing-Talos-from-Team-Argonauts/Diagram-2.webp">
<figcaption>Diagram 2</figcaption>
</figure>
<h3>User Social Media Post Analysis</h3>
<p>In this step, AWS AI/ML services play a big role. Two services run together: one analyzes users' social media posts with AWS Comprehend, while the other analyzes users' photos/videos with AWS Rekognition. AWS Step Functions coordinate these analyses, with multiple Lambda functions executing based on our algorithm. Additionally, AWS Location Services identify the location of social media activity. After analyzing and extracting all the data, it's saved in AWS DynamoDB.</p>
<figure>
<img src="/assets/img/articles/2024-05-16-Revolutionizing-Fan-Engagement-Introducing-Talos-from-Team-Argonauts/Diagram-3a.webp">
<figcaption>Diagram 3(a)</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2024-05-16-Revolutionizing-Fan-Engagement-Introducing-Talos-from-Team-Argonauts/Diagram-3b.webp">
<figcaption>Diagram 3(b)</figcaption>
</figure>
<h3>Analyze data in DynamoDB with Amazon Athena Federated Query</h3>
<p>Since we're dealing with various data sources and are uncertain about the scale of analytics needed, it's wise to opt for a lightweight querying solution. Athena comes into play here, using data source connectors running on AWS Lambda for federated queries. These connectors translate between our data sources and Athena, automatically detecting metadata before each query. Additionally, we can use the AWS Glue Data Catalog to supplement metadata for connectors, especially useful when a data source lacks its metadata source, like DynamoDB. To share analysis results across teams, a custom dashboard is created using AWS Quicksight</p>
<figure>
<img src="/assets/img/articles/2024-05-16-Revolutionizing-Fan-Engagement-Introducing-Talos-from-Team-Argonauts/Diagram-4.webp">
<figcaption>Diagram 4</figcaption>
</figure>
<h3>Frontend</h3>
<p>In this setup, we're using three databases: two AWS DynamoDB and one AWS relational database. These databases are connected to auto scaling-enabled EC2 services through an API Gateway.</p>
<figure>
<img src="/assets/img/articles/2024-05-16-Revolutionizing-Fan-Engagement-Introducing-Talos-from-Team-Argonauts/Diagram-5.webp">
<figcaption>Diagram 5</figcaption>
</figure>
<p><em>Article Photo by <a href="https://unsplash.com/photos/people-watching-baseball-SEQ2VI0KI6A">Jimmy Connover</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Digitalizing Fishing Competitions, the journey of The ReelStreamers team in MonstarHacks 2024]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2024/05/12/Transforming-Fishing-Matches</link>
            <guid>https://engineering.monstar-lab.com/en/post/2024/05/12/Transforming-Fishing-Matches</guid>
            <pubDate>Sun, 12 May 2024 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><a href="https://www.linkedin.com/showcase/monstarhacks">MonstarHacks</a>, the annual competition hosted by Monstarlab, has reached its 4th edition, bringing together talented individuals from diverse backgrounds who are driven by innovation and dedicated to creating a positive social impact. This year, the challenges are brought to the table with collaboration with our partner cloud provider AWS.</p>
<h2>Challenge statement</h2>
<p>Our challenge was to transform fishing matches with technology in order to streamline organization, creating immersive watching experiences while collecting valuable insights based on real data analysis for spectators, fishermen, sponsors, advertisers and equipment producers.
Fishing competitions involve anglers competing to catch the largest, heaviest, or most fish within a set time, often requiring strategic planning and keen understanding of aquatic environments. Competitions can take place in a variety of settings, from tranquil freshwater lakes and rivers to the vast and challenging environments of deep-sea fishing. These events are open to a wide range of participants, including amateur local anglers, seasoned professionals, and anyone in between looking to test their skills.
However, these events can face challenges in fan engagement and operational efficiency such as paperwork, complexity of live data recording, overall event organization and others.</p>
<h2>The proposed solution</h2>
<p>We want to perform a digital transformation of such fishing competitions by building a platform that targets to solve the challenges by bringing interactable viewing experience and data analysis for competitors, equipment manufacturers and fishing enthusiasts. Event management will get digitized, enhancing organization and sponsor interactions, helping in building digital communities to connect fans more deeply with the sport.
This approach will not only streamline the logistics of hosting competitions but also will transform how spectators interact with and enjoy these events.</p>
<h3>Beneficiaries of the Platform</h3>
<p>The main benefits include the following:</p>
<ul>
<li>Event Organizers benefit from streamlined event management.</li>
<li>Spectators enjoy a more immersive and interactive viewing experience.</li>
<li>Competitors gain valuable insights from post-event analytics, helping them refine their techniques.</li>
<li>Sponsors and Advertisers engage more effectively with a captive audience through targeted advertising.</li>
<li>Equipment Manufacturers use detailed usage data as a feedback to improve their products.</li>
</ul>
<h3>Advanced Viewing Experience</h3>
<p>For spectators, the experience is elevated with multi-angle live streams and interactive features. Multi-camera setup, including underwater cameras, will allow users to choose from various camera feeds, including those controlled by producers for dynamic shots and others that can be selected by viewers.
Configurable overlay will allow users to view current match statistics, records and other information that will help to understand the current situation on a deeper level.
All these ensure that every significant moment of the competition is captured and accessible, bringing viewers closer to the action than ever before.</p>
<figure>
<img src="/assets/img/articles/2024-05-12-Transforming-Fishing-Matches/platform-overview.webp">
<figcaption>First look at the ReelStream app.</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2024-05-12-Transforming-Fishing-Matches/features-overview.webp">
<figcaption>Competitors list, available camera list, chat and other features.</figcaption>
</figure>
<h3>Data Analysis and Utilization</h3>
<p>The platform employs algorithms to analyze data collected during competitions. This analysis includes the effectiveness of different equipment, the strategies used by competitors, and environmental impacts on fish behavior. During a match, spectators will be able to view live data. After the match is done, relevant stakeholders will have access to final data analysis, helping them make informed decisions to enhance performance.
This data can be provided to the manufacturers, so they can improve their professional products.</p>
<h3>Promoting Sustainability and Education</h3>
<p>Our platform also emphasizes sustainability and education by providing viewers with information on responsible fishing practices and the environmental aspects of fishing. This helps to promote a more sustainable approach to the sport and educates the public about the importance of conservation in fishing environments.</p>
<h2>Our Tech stack</h2>
<ul>
<li>Machine Learning and AI using AWS Web Services</li>
<li>React JS (Web) &#x26; Flutter for Desktop (MacOS, Windows, Linux) and Mobile (iOS and Android) Apps</li>
<li>Python (Backend)</li>
</ul>
<h3>High level AWS Infrastructure diagram and description</h3>
<p>In the diagram below you can see the high level AWS architecture with the main services leveraged in our solution.</p>
<ul>
<li>S3 buckets to store raw and normalized data</li>
<li>Lambda functions to trigger other AWS services like AWS Rekognition/SageMaker</li>
<li>AWS Elemental MediaLive for normalising different feeds from various cameras setups</li>
<li>AWS Elemental MediaPackage for real time packaging and streaming videos to end users</li>
<li>AWS Interactive Video Service (IVS) for enabling interaction with streamed videos securely to end users</li>
<li>AWS Rekognition (Custom Labels) with AWS Sagemaker to provide a suitable and efficient ML models for input raw data analysis</li>
<li>AWS RDS for storing the data analysis results for further usage</li>
<li>AWS Step Functions for orchestrating the over all flow of the platform</li>
</ul>
<figure>
<img src="/assets/img/articles/2024-05-12-Transforming-Fishing-Matches/non-interactive-devices.webp">
<figcaption>AWS Architecture for non-interactive devices</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2024-05-12-Transforming-Fishing-Matches/interactive-devices.webp">
<figcaption>AWS Architecture for interactive devices</figcaption>
</figure>
<h2>Conclusion</h2>
<p>We believe that the overall interest in fishing matches will increase thanks to our solution, which will lead to fulfilling our vision and mission values.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/six-black-and-yellow-fishing-rod-in-boat-UivGzIDhVyw?utm_content=creditShareLink&#x26;utm_medium=referral&#x26;utm_source=unsplash">Stephen Momot</a></em></p>
<p><em>Screenshot from a video by <a href="https://www.youtube.com/watch?v=HpXAbrjVCyw">Milliken Fishing</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Setup Sonarqube with Docker]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2024/02/06/Setup-Sonarqube-with-Docker</link>
            <guid>https://engineering.monstar-lab.com/en/post/2024/02/06/Setup-Sonarqube-with-Docker</guid>
            <pubDate>Tue, 06 Feb 2024 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>In this article, we will discuss how to set up SonarQube with Docker and produce reports on the code quality of your projects.</p>
<h2>Install Docker on your system</h2>
<p>Install Docker into your system using any of the guides online for your preferred system.</p>
<p>Run the following command in your terminal to verify docker has been installed.</p>
<pre><code class="hljs language-sh">docker -v
</code></pre>
<h2>Installing SonarQube on Docker</h2>
<p>To run SonarQube on your machine using docker, first open a new terminal tab and then pull the image from docker hub using the command below.</p>
<pre><code class="hljs language-sh">docker pull sonarqube
</code></pre>
<p>Once the image has been pulled, start the container by running:</p>
<pre><code class="hljs language-sh">docker run -d --name sonarqube -e SONAR_ES_BOOTSTRAP_CHECKS_DISABLE=<span class="hljs-literal">true</span> -p 9000:9000 sonarqube:latest
</code></pre>
<p>Once your instance is up, log in to the Sonarqube dashboard by navigating to <a href="http://127.0.0.1:9000">http://127.0.0.1:9000</a> and using System Administrator credentials:</p>
<ul>
<li>login: admin</li>
<li>password: admin</li>
</ul>
<h2>Create a new project on Sonarqube</h2>
<p>After logging in, you should see this screen.
<img src="/assets/img/articles/2024-02-06-Setup-Sonarqube-with-Docker/sonarqube-intro.webp" alt=""></p>
<ul>
<li>Let's select manually.</li>
<li>Enter your project display name and main branch name.</li>
<li>Select a baseline for new code for your project</li>
<li>Your project should now be successfully created in Sonarqube</li>
</ul>
<h2>Analyze your project on Sonarqube</h2>
<p>After creating your project, you should be seeing this screen.
<img src="/assets/img/articles/2024-02-06-Setup-Sonarqube-with-Docker/sonarqube-analyze.webp" alt=""></p>
<p>Since we are going to analyze our project locally, click on Locally. Then generate your project token. Select the appropriate options under Run analysis on your project.</p>
<p>Let's also run SonarScanner via docker, no need to download and install it.</p>
<p>First create a file in your project in the root directory called
<strong>sonar-project.properties</strong></p>
<pre><code class="hljs language-ini"><span class="hljs-comment"># must be unique in a given SonarQube instance</span>
<span class="hljs-attr">sonar.projectKey</span>={projectKey}

<span class="hljs-comment"># --- optional properties ---</span>

<span class="hljs-comment"># Encoding of the source code</span>
<span class="hljs-attr">sonar.sourceEncoding</span>=UTF-<span class="hljs-number">8</span>

<span class="hljs-comment"># Here, you can exclude all the directories that you don't want to analyze.</span>
<span class="hljs-comment"># As an example, I'm excluding the node_module and .next directories</span>
<span class="hljs-attr">sonar.exclusions</span>=node_modules/**, .next/**
</code></pre>
<p>Then, to run the SonarScanner and scan your code, navigate to your project in a new terminal tab and execute the following command:</p>
<pre><code class="hljs language-sh">docker run \
    --rm \
    --network=host \
    -e SONAR_HOST_URL=<span class="hljs-string">"http://127.0.0.1:9000"</span> \
    -e SONAR_SCANNER_OPTS=<span class="hljs-string">"-Dsonar.projectKey={Project key}"</span> \
    -e SONAR_TOKEN=<span class="hljs-string">"{authenticationToken}"</span> \
    -v <span class="hljs-string">"<span class="hljs-subst">$(pwd)</span>:/usr/src"</span> \
    sonarsource/sonar-scanner-cli
</code></pre>
<p>Eg:</p>
<pre><code class="hljs language-sh">docker run \
    --rm \
    --network=host \
    -e SONAR_HOST_URL=<span class="hljs-string">"http://127.0.0.1:9000"</span> \
    -e SONAR_SCANNER_OPTS=<span class="hljs-string">"-Dsonar.projectKey=hrbot"</span> \
    -e SONAR_TOKEN=<span class="hljs-string">"sqp_9f5ff08eba566ab37afe5f36104fcxxxxxxxx"</span> \
    -v <span class="hljs-string">"<span class="hljs-subst">$(pwd)</span>:/usr/src"</span> \
    sonarsource/sonar-scanner-cli
</code></pre>
<p>After the scanning is complete, go back to the dashboard and you should see the following screen:
<img src="/assets/img/articles/2024-02-06-Setup-Sonarqube-with-Docker/sonarqube-result.webp" alt=""></p>
<p>You have now completed the scanning of your code for the first time. You can now go ahead and solve any issues and smells that are reported by Sonarqube on your path to a sparkling clean code!</p>
<p>To turn off the Sonarqube container afterwards you can run the command:</p>
<pre><code class="hljs language-sh">docker stop sonarqube
</code></pre>
<p>To turn it on again for later use, you can run the command:</p>
<pre><code class="hljs language-sh">docker start sonarqube
</code></pre>
<p><em>Article Photo by <a href="https://unsplash.com/photos/PpgY7sjpf_0">Milad Fakurian</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[An Introduction to HTMX]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2024/01/24/Introduction-to-HTMX</link>
            <guid>https://engineering.monstar-lab.com/en/post/2024/01/24/Introduction-to-HTMX</guid>
            <pubDate>Wed, 24 Jan 2024 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Single Page Applications (SPA) revolutionized the web development landscape and provided users emulation of native applications. With it however, also came added complexity. “Meta frameworks” such as NextJS or NuxtJS further add on to the complexity as developers are now tasked with managing complex interactions between server and client. In an attempt to reduce this complexity, <a href="https://htmx.org/">HTMX</a> has re-emerged into the scene.</p>
<p>What was formally called “<a href="https://intercoolerjs.org/">intercooler.js</a>” is now HTMX. HTMX in a nutshell is a small client side Javascript library that attaches attributes to DOM elements. These DOM elements are then used to send requests to the server. What this effectively allows the developer to do, is to emulate SPA reactivity by controlling it from server by sending small, HTML “snippets”, which then HTMX swaps or inserts somewhere in the DOM. This simple concept is enough to power many web applications, which do not require the large baggage that modern frontend frameworks come with.</p>
<p>Among many other benefits, this also reduces the need for managing state in two places (client and server), and also allows developers to code in any backend language they prefer.</p>
<p>In this post I’d like to highlight HTMX's simplicity by building a small todo list application together while discussing its benefits and why one should consider using it in their next projects.</p>
<h2>A Todo List</h2>
<p>The best way to understand HTMX is by building something with it. As is tradition, let’s build a todo list application with it. Here already we start with the inherent merit of HTMX and that is that we can use it together with any programming language of ones choice. This allows teams not to be locked into some framework that requires specialized infrastructure. In fact, many developers may choose HTMX simply to get away from JavaScript, and we’ll be doing just that with Python and Flask.</p>
<p>This combination offers a wonderful experience for web development, enabling the creation of web applications that emulate Single Page Applications (SPAs) with ease and efficiency. With Flask and HTMX, you can avoid the complexities associated with traditional build systems like webpack, while effortlessly managing state between the client and server. This approach is also ideal for developing proof of concepts at high velocity. We’ll do this with the coding platform Replit. If you wish to follow this on your local machine, please refer to this <a href="https://flask.palletsprojects.com/en/2.2.x/quickstart/">tutorial</a>, but since the point of this article is to display HTMX’s ease of use, we will disregard application setup.</p>
<p>You can start right away with Replit by navigating to a new <a href="https://replit.com/new">Repl</a> and selecting the Flask template.</p>
<p>Let’s start right await by making an index template that wraps our entire page:</p>
<pre><code class="hljs language-html"><span class="hljs-meta">&#x3C;!DOCTYPE <span class="hljs-keyword">html</span>></span>
<span class="hljs-comment">&#x3C;!-- templates/index.html --></span>
<span class="hljs-tag">&#x3C;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>></span>
  <span class="hljs-tag">&#x3C;<span class="hljs-name">head</span>></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span> /></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span> /></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">title</span>></span>Todo App<span class="hljs-tag">&#x3C;/<span class="hljs-name">title</span>></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/htmx.org"</span>></span><span class="hljs-tag">&#x3C;/<span class="hljs-name">script</span>></span>
    <span class="hljs-comment">&#x3C;!-- Tailwind for styling --></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.tailwindcss.com"</span>></span><span class="hljs-tag">&#x3C;/<span class="hljs-name">script</span>></span>
  <span class="hljs-tag">&#x3C;/<span class="hljs-name">head</span>></span>
  <span class="hljs-tag">&#x3C;<span class="hljs-name">body</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"display flex flex-col items-center w-full relative h-screen"</span>></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-3xl flex gap-4"</span>></span>
            {% include 'star.html' %}       Today    
    <span class="hljs-tag">&#x3C;/<span class="hljs-name">h1</span>></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"todo-list"</span>></span>
      <span class="hljs-comment">&#x3C;!-- This is where our todo list will go --></span>
    <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">form</span> <span class="hljs-attr">hx-post</span>=<span class="hljs-string">"/add-todo"</span> <span class="hljs-attr">hx-target</span>=<span class="hljs-string">"#todo-list"</span> <span class="hljs-attr">hx-swap</span>=<span class="hljs-string">"“innerHTML”"</span>></span>
      <span class="hljs-tag">&#x3C;<span class="hljs-name">button</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">"text-white text-3xl absolute right-5 bottom-5 rounded-full bg-blue-600 aspect-square w-6"</span>
      ></span>
        +
      <span class="hljs-tag">&#x3C;/<span class="hljs-name">button</span>></span>
    <span class="hljs-tag">&#x3C;/<span class="hljs-name">form</span>></span>
  <span class="hljs-tag">&#x3C;/<span class="hljs-name">body</span>></span>
<span class="hljs-tag">&#x3C;/<span class="hljs-name">html</span>></span>
</code></pre>
<p>And the following Python file that will contain all our logic:</p>
<pre><code class="hljs language-python"><span class="hljs-comment"># main.py</span>
app = Flask(__name__)

<span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">'/'</span></span>)</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">index</span>():

  <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">'index.html'</span>)
</code></pre>
<p>On the page we simply include HTMX via a script tag, no build step required.</p>
<p>Our first introduction to HTMX begins with the <code>hx-post</code>, <code>hx-target</code>, and <code>hx-swap</code> attributes on the <code>form</code> element. HTMX empowers web developers by allowing the addition of special attributes to any standard HTML element. These enhanced elements gain the ability to initiate HTTP requests. The responses in the form of HTML fragments, can then be dynamically inserted or replaced in any part of the webpage. This feature introduces a seamless way to update content without needing a full page refresh. In our case, we want to put our tasks into the <code>div</code> element with the id of "todo-list".</p>
<p>Let’s do this by inserting an empty task when the user presses the “+” button. We start by adding an endpoint that will create the HTML fragment, and then return that fragment:</p>
<pre><code class="hljs language-python"><span class="hljs-comment"># main.py</span>
<span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">'/add-todo'</span>, methods=[<span class="hljs-string">"GET"</span>]</span>)</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">task</span>():
  <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">'task.html'</span>)
</code></pre>
<p>And the following template:</p>
<pre><code class="hljs language-html"><span class="hljs-comment">&#x3C;!-- templates/task.html --></span>
<span class="hljs-tag">&#x3C;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-gray-800 rounded-lg p-4 max-w-lg mx-auto my-6"</span>></span>
  <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex items-center"</span>></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">input</span>
      <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span>
      <span class="hljs-attr">class</span>=<span class="hljs-string">"form-checkbox h-5 w-5 text-yellow-400 rounded border-gray-600"</span>
    /></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">input</span>
      <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
      <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"New To-Do"</span>
      <span class="hljs-attr">class</span>=<span class="hljs-string">"ml-4 bg-transparent border-0 placeholder-gray-400 text-white focus:outline-none"</span>
      <span class="hljs-attr">style</span>=<span class="hljs-string">"width: calc(100% - 2rem);"</span>
    /></span>
  <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
  <span class="hljs-tag">&#x3C;<span class="hljs-name">textarea</span>
    <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Notes"</span>
    <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-2 bg-transparent border-0 placeholder-gray-400 text-white focus:outline-none w-full"</span>
  ></span><span class="hljs-tag">&#x3C;/<span class="hljs-name">textarea</span>></span>
  <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex items-center justify-between mt-4"</span>></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-yellow-400 focus:outline-none"</span>></span>
                  Today        
    <span class="hljs-tag">&#x3C;/<span class="hljs-name">button</span>></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex space-x-2"</span>></span>
      <span class="hljs-tag">&#x3C;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"focus:outline-none"</span>></span>
                      {% include '/svg/line.html' %}            
      <span class="hljs-tag">&#x3C;/<span class="hljs-name">button</span>></span>
      <span class="hljs-tag">&#x3C;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"focus:outline-none"</span>></span>
                        {% include '/svg/dropdown.html' %}            
      <span class="hljs-tag">&#x3C;/<span class="hljs-name">button</span>></span>
      <span class="hljs-tag">&#x3C;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"focus:outline-none"</span>></span>
                        {% include '/svg/hamburger.html' %}            
      <span class="hljs-tag">&#x3C;/<span class="hljs-name">button</span>></span>
    <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
  <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
<span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
</code></pre>
<p>With some added styling, the actual important points of this fragment is the <code>input</code> elements which will play a part soon. Clicking on the + button will now display a new, empty task. Let’s see how this works. The button looks like this:</p>
<pre><code class="hljs language-html"><span class="hljs-tag">&#x3C;<span class="hljs-name">form</span> <span class="hljs-attr">hx-get</span>=<span class="hljs-string">"/add-todo"</span> <span class="hljs-attr">hx-target</span>=<span class="hljs-string">"#todo-list"</span> <span class="hljs-attr">hx-swap</span>=<span class="hljs-string">"“innerHTML”"</span>></span>
  <span class="hljs-tag">&#x3C;<span class="hljs-name">button</span>
    <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
    <span class="hljs-attr">class</span>=<span class="hljs-string">"text-white text-3xl absolute right-5 bottom-5 rounded-full bg-blue-600 aspect-square w-6"</span>
  ></span>
    +
  <span class="hljs-tag">&#x3C;/<span class="hljs-name">button</span>></span>
<span class="hljs-tag">&#x3C;/<span class="hljs-name">form</span>></span>
</code></pre>
<p>The special attributes added by HTMX <code>hx-get</code>, <code>hx-target</code> and <code>hx-swap</code> tells us, “when the form is submitted, issue a (hx-)get request, and (hx-)target the element with id “todo-list, and (hx-)swap the inner content of this HTML.”. The concept of emulating SPA-like behavior via attributes is what forms the entire behavior of HTMX. Here’s a few other attributes which would allow us to do other things:</p>
<ol>
<li><code>hx-trigger</code> would allow us to trigger requests based on behavior, such as on mouse hover.</li>
<li><code>hx-post</code> sends a post request instead of a get request. You can also do this with PUT, DELETE, and PATCH.</li>
<li><code>hx-boost</code> allows us to convert all anchor tags and forms into AJAX requests that, by default, target the body of the page.</li>
<li><code>hx-push-url</code> allows us to interact with the browser history API.</li>
</ol>
<p>And many others. This also does not include the long list of values for each of these attributes which further allows us to customize behavior. It’s also important to note that these attributes can be applied to any element. These are all well documented in the <a href="%5Bhttps://htmx.org/docs%5D(https://htmx.org/docs)">documents page</a>.</p>
<p>Let’s improve the todo list application by adding attributes to append tasks to the list, and to edit the individual tasks. Instead of swapping the entire inner HTML of the <code>#todo-list</code> element, we can append to the end with <code>beforeend</code> as such:</p>
<pre><code class="hljs language-html"><span class="hljs-tag">&#x3C;<span class="hljs-name">form</span> <span class="hljs-attr">hx-get</span>=<span class="hljs-string">"/add-todo"</span> <span class="hljs-attr">hx-target</span>=<span class="hljs-string">"#todo-list"</span> <span class="hljs-attr">hx-swap</span>=<span class="hljs-string">"beforeend"</span>
  …
 
</span></code></pre>
<p>These tasks are ephemeral, so we need to start adding some state. Since HTMX allows us to control the front end from the server side, we don’t need to reach for state synchronization solutions as it is often needed in applications that use larger frontend frameworks. We’ll drive everything from the server as our source of truth:</p>
<pre><code class="hljs language-python"><span class="hljs-comment"># main.py</span>
task_list = [{
    <span class="hljs-string">"id"</span>: <span class="hljs-number">222</span>,
    <span class="hljs-string">"title"</span>: <span class="hljs-string">"Schedule doctors appt"</span>,
    <span class="hljs-string">"notes"</span>: <span class="hljs-string">"Make sure its in the evening after the 14th."</span>
}]


<span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">'/todos'</span>, methods=[<span class="hljs-string">"GET"</span>]</span>)</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">load_task</span>():
  <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">'tasks.html'</span>, tasks=task_list)
</code></pre>
<p>We load our state into the template, and further split up our code into fragments, similar to components (create a file <code>templates/tasks.html</code>):</p>
<pre><code class="hljs language-html"><span class="hljs-tag">&#x3C;<span class="hljs-name">ul</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"todo-list"</span>></span>
      {% for task in tasks %}         {% include 'task.html' with context %}    
  {% endfor %}
<span class="hljs-tag">&#x3C;/<span class="hljs-name">ul</span>></span>
</code></pre>
<p>The individual item templates are ready to consume the data supplied by Flask. This completes our "READ" process in our little CRUD application. Since we already have the "CREATE" process from our first code written, we can continue here onto our "UPDATE". We can add the ability to edit a task with a nice UX pattern. The user should be able to just type and the tasks will be updated seamlessly. We can do this by debouncing on key input. This can typically be quite terse with other frontend frameworks, requiring several lines and some intricate state updates, but with HTMX it is elegant:</p>
<pre><code class="hljs language-html"><span class="hljs-tag">&#x3C;<span class="hljs-name">form</span>
  <span class="hljs-attr">id</span>=<span class="hljs-string">"task-form-{{ task.id }}"</span>
  <span class="hljs-attr">hx-post</span>=<span class="hljs-string">"/update-todo"</span>
  <span class="hljs-attr">hx-trigger</span>=<span class="hljs-string">"keyup from:input, keyup from:textarea delay:500ms
"</span>
></span>
  <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex items-center"</span>></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"hidden"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"task_id"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"{{ task.id }}"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">""</span> /></span>      
      <span class="hljs-tag">&#x3C;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"“…”"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"checked"</span> /></span>        
    <span class="hljs-tag">&#x3C;<span class="hljs-name">input</span>
      <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
      <span class="hljs-attr">name</span>=<span class="hljs-string">"title"</span>
      <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"New To-Do"</span>
      <span class="hljs-attr">class</span>=<span class="hljs-string">"“…”"</span>
      <span class="hljs-attr">value</span>=<span class="hljs-string">"{{ task.title if task.title }}"</span>
    /></span>
  <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
  <span class="hljs-tag">&#x3C;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"notes"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Notes"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"“…”"</span>></span>
{{ task.notes if task.notes }}&#x3C;/textarea
  >
<span class="hljs-tag">&#x3C;/<span class="hljs-name">form</span>></span>
</code></pre>
<p>The <code>hx-trigger</code> on top of the form does a lot of work for us here, and it almost reads like in plain English, “trigger on key from input or textarea, and delay for 500 milliseconds”.</p>
<p>Finally, we can update the data on our backend:</p>
<pre><code class="hljs language-python"><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">'/update-todo'</span>, methods=[<span class="hljs-string">"POST"</span>]</span>)</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">update_todo</span>():
  <span class="hljs-comment"># Extract the data from the form</span>
  task_id = request.form.get(<span class="hljs-string">'task_id'</span>)
  title = request.form.get(<span class="hljs-string">'title'</span>)
  notes = request.form.get(<span class="hljs-string">'notes'</span>)
  checked = request.form.get(<span class="hljs-string">'checked'</span>) == <span class="hljs-string">'on'</span>

  <span class="hljs-comment"># Update the task in the list with some fun list comprehension</span>
  <span class="hljs-keyword">global</span> task_list
  task_list = [{
      **task, <span class="hljs-string">'title'</span>: title,
      <span class="hljs-string">'notes'</span>: notes,
      <span class="hljs-string">'checked'</span>: checked
  } <span class="hljs-keyword">if</span> <span class="hljs-built_in">str</span>(task[<span class="hljs-string">'id'</span>]) == task_id <span class="hljs-keyword">else</span> task <span class="hljs-keyword">for</span> task <span class="hljs-keyword">in</span> task_list]

  <span class="hljs-comment"># return nothing so to disallow default swapping from HTMX</span>
  <span class="hljs-keyword">return</span> <span class="hljs-string">''</span>, <span class="hljs-number">204</span>
</code></pre>
<p>And with that, we've concluded our "UPDATE" process code. I leave the exercise of deletion and sorting (or whatever else you may think of) to the reader. If you’ve been convinced by now, you can probably already see how trivial it can be to add the rest of these features.
<img src="/assets/img/articles/2023-12-19-Introduction-to-HTMX/img-1.webp" alt="Github Branches">
Up until now we've explored how to do some simple CRUD operations. One reason however that people chose to use the complex modern frontend frameworks is the ability to have state dependent on other state update automatically. While HTMX may not be the best approach here when requiring a complex set of dependencies across many DOM elements, it is still absolutely achievable to accomplish this with HTMX. HTMX provides the <a href="https://htmx.org/attributes/hx-swap-oob/"><code>hx-swap-oob</code></a> attribute that lets you swap outside of the target element. As the HTMX documentation says:</p>
<blockquote>
<p>This allows you to piggy back updates to other element updates on a response.</p>
</blockquote>
<p>With this in mind, we can add a great example for our little app. What we will add is a counter at the top of the application header. This counter is a state that is dependent on the number of todo items. When we add a new todo item, we can update the top header with the new updated count value:</p>
<p>At the end of the <code>task.html</code> file we add:</p>
<pre><code class="hljs language-html"><span class="hljs-comment">&#x3C;!-- rest of HTML --></span>
{% if new_task_length %}
<span class="hljs-tag">&#x3C;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"counter"</span> <span class="hljs-attr">hx-swap-oob</span>=<span class="hljs-string">"true"</span>></span>{{ new_task_length }}<span class="hljs-tag">&#x3C;/<span class="hljs-name">span</span>></span>
{% endif %}
</code></pre>
<p>And then we add the following item to the return statement of the <code>/add-todo</code> endpoint:</p>
<pre><code class="hljs language-python"><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">'/add-todo'</span>, methods=[<span class="hljs-string">"GET"</span>]</span>)</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">task</span>():
<span class="hljs-comment"># ...rest of logic</span>
	<span class="hljs-keyword">return</span> render_template(<span class="hljs-string">'task.html'</span>, task={}, new_task_length=<span class="hljs-built_in">len</span>(task_list))
</code></pre>
<p>this demonstrates how easy it is to manage state dependent updates via HTMX's <code>hx-swap-oob</code> feature. Here, with just a few lines of code, we have created an additional feature in our app - a counter that automatically updates based on the current length of our task list. The length of the task list is a dependent state, reflecting the total count of todo items. When a new task is created, the counter in the header is automatically refreshed to display the new count.</p>
<p>I want to conclude this tutorial exercise section by discussing animations, one reason why many people choose to use Javascript frameworks.</p>
<p>![Github Branches](/assets/img/articles/2023-12-19-Introduction-to-HTMX/img-2.webp</p>
<p>Indeed, animations are a key aspect of modern web applications, often being a primary reason for choosing JavaScript frameworks. However, with HTMX, you don't have to forgo this essential element of user experience.</p>
<h3>Adding Animations with HTMX</h3>
<p>Animations can be achieved through various methods while using HTMX. A significant portion of interactive feedback can be handled with CSS. Modern CSS provides enough for creating animations and transitions, making it possible to animate changes in your UI in response to HTMX events.</p>
<p>For instance, using CSS transitions, you can smoothly animate the appearance and disappearance of elements, or their transformation in response to user interactions. This can be particularly effective for operations like adding, updating, or deleting items in a list, where visual feedback enhances the user experience. With HTMX, this is taken a step further with great integrations such as swap transitions. Take an example from the docs:</p>
<blockquote>
<p>If you want to fade out an element that is going to be removed when the request ends, you want to take advantage of the <code>htmx-swapping</code> class with some CSS and extend the swap phase to be long enough for your animation to complete. This can be done like so:</p>
</blockquote>
<pre><code class="hljs language-html"><span class="hljs-tag">&#x3C;<span class="hljs-name">style</span>></span><span class="css">
  <span class="hljs-selector-class">.fade-me-out</span><span class="hljs-selector-class">.htmx-swapping</span> {
    <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">transition</span>: opacity <span class="hljs-number">1s</span> ease-out;
  }
</span><span class="hljs-tag">&#x3C;/<span class="hljs-name">style</span>></span>
<span class="hljs-tag">&#x3C;<span class="hljs-name">button</span>
  <span class="hljs-attr">class</span>=<span class="hljs-string">"fade-me-out"</span>
  <span class="hljs-attr">hx-delete</span>=<span class="hljs-string">"/fade_out_demo"</span>
  <span class="hljs-attr">hx-swap</span>=<span class="hljs-string">"outerHTML swap:1s"</span>
></span>
  Fade Me Out
<span class="hljs-tag">&#x3C;/<span class="hljs-name">button</span>></span>
</code></pre>
<h3>Leveraging JavaScript Libraries</h3>
<p>For more complex animations, you can integrate lightweight JavaScript libraries. GreenSock (GSAP) is an excellent example of a library that provides advanced animation capabilities with a simple API. By combining HTMX with such libraries, you can create sophisticated animations that bring your UI to life, without the overhead of a full JavaScript framework.</p>
<p>Lastly, I'd like to point out the emerging Transitions API, currently only available in Chrome. This API is set to offer native support for smooth transitions, potentially reducing the reliance on external libraries for such effects, thus making the necessity of SPA frameworks less relevant. HTMX too, integrates well as it provides access to this API via the <code>hx-swap</code> attribute, which you can learn more about <a href="https://htmx.org/examples/animations/#view-transitions">here</a>.</p>
<h2>Wrapping Up</h2>
<p>HTMX offers a sublime experience for developers with simplicity and functionality. It allows for developers to focus on business logic without compromising on UI/UX. With HTMX, basic REST knowledge is enough to build a fully functional user interface, reducing complexity, development time, and costs.</p>
<p>HTMX isn't a solution for everything, though. For more complex applications, a comprehensive JavaScript framework may be more suitable. The state management and component-based architectures that HTMX doesn't inherently support might be a better fit depending on the application and needs.</p>
<p>Despite this, HTMX's ease of integration and minimalistic approach makes it a valuable tool that should always be considered. It's particularly effective for enhancing server-rendered pages or for projects where a full JavaScript framework is unnecessary. Ultimately, HTMX stands out for its ability to deliver efficient, interactive web experiences with minimal overhead, making it a technology worth considering in various web development scenarios.</p>
<p>Since there is always more to learn, please consider checking out the <a href="https://htmx.org/docs/">official documentation</a> and the <a href="https://htmx.org/essays/">essays</a> as well as the <a href="https://hypermedia.systems/">free book</a> provided by the HTMX author. You can also visit the source code used for this project here: <a href="https://replit.com/@hhofner/KnownHeartyApplescript">Replit</a></p>
<h2>Resources</h2>
<p><em>Article Photo by <a href="https://htmx.org/">HTMX</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Behind the screens: Interview with Nikita Sirovskiy, Lead Mobile Engineer at Monstarlab]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2024/01/19/Interview-Nikita-Sirovskiy</link>
            <guid>https://engineering.monstar-lab.com/en/post/2024/01/19/Interview-Nikita-Sirovskiy</guid>
            <pubDate>Fri, 19 Jan 2024 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Today, we're chatting with <a href="https://www.linkedin.com/in/nivisi">Nikita Sirovskiy</a>, Lead Mobile Engineer who on top of working on client projects, plays an important role in enhancing Monstarlab's Flutter Service Offering by driving meetings, creating &#x26; managing tasks and helping with people’s allocation.</p>
<p>Nikita is, in his own words, a "healthy perfectionist, stoic and a creative person". In his free time, he likes to play guitar, sing, write stories, poems and books. Nothing is published yet, but hopefully one day we'll get to read his work.</p>
<p>![Prague Team](/assets/img/articles/2024-01-19-Interview-Nikita-Sirovskiy/team.webp</p>
<figcaption>In the photo: Prague team (Omar Bekier, Ekaterina Surovtseva, Eleonora Perevozchikova, Juraj Longauer, Nikita Sirovskiy, Pedro Massango, Oleksandr Mochalov, Esmaeil Abedi, Muhittin Kaya)</figcaption>
<p><em><strong>Nikita, can you describe your journey of transitioning from a developer to a project lead? What were the most significant changes you had to adapt to?</strong></em></p>
<p>“Extreme Ownership”, that’s what sums it up best. I've always been proactive and tried to enhance things on all the projects I was a part of. Questioning decisions made outside of my direct responsibility zone was and is very important to me. I work not only on mobile apps, I work on products. A right question at the right time might turn the tide. I know people say ‘I’m not getting paid for this or that.’, but I never understood this. Of course you must not do things for others, yet, if you see a room for improvement, why not just improve it?</p>
<p>One of the most significant changes is that now, most of the time, I am the person who answers questions, not the one who asks them. Deadlines, scopes, code reviews and all this. After all this, there’s not much time to code myself. While being a lead gives me a bird-eye view on the entire project, which is very interesting, sometimes I miss these days where all I had to do was just to code. Coffee, good music, quietness, flow state… Yes, sometimes I definitely miss this.</p>
<p><em><strong>Q: During your first project as a lead, what were some of the biggest challenges you faced? Could you share a story where you had to overcome a difficult obstacle, and how you managed to do it?</strong></em></p>
<p>Self-doubt. That’s the biggest thing for sure. It was hard to accept that now it’s me who is responsible for making decisions and saying yes or no to other’s decisions. Sometimes the pressure was high, sometimes I was not confident in my own solutions and decisions, sometimes the deadlines were really tight. Although people around me — my family, my line manager, my project manager and my teammates — supported me a lot. Not only with valuable advice, but also by saying that the result is fantastic so far. Another day, when looking at the result myself, I realized that we’ve built the best mobile app I ever worked on. It’s stable, developer experience is great, the code is clean, and the UI/UX is amazing. That gave me a lot of confidence and the game changed.</p>
<blockquote>
<p>“Don’t pull yourself backwards” — that’s what one of my friends told me.</p>
</blockquote>
<p><em><strong>Q: Given your experience with remote work, how do you handle the dynamics of leading a team remotely, especially when occasionally there is a need to visit client offices as well?</strong></em></p>
<p>I worked remotely since the beginning of my career, and I still do. Although Monstarlab’s borderless approach took it to another level. Even people who work from office usually sit in different offices all around the world! This is amazing. You can meet so many different people. This is how we work on the current project as well.</p>
<p>We try to be more autonomous and contextualized. Less meetings, more documentation: in-code, Confluence, Google docs, or whatever is more convenient for a specific topic. Less direct messages in messengers, more communication in corresponding tools (GitHub, Figma, etc). Different time zones is a challenge itself. I want us to reach the point when, if someone needs something, they will be able to find it in the docs. So we won’t need to disturb or wait for each other in Slack or in a meeting room.</p>
<p>But working remotely means that colleagues are more disconnected from each other. Sometimes it’s hard to understand the needs or mood of your teammates, so periodic catch ups are very important. I have 1-on-1 calls where I sincerely ask my teammates about what is going on. Is there something to change, a problem that must be fixed or just a question that needs an answer. We find it very beneficial. I was given a lot of suggestions on these calls. It also allows you to discuss off-topic things which leads to better connection with a person. Although it is important not to turn these calls into rituals that you blindly follow just “because”.</p>
<p>Work trips are also very beneficial. All my visits were filled with productive and interesting time. Brainstorming sessions, lunches, morning coffees, debug sessions. This for sure enriches your connection with people. As someone who works remotely most of the time, I value it a lot. Perhaps one day I decide to relocate to be with the team all the time. I’m very curious how it would be.</p>
<p><em><strong>Q: What advice would you give to developers who are about to take on their first leadership role? Are there any key lessons you wish you had known when you started?</strong></em></p>
<p>Well, don’t pull yourself backwards, that’s for sure. You are where you are for a reason. Your skills and commitment have led you to this role, so just keep it up and do your best.</p>
<p>For more specific things, learn how to organize yourself. Self-organization helps to keep track of things: questions raised on calls or chats, project goals, tech debt, ideas etc. Proper tracking allows you to plan better, oversee everything and move faster. I started using Apple Notes extensively. I have folders for interviews, TODOs, suggestions, conversations, and basically anything that is important in any way. One thing I love is the new (maybe not that new anymore) feature with the quick note in the corner of your screen. I keep my TODO list there. If I need to note something, I just move my cursor to the right-bottom corner and start noting.</p>
<p>I write a lot of documentation. This is an extremely beneficial tool for time saving and optimization. We don’t need to re-explain rules and there’s less need to sync. If in doubt, one can open the documentation to learn how certain things are done.</p>
<p>Another thing is talking to people. This will sound classic, but everyone is very different, and everyone requires a different approach. You will need to provide constructive feedback, be a mentor for your colleagues, raise problems and concerns to managers and the business, suggest ideas and say no. The latter is a skill itself, a very important one. I like this phrase that I heard somewhere, «If it’s not a 100% yes, then it’s a no». Of course it’s not really applicable here (otherwise we would’ve been blocked most of the time!), but this idea is still a good foundation for making decisions. Remember, you’re in charge now, and raising concerns, whatever those are, is a must. I failed to do this a few times. Those problems came back at a later stage of the project. Nothing big, fortunately, yet still… I learned this very well.</p>
<p><em><strong>Q: How has your experience been working at Monstarlab? Could you share some highlights or key moments that have shaped your journey here?</strong></em></p>
<p>It’s been fantastic so far! Working here has been a journey of growth and challenges. What I can highlight is the level of trust that you have here. On my very first project, I quickly shifted to conducting demos, enhancing code practices, and doing code reviews. On my second project, when the lead relocated to a different time zone, I stepped up to handle most of the code reviews, demos and crucial feature finalizations. On my third project I was mostly solo, which made me the one who talks with designers, backend developers, clients and PMs.</p>
<blockquote>
<p>The atmosphere on every project was amazing. Ask anyone, and everyone will tell you that people in Monstarlab are special. They are always eager to chat, to laugh, to help and support.</p>
</blockquote>
<p>Someone even invited me to their home, and we spent evenings talking about life. Someone drives me to and from the office every time I’m on a work trip, and we have these short morning and evening conversations.</p>
<p><em><strong>Q: Reflecting back to the beginning of your career, what initially inspired you to start programming? Have there been any specific role models in the tech industry who have influenced or guided your path?</strong></em></p>
<p>I have a very vivid memory of asking my father “Who creates computer games?” and him answering “programmers”. That was the initial inspiration. I wanted to be either a programmer or a taxi driver. Don’t ask me why. Today I don’t even have any clue about cars. Anyways.</p>
<p>It became serious when my father discovered that you can create custom game maps, or modes, for my favorite computer game, Warcraft 3. This involved coding, designing characters, and crafting stories and worlds – a mix of technical skills and creativity. I think I was 7 years old when he showed me that program, World Editor. I spent countless hours, days and nights, in trying to craft things. My mother also played a crucial role. She forced me to learn more and take additional classes, even when I didn’t want to. But in a soft way. I always had full freedom in experimenting what I want from this life and in learning whatever I think is needed.</p>
<p>But I didn’t become a game developer! Maybe one day, maybe one day. I was lucky to have that map-creating period in my life. It has built a solid foundation for problem-solving and creative skills that I use today.</p>
<p><em><strong>Q: Every profession has its lighter side, and programming is no exception. Do you have a favorite programming joke or a meme that resonates with you or brings a smile to your face?</strong></em></p>
<p>Yes! I wanted to find the picture, but I couldn’t. There's this meme comparing users enjoying a new feature to developers tangled in a mess of code trying to figure out how to develop a new feature without breaking the app.</p>
<p>While humorous, it shows the real challenges in coding. I've seen poorly organized codebases that looked like spaghetti. It's a reminder that we should never trade-off maintainability for delivering something new. Organization, requirements, documentation and optimal processes make development cycles efficient and apps cost-effective.</p>
<p>It improves another thing that is often ignored, developer experience. The happier the devs, the more interesting it is to work on a project, the better the project will be. I hope that that meme is not applicable for us. Hope that in our case both of the sides of the picture represent happy people.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Enhancing ETL Expertise: A Backend Engineer's Insight into Leveraging AI Tools for Python Development]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2024/01/18/Enhancing-ETL-Python-Project-to-expertise-with-AI-companion-tools</link>
            <guid>https://engineering.monstar-lab.com/en/post/2024/01/18/Enhancing-ETL-Python-Project-to-expertise-with-AI-companion-tools</guid>
            <pubDate>Thu, 18 Jan 2024 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>A few months ago, I started exploring the world of data engineering. Armed with just basic knowledge of Python, I set out to implement my first ETL (Extract, Transform, Load) pipeline using Python to move data between two systems seamlessly.</p>
<p>GitHub Copilot turned out to be my guiding star in this Python odyssey. With its code suggestions and real-time assistance, it felt like I had a seasoned Python developer right by my side. Copilot not only streamlined my coding process but also served as an exceptional teacher. It provided explanations, alternative solutions, and helped me grasp Python's intricacies faster than I could have imagined.</p>
<p>But Copilot wasn't my only ally. ChatGPT, another AI companion, was there to answer my questions and provide insights whenever I needed them. It didn't matter if it was about Python syntax, or data manipulation with Pandas, or even conceptualizing ETL workflows – ChatGPT was always ready with accurate and thorough responses.</p>
<p>Together, Copilot and ChatGPT became the dynamic duo that guided me through the twists and turns of this project. They helped me build an ETL pipeline that not only met the project's goals but exceeded my own expectations. The pipeline seamlessly extracted data, transformed it to fit the target system, and loaded it with just some adjustments on the code but in general guiding me with really good samples.</p>
<p>As I reflect on this journey, I can't help but marvel at the capabilities of modern technology. With just basic Python knowledge, I accomplished what once seemed like an task I wouldn't have the skills to do. Copilot and ChatGPT not only accelerated my learning but also provided creative solutions and expanded my horizons. This project has taught me that with the right tools and determination, even a novice can achieve remarkable results in the world of programming.</p>
<h3>Getting Started</h3>
<p>Beyond coding, ChatGPT helped me with setting up my development environment. It guided me through best practices for organizing Python scripts, ensuring my codebase was clean and maintainable. When it came to connecting to various data sources, ChatGPT was my helper when I got blocked. It provided step-by-step guidance on how to integrate with Salesforce, external APIs, and Google Services like Google Drive API. Obviously, I had to explore a little bit directly into the APIs documentation to ensure the current ChatGPT response was in line with the needs. At one moment I was lost on a task to set up the services needed for the project and ChatGPT surprised me. It even outlined the necessary roles and permissions demystifying the process of establishing these connections and setting up the grants needed for this task.</p>
<p>Moreover, ChatGPT helped me navigate the complex world of the Google Cloud (GCloud) infrastructure. It guided me through setting up connections to services like BigQuery and Google Storage. And as mentioned before it also helped me demystifying the process of managing grants between service accounts needed to open a connection between the different services used, ensuring that my ETL pipeline operated smoothly within the Google Cloud ecosystem. ChatGPT showed me step by step instructions on how to do things like creating a Service Account Key and how I could use it to connect with Google services using Python and the libraries used for a specific service.</p>
<p>On the other hand, ChatGPT helped me to set up Salesforce connections and it guided me through the existent APIs. Building the connection between Salesforce and the ETL on Python was a challenge since it implies a different kind of Authentication with OAuth. ChatGPT was able to explain me the ways that we could authenticate and it showed a few approaches that could do. In fact, during the implementation, it was necessary to look at the libraries used for an easier Salesforce API connection. During this research the Simple Salesforce library was recommended which ended up being my tool to build the solution and perform in a easy way queries to retrieve the information from that source.</p>
<p>One more sample that ChatGPT provided was for Google spreadsheets connection. I asked “Which grants needs a Google Service account to read using Google Drive API and get access to Google Spreadsheets?” and ChatGPT showed me what I needed from Google Cloud and how I could use a Service Account:</p>
<p><img src="/assets/img/articles/2024-01-18-Enhancing-ETL-Python-Project-to-expertise-with-AI-companion-tools/DLC_Image_1.webp" alt="ChatGPT Google Spreadsheet python snippet"></p>
<p>That was not the only thing, it also helped to understand things like how could I sort and use Pandas dataframes. This was really helpful through my work of developing the solution:</p>
<p><img src="/assets/img/articles/2024-01-18-Enhancing-ETL-Python-Project-to-expertise-with-AI-companion-tools/C2W_Image_2.webp" alt="ChatGPT Pandas short snippet"></p>
<h3>Using Copilot for Code Assistance</h3>
<p>Initially, I started thinking about how I could use the tools and libraries to set up my environment. During this phase, Copilot helped me build the initial inputs for tasks like establishing the connection with the Salesforce API and how I could start sending requests through it using SOQL queries to retrieve information from the system. I have to admit that in those cases where a language like SOQL comes into the scene, it was not completely precise since it showed normal SQL in the suggestions, but sometimes it was close enough to what I wanted to achieve, so I just had to modify the proposed query slightly.</p>
<p>One additional task Copilot helped me with was clarifying complex aspects of Python and the Pandas library, providing me with insights and solutions that were instrumental in achieving the project's goals and understanding the libraries used.</p>
<p>Tasks like opening a research to merge two different Pandas or understanding how the Pandas work, are easier with Copilot completion:</p>
<p><img src="/assets/img/articles/2024-01-18-Enhancing-ETL-Python-Project-to-expertise-with-AI-companion-tools/NUe_Image_3.webp" alt="Copilot Pandas merge suggestion sample"></p>
<h3>Navigating Legacy Code and Pandas Mastery</h3>
<p>One early challenge I encountered was dealing with legacy code from an old ETL pipeline, in order to understand the model expected by the final output. There was a huge amount of "spaghetti code" that I had to try to understand, which was not an easy task given my expertise in managing Python and Pandas. However, Copilot proved to be a lifesaver in giving me clarity. It assisted me in understanding the intricacies of the existing code, breaking down its logic, and understanding legacy code that was not following best practices.</p>
<p>As I mentioned, a significant part of my project involved working with Pandas library for data manipulation. Copilot helped me understand how Pandas could make different tasks like merging data from different tables, making transformations, sorting, and more. Its code suggestions and explanations really helped me, enabling me to merge different tables from different data sources and to ensure data consistency throughout the ETL process.</p>
<p>In cases where I was not completely sure what the code was doing Copilot Chat was able to explain it to me and give me more context about what a piece of code was doing.</p>
<p>Let’s say that I have the next piece of code:</p>
<pre><code class="hljs language-python">trackings[<span class="hljs-string">'hours'</span>] = (pd.to_datetime(trackings.end_at, <span class="hljs-built_in">format</span> = <span class="hljs-string">"%H:%M:%S"</span>) - pd.to_datetime(trackings.start_at, <span class="hljs-built_in">format</span> = <span class="hljs-string">"%H:%M:%S"</span>)) / np.timedelta64(<span class="hljs-number">1</span>, <span class="hljs-string">'h'</span>)
</code></pre>
<p>It was able to read the opened file and explain to me a little bit about the operation of the given code:</p>
<p><img src="/assets/img/articles/2024-01-18-Enhancing-ETL-Python-Project-to-expertise-with-AI-companion-tools/Fbv_Image_4.webp" alt="Copilot chat sample snippet"></p>
<p>In addition it was able to add some improvements suggestions to the code:</p>
<p><img src="/assets/img/articles/2024-01-18-Enhancing-ETL-Python-Project-to-expertise-with-AI-companion-tools/vss_Image_5.webp" alt="Copilot chat suggestion sample snippet"></p>
<p>This method proved beneficial in not only developing the code but also in documenting it simultaneously. Whenever a line of code had ambiguous intentions, the tool assisted by generating explanations and documentation, ensuring each step of the code was clear and well-documented from the outset.</p>
<p><img src="/assets/img/articles/2024-01-18-Enhancing-ETL-Python-Project-to-expertise-with-AI-companion-tools/hPp_Image_6.webp" alt="Copilot documenting code"></p>
<p>Copilot helps a lot when you want to document your code <strong>to improve its readability</strong>.</p>
<h3>Transformation Magic</h3>
<p>Data transformation was a fundamental aspect of my ETL processes. Copilot and ChatGPT became my virtual mentors in this regard. They guided me through the process of applying transformations to the data, showcasing the versatility of Pandas in handling diverse data manipulation tasks. Whether it was altering data types, aggregating information, or reshaping dataframes, Copilot provided clear code suggestions. It is important to mention that in some cases where Copilot had generated some strange code I used ChatGPT in conjunction with Copilot’s suggestions to give me some clear explanations and better approaches. This enabled me to wield Pandas with confidence.</p>
<p>Considerations such as how to use the Pandas 'loc' function to fill data from other columns only when a specific condition is met, or other suggestions where I just need to adjust the names of the columns in the dataframes, are areas where these tools have proven invaluable. They have also assisted in performing calculations within the dataframes, such as the sample of computing the number of hours between a range of dates. This demonstrates that using both AI tools can unleash your potential and streamline complex tasks, saving significant time.</p>
<p>Even when I had to combine data from multiple tables and sources, providing samples in the prompt or within the method, it was capable of generating an entire method with the required output. In this case, I provided sample inputs of the method along with the expected output, and it generated multiple completion options, which was really helpful as it saved me a lot of time. To successfully accomplish these complex tasks, it was necessary to provide sufficient scenarios of data combinations from the input tables and the expected output.</p>
<p>Things like how I could split column tables on rows assigning numeric values in the same method it was able to actually build the whole structure.</p>
<p>Whit the next explanation on the method:</p>
<pre><code class="hljs language-python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">transform_work_calendar_to_user_working_days</span>(<span class="hljs-params">
    users_df: pd.DataFrame,
    work_calendar_df: pd.DataFrame,
</span>) -> pd.DataFrame:
    <span class="hljs-string">"""Converts work calendar to user working days mixing users_df and work_calendar_df in order
    to create a user_working_days_df output matching with UserWorkingDays model,
    with the following columns:
    - user_id: User.Username | Contact.Id depending if the user is a resource contact. Each contact has Work_Calendar_id relationship.
    - numeric_day: Day of the week starting from 0 (Sunday) to 6 (Saturday), use the Work_Calendar.{day}_Hours to identify the day.
    - hours: Work_Calendar.{day}_Hours value as hours in float format.

    Args:
        users_df (pd.DataFrame): users df with user data to match with its information.
        work_calendar_df (pd.DataFrame): work calendar df with work calendar.
    Returns:
        pd.DataFrame: with the MPlanner UserWorkingDays schema columns needed.
    """</span>

</code></pre>
<p>It was able to produce the following implementation:</p>
<pre><code class="hljs language-python">    <span class="hljs-comment"># Transform days to numeric values and placing values to its corresponding numeric day</span>
    <span class="hljs-comment"># Melt the DataFrame to reshape the data</span>
    working_hours_df = pd.melt(
        working_hours_df,
        id_vars=[<span class="hljs-string">"id"</span>],
        value_vars=[
            <span class="hljs-string">"Sunday_Hours"</span>,
            <span class="hljs-string">"Monday_Hours"</span>,
            <span class="hljs-string">"Tuesday_Hours"</span>,
            <span class="hljs-string">"Wednesday_Hours"</span>,
            <span class="hljs-string">"Thursday_Hours"</span>,
            <span class="hljs-string">"Friday_Hours"</span>,
            <span class="hljs-string">"Saturday_Hours"</span>,
        ],
        var_name=<span class="hljs-string">"numeric_day"</span>,
        value_name=<span class="hljs-string">"hours"</span>,
    )

    <span class="hljs-comment"># Map the day names to numbers</span>
    day_to_number = {
        <span class="hljs-string">"Sunday_Hours"</span>: <span class="hljs-number">0</span>,
        <span class="hljs-string">"Monday_Hours"</span>: <span class="hljs-number">1</span>,
        <span class="hljs-string">"Tuesday_Hours"</span>: <span class="hljs-number">2</span>,
        <span class="hljs-string">"Wednesday_Hours"</span>: <span class="hljs-number">3</span>,
        <span class="hljs-string">"Thursday_Hours"</span>: <span class="hljs-number">4</span>,
        <span class="hljs-string">"Friday_Hours"</span>: <span class="hljs-number">5</span>,
        <span class="hljs-string">"Saturday_Hours"</span>: <span class="hljs-number">6</span>,
    }
    working_hours_df[<span class="hljs-string">"numeric_day"</span>] = working_hours_df[<span class="hljs-string">"numeric_day"</span>].<span class="hljs-built_in">map</span>(
        day_to_number
    )
</code></pre>
<p>And it was also able to create the merge output of the data:</p>
<pre><code class="hljs language-python">...
    user_working_hours_df = user_df.merge(
        work_calendar_df,
        left_on=<span class="hljs-string">"Work_Calendar_id"</span>,
        right_on=<span class="hljs-string">"Id"</span>,
        how=<span class="hljs-string">"left"</span>,
    )

    <span class="hljs-keyword">return</span> user_working_hours_df
</code></pre>
<p>The above code is just a sample that I have in the method. With more complex tables, it was also able to combine different tables, rename columns, and make calculations if needed. I just had to make some adjustments to clean the code and align with best practices.</p>
<p>On the other hand, tasks like defining new methods to refactor the code and avoiding duplicated pieces of code are aspects that Copilot does not always identify. When it generates suggestions, I conduct a thorough review of the generated code, removing or calling existing methods or defining new ones as needed. Even in these cases, it helps me to refactor and create new code with minimal effort, without needing specific prompts. Therefore, it's important to pay special attention to these aspects when using a companion tool like Copilot.</p>
<h3>Filtering and Beyond</h3>
<p>Navigating through data filtering posed a significant challenge for me. The guidance I received in crafting and implementing filter conditions within dataframes was tremendously helpful. It wasn't just about understanding the syntax; it was about discovering innovative ways to accurately and efficiently retrieve the necessary data.</p>
<h2>Challenges and Limitations with GitHub Copilot</h2>
<p>While GitHub Copilot undoubtedly played a pivotal role in my ETL project, it was not without its share of challenges and limitations. It's essential to acknowledge these aspects to provide a comprehensive view of my experience. It is important to mention that the challenges come with the prompts you use and that is where you start seeing the limitations of this kind of technology.</p>
<h3>Contextual Understanding</h3>
<p>One notable challenge I encountered was Copilot's occasional difficulty in grasping the full context of my project. This became apparent when it came to fetching columns from tables within my code. Copilot tended to infer common fields found in typical databases, such as 'id,' 'name,' 'full_name,' 'cost,' and others. While these fields are indeed prevalent, my project often required more specific columns that were unique to our data schema and the model on the project used.</p>
<p>In such cases, I found myself having to provide additional context within the prompt, explaining the current columns of a table explicitly. This was necessary to guide Copilot in suggesting the correct fields. It's worth noting that this limitation might have arisen because I couldn't grant Copilot access to the entire codebase due to company privacy agreements so I had to be really careful to not share information relevant to keys and other variables that could imply an exposure risk.</p>
<p>Another noteworthy observation was the evolving precision of Copilot's suggestions as I actively engaged with it. Over time, while Copilot was active and I navigated through the code, it gradually became more precise in its recommendations.</p>
<h3>Quality of Suggestions</h3>
<p>However, it's important to acknowledge that Copilot isn't infallible. There were instances when it provided suggestions that were not entirely aligned with the project's requirements, particularly in handling Pandas dataframes and processing its data in some cases. For example, I encountered a situation where Copilot recommended iterating through dataframe rows using conventional loops. This approach, while technically viable, was less efficient and more cumbersome compared to utilizing Pandas' built-in functions for data transformation. These kinds of suggestions, though well-intentioned, could sometimes lead to undesirable or even incorrect code snippets. This was especially challenging in scenarios where leveraging Pandas' powerful and optimized methods could significantly streamline processes and improve performance. Such instances highlighted the need for careful review and consideration of Copilot's suggestions, especially when implementing more sophisticated data manipulation techniques in Python.</p>
<h2>Recommendations for Improvement</h2>
<p>Based on my experience on this project, I'd like to offer some suggestions for enhancing Copilot's effectiveness to new users:</p>
<ol>
<li>
<p>Access to the whole codebase: To improve Copilot's contextual understanding, granting it access to the entire codebase would be beneficial. This would enable it to better recognize and adapt to project-specific variables, tables, objects, schemas and so on. In that way it could give better suggestions using existent code and giving more context.</p>
</li>
<li>
<p>Context in prompts: Providing more detailed context within prompts could aid Copilot in generating code that aligns closely with the project's needs. For example you could explicitly specifying the desired columns or explaining unique data requirements which will lead to more accurate suggestions.</p>
</li>
<li>
<p>Iterative learning: As observed, Copilot's suggestions became more precise with active usage. So, encouraging developers to interact with Copilot on a project actively and fine-tune its suggestions during the coding process could lead to better results.</p>
</li>
<li>
<p>Don’t use the first suggestion and instead explore different prompts, it possibly suggest better outputs.</p>
</li>
<li>
<p>Before apply a suggestion from Copilot think about which other solution could be better on terms of performance and also taking into account best practices like clean code or applying SOLID principles for example.</p>
</li>
</ol>
<p>Limitations that I found were the plugins on certain IDEs don’t have all the features that Visual Studio Code IDE offers like interacting with Copilot in a similar way as we were chatting to ChatGPT. This feature at the moment of this article has been written is in Beta and not all people have access to it on other IDEs.</p>
<p>Also it is important to mention that even with this companion tools is necessary to have the knowledge of expertise building applications and applying concepts like clean code and clean architecture principles.</p>
<h2>Conclusion: Copilot - ChatGPT</h2>
<p>In summary, GitHub Copilot proved to be an indispensable coding companion throughout my ETL project. It bridged the gap between my basic Python knowledge and the intricate demands of data migration and transformation. Copilot's ability to clarify legacy code, demystify Pandas, and provide guidance on data manipulation was instrumental in achieving the project's objectives. With Copilot's assistance, I not only completed the project successfully but also expanded my Python and data engineering skills significantly. Its ability to accelerate coding, offer insights, and simplify complex tasks is undeniable.</p>
<p>However, it's essential to acknowledge its limitations and work towards improving its contextual understanding and the quality of its suggestions. With continuous development and refinement, Copilot has the potential to become an even more indispensable asset in the developer's toolkit.</p>
<p>On the other hand, ChatGPT is another really valuable companion tool that helps you to find in a easy way the punctual topics that you need to achieve your goals, whether in professional or even in your personal life, showing you clarity, guidance, and really useful suggestions in your tasks. Questions, blockers or lack of comprehension in your daily life could be improved and it could unleash more of your potential. I have to admit that my searches on Google and StackOverflow have decreased significantly using these tools. It’s like talking with an expert without those extensive explanations and really long threads that sometimes just lead you to waste your time and energy without a clear answer.</p>
<p>Obviously, this tools won’t replace the expertise you have gained during years of hard work, and here is another good point to underline: even with all this access to exactly what you need using AI, you always have to make smart decisions and not just copy and paste what these companion tools suggest to you. You have to continuously apply best practices, and depending of your challenge it could guide you to make different decisions than the suggestions made by these tools.</p>
<p><em>Article Photo by Luis Forero generated using DALL-E</em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Behind the screens: Interview with Nairah Thaha, Immersive Technologies Engineer at Monstarlab]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2024/01/11/Interview-Nairah-Thaha</link>
            <guid>https://engineering.monstar-lab.com/en/post/2024/01/11/Interview-Nairah-Thaha</guid>
            <pubDate>Thu, 11 Jan 2024 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Today, we're chatting with <a href="https://www.linkedin.com/in/nairah-thaha-0a37269a/">Nairah Thaha</a>, a skilled Immersive Technologies Engineer at Monstarlab. Nairah joined the Monstarlab team back in March 2023, bringing with her over 7 years of expertise as a Unity developer with a focus on Immersive Technologies (AR, VR and Metaverse).</p>
<p>Born and raised in Dubai, she indulges in a variety of activities that reflect her dynamic personality. From practicing calisthenics and aerial dance to enjoying video games, paddle boarding, and tackling escape rooms, Nairah loves to challenge herself. And when it comes to food, she will never say no to a delicious bowl of ramen.</p>
<p><img src="/assets/img/articles/2024-01-11-Interview-Nairah-Thaha/nairah_ennis.webp" alt="Ennis and Nairah"></p>
<figcaption>In the photo: Ennis Elmudir and Nairah Thaha</figcaption>
<p><em><strong>Nairah, could you tell us about your experience developing immersive technology apps that use generative AI? Could you share a cool prototype you have worked on recently?</strong></em></p>
<p>I’ve started incorporating generative AI into my projects as early as March 2023. When the trend took off, I immediately saw its value and got started on ideating and developing new experiences. The latest one I developed was to help train customer facing employees to deal with difficult client cases. In this scenario, the “customer” was a generative AI human that had a specific banking challenge that you needed to converse with to help solve. The conversation would be recorded and at the end you get an evaluation of how well you handled the situation – what you could have done better, statistics on the conversation as a rating from 1-10, etc. Because the “customers” in the prototype are generative AI, it’s very easy to change out their prompt to handle different situations or even industries.</p>
<p><em><strong>What have been some of the most significant challenges you've faced when creating immersive tech experiences using generative AI? Could you share an example where you had to innovate or think creatively to overcome these challenges?</strong></em></p>
<p>When I had started off back in March, the generative AI scene in Unity was still quite fresh and there weren’t any helpful tools yet. My earliest prototypes had a pretty dissociative issue which was a long delay between you speaking to a generative AI avatar and it responding back to you. This was due to the processes of speech to text, analyzing the question and generating an answer, and then transforming this text back to speech using generative AI to sound more human. As a workaround, I started processing smaller chunks of text at a time, in order to reduce this delay. Eventually, many helpful tools came to market, such as Inworld, that make this process easier to implement and without the speech delay I was facing.</p>
<p><em><strong>In a field as rapidly evolving as immersive tech and generative AI, how do you keep your skills and knowledge up to date?</strong></em></p>
<p>With new challenges and brief timeframes, I’m compelled to find the most efficient and latest technologies to help me develop them. It’s been very helpful to have the freedom to explore new creative solutions and develop them. The constant practice of doing this over the last year has really helped me stay up to date with the latest technology.</p>
<p><em><strong>Looking forward, where do you see the integration of generative AI and immersive technologies heading? What are some advancements you anticipate in creating more engaging or realistic experiences?</strong></em></p>
<p>I feel generative AI is already in a very good state and I trust it to continue to get better over time. Where I anticipate more improvement would be in the realm of more realistic human characters to work with. While the avatars look great, there are nuances in real-time facial experiences and mouth movements that I’d be excited to see improve in order to create better immersion.</p>
<p><em><strong>How has your experience been working at Monstarlab? Could you share some highlights or key moments that have shaped your journey here?</strong></em></p>
<p>I think it would be difficult to highlight only one particular moment. As a whole, the environment I’m in is set up nicely to foster innovation and push the boundaries of what is possible. I also have a great team I can depend on and I cross collaborate regularly with other departments. This helps us come up with some really great solutions.</p>
<blockquote>
<p>As an immersive tech engineer, it’s always important to be up to date with the latest in technological innovation and Monstarlab gives me the platform to do so.</p>
</blockquote>
<p><em><strong>If you had the chance to choose one programming superpower, what would that be and why?</strong></em></p>
<p>Read documentation once and remember it forever 😊. However, I am super happy to see tools such as Unity Muse come out, which keeps all its documentation alive in a generative AI chat, similar to ChatGPT. It’s very handy and has made programming a lot more efficient.</p>
<p><em><strong>What's on your current tech wish list? Any specific gadgets or software tools?</strong></em></p>
<p>Definitely the Meta Quest 3 for its mixed reality capabilities. I’m also looking forward to trying the Apple Vision Pro soon!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Behind the screens: Interview with Adam Mack, Lead Product Manager at Monstarlab]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2024/01/03/Interview-Adam-Mack</link>
            <guid>https://engineering.monstar-lab.com/en/post/2024/01/03/Interview-Adam-Mack</guid>
            <pubDate>Wed, 03 Jan 2024 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Welcome to our interview series!</p>
<p>Today, we're chatting with Adam Mack. With over 15 years at the company, starting back at Fuzz Productions (a digital agency which was <a href="https://monstar-lab.com/global/ml-news/fuzz_join">acquired by Monstarlab in 2019</a>), Adam is a Solution Architect or Technical Product Manager, depending on the day of the week!</p>
<p>Living in Massachusetts, USA, Adam enjoys life with his partner Molly and their cat, Cray-cray. When he's not tackling tech problems, he's into pinball, making and playing video games, creating music, and cooking. Let's dive into his world!</p>
<p><img src="/assets/img/articles/2024-01-03-Interview-Adam-Mack/adam_cesar.webp" alt="Adam and Cesar"></p>
<figcaption>In the photo: Adam Mack and Cesar Aguilar</figcaption>
<p><em><strong>Adam, could you share with us the story of how you entered the field of generative AI? What was the inspiration that led you to focus on this particular area in technology?</strong></em></p>
<p>I was fascinated by Google Deep Dream back in 2015, and the bizarre dreamlike images it could create. That’s when I first became aware of AI image generation as an academic field, and I’ve sort of kept watching the space since then. Fast forward to 2021, and the fairly quiet announcement of Dall-E 1 from OpenAI; I was showing every single person I know the landing page, saying things like “look! It can make an avocado chair out of nothing!!”. I was very excited about the possibilities, to say the least.</p>
<p><em><strong>For those who are new to the concept of generative AI, how would you explain it in simple terms?</strong></em></p>
<p>For friends and family who know nothing about computers, let alone neural networks, I usually refer (half joking) to the analogy of waiting for a million monkeys to accidentally write Shakespeare - what if we could train the monkeys over time to learn what words mean, and which words go next to each other more frequently than others? Even if they can’t fully read or write like you or I can, you’d start to see patterns emerge that resemble Shakespeare (or whatever input) more closely than the random noise you’d otherwise get. Now imagine you can give positive reinforcement to specific monkeys that do a great job of generating Shakespeare-like patterns; you’re part of the way toward visualizing a neural network at work.</p>
<p><em><strong>What do you think are some of the most exciting trends in the field of generative AI? How do these trends influence your work?</strong></em></p>
<p>The most exciting trend, hands down, is seeing how product teams everywhere are taking the “basic” building blocks of generative AI and layering them with the fundamental building blocks that make up their product, leading to new interaction patterns that would have been unimaginatively complex to engineer just a few years ago. Just opening up natural language as a viable user interface for software is a game changer - I think anyone frustrated with the limitations of last gen voice assistants knows how much unrealized promise there is in the ability for a user to naturally converse with a machine. Especially when it comes to the context of providing enhanced accessibility or representation to audiences that are either underserved or have accessibility challenges.</p>
<p><em><strong>Looking ahead, where do you see generative AI heading in the next 5 to 10 years? What kind of advancements or changes do you anticipate?</strong></em></p>
<p>I have so much hope for generative AI’s potential to give an artistic voice to those without the resources or ability to “compete” with commercial interests. Suddenly, if you have a dream for a movie or illustration, it’s no longer locked up in your brain if you lack the artistic ability or tools to realize it. I feel like that could be transformative for society in the same ways that the printing press was transformative, in giving a voice to those who previously lacked a voice, giving access to creating art to those who lacked access before. At the same time, especially as AI outputs become closer to commercially viable replacements for human labor, we’re going to see a huge backlash against these technologies and the use of these technologies in commercial applications. I don’t know who wins that battle for hearts and minds of consumers, but it’ll be interesting. In the meantime, I’m loving that I can create beautiful “oil paintings” with words.</p>
<p><em><strong>You seem to have a strong passion for image generation. Could you share with us an image you created using generative AI tools that particularly amazed you?</strong></em></p>
<p>Great timing! At time of writing, Midjourney 6 just came out. In my many hours experimenting, I’ve coaxed it to create some stunning images. I’ve included 4 of my favorites!</p>
<p><img src="/assets/img/articles/2024-01-03-Interview-Adam-Mack/mid_favs.webp" alt="Midjourney Images"></p>
<p><em><strong>How has your experience been working at Monstarlab? Could you share some highlights or key moments that have shaped your journey here?</strong></em></p>
<p>I’ve been here quite a long time in internet years, growing my career from being a very inexperienced graphic designer, to being a solution architect capable of leading international projects. In many ways, the big opportunities of my career have been largely due to chance, where I accidentally find myself in front of a new opportunity to do something important, and I need to adapt and succeed. So that has always been my takeaway, and my secret to success and longevity in my career growth here:</p>
<blockquote>
<p>Always be ready to seize unusual and unexpected opportunities when they arise. It’ll never be what you expected.</p>
</blockquote>
<p><em><strong>Reflecting on your early days, what is your earliest memory related to coding? Could you tell us about your first project and the experience of working on it?</strong></em></p>
<p>Growing up, before DOS and Windows machines, we had several Atari personal computers around the house, and one I remember most fondly was the 800. I was fascinated by early games like Time Bandit and Pitfall, and it led to me asking my father questions about how games work. That led us to doing little programming exercises together out of magazines, plugging them into BASIC and hoping they ran. I remember one time he led us in daisy chaining a bunch of hardware together for the intended outcome of capturing still images from a VHS and printing them in high contrast black and white on a dot matrix printer as “coloring book” images. Wizardry like that is what sparked my life long interest in software and hardware development.</p>
<p><em><strong>Is there a book, not necessarily related to programming, that has had a significant impact on your thinking or approach to your work?</strong></em></p>
<p>I always love an opportunity to plug this book - I read it in college, and it changed my whole way of approaching critical thinking: <em>How We Know What Isn’t So by Thomas Gilovich</em>. It’s all about the simple lapses in reason that affect everyone, every single day, and how you can recognize and work around them. It really impacted how I evaluate my own thinking and beliefs, and how I approach problem solving.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to use C in Swift Packages]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2024/01/02/SPM-C-Wrapper</link>
            <guid>https://engineering.monstar-lab.com/en/post/2024/01/02/SPM-C-Wrapper</guid>
            <pubDate>Tue, 02 Jan 2024 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>C language is never going away. Not only is it portable, efficient and performant, but it's also
very easy to find a compiler for any platform.
<a href="https://www.sqlite.org/index.html">SQLite</a> might be one of the most well known C library, but it's not
the only one.</p>
<ul>
<li><a href="https://www.lua.org/">Lua</a> scripting language.</li>
<li><a href="https://curl.se/">curl</a> command-line tool for transferring data specified with URL syntax.</li>
<li><a href="https://github.com/libsdl-org/SDL">SDL</a> cross-platform development library designed to
provide low-level access to audio, keyboard, mouse, joystick, and graphics hardware via OpenGL and Direct3D.</li>
</ul>
<p>Python libraries, such as <a href="https://numpy.org/">Numpy</a>, also reach out to C in order
to speed up certain parts of the code.</p>
<p>Recently, while working with images, we discovered that UIKit can't decompress all JPEG2K images.
Since this was a mandatory requirement, we had to find a solution.
After a bit of digging, we came across <a href="https://www.openjpeg.org/">OpenJPEG</a>.
We did a few tests and confirmed that it was able to decompress the images that UIKit couldn't.
The only issue remaining was that <strong>OpenJPEG</strong> was a C library.</p>
<p>To use <strong>OpenJPEG</strong> in an iOS SDK, there were two options:</p>
<ol>
<li>statically compile the library and bundle it in an XCFramework</li>
<li>wrap the source code in a Swift package</li>
</ol>
<p>For option 1, we'd have to deal with CMake, compile it for multiple architectures, archive them in Frameworks
and finally package them in an XCFramework bundle. That seemed to be very time-consuming, so we went with option 2,
which is not without its quirks, but pretty straightforward once you understand it. So let's C how to do that, <em>wink</em> <em>wink</em>.</p>
<p>In this article, we're going to create a small wrapper around the Xoshiro256++ PRNG (Pseudorandom Number Generator).
This is a great PRNG, and it's about 10x to 28x faster than
<code>UInt64.random</code>.</p>
<p>If you wanted to implement this algorithm in Swift you could do it like this.</p>
<p>public final class Xoshiro256PlusPlus: RandomNumberGenerator {
public typealias Seed = (UInt64, UInt64, UInt64, UInt64)
private var s: Seed</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">public</span> <span class="hljs-keyword">init</span>(<span class="hljs-params">seed</span>: <span class="hljs-type">Seed</span> <span class="hljs-operator">=</span> <span class="hljs-type">Xoshiro256PlusPlus</span>.randomSeed()) {
    <span class="hljs-keyword">self</span>.s <span class="hljs-operator">=</span> seed
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">randomSeed</span>() -> <span class="hljs-type">Seed</span> {
    <span class="hljs-keyword">return</span> (
        <span class="hljs-type">UInt64</span>.random(in: <span class="hljs-type">UInt64</span>.min<span class="hljs-operator">...</span><span class="hljs-type">UInt64</span>.max),
        <span class="hljs-type">UInt64</span>.random(in: <span class="hljs-type">UInt64</span>.min<span class="hljs-operator">...</span><span class="hljs-type">UInt64</span>.max),
        <span class="hljs-type">UInt64</span>.random(in: <span class="hljs-type">UInt64</span>.min<span class="hljs-operator">...</span><span class="hljs-type">UInt64</span>.max),
        <span class="hljs-type">UInt64</span>.random(in: <span class="hljs-type">UInt64</span>.min<span class="hljs-operator">...</span><span class="hljs-type">UInt64</span>.max)
    )
}

<span class="hljs-comment">/// Generates a pseduo random number.</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">next</span>() -> <span class="hljs-type">UInt64</span> {
    <span class="hljs-keyword">let</span> result <span class="hljs-operator">=</span> rotl(s.<span class="hljs-number">0</span> <span class="hljs-operator">&#x26;+</span> s.<span class="hljs-number">3</span>, <span class="hljs-number">23</span>) <span class="hljs-operator">&#x26;+</span> s.<span class="hljs-number">0</span>
    <span class="hljs-keyword">let</span> t <span class="hljs-operator">=</span> s.<span class="hljs-number">1</span> <span class="hljs-operator">&#x3C;&#x3C;</span> <span class="hljs-number">17</span>
    s.<span class="hljs-number">2</span> <span class="hljs-operator">^=</span> s.<span class="hljs-number">0</span>
    s.<span class="hljs-number">3</span> <span class="hljs-operator">^=</span> s.<span class="hljs-number">1</span>
    s.<span class="hljs-number">1</span> <span class="hljs-operator">^=</span> s.<span class="hljs-number">2</span>
    s.<span class="hljs-number">0</span> <span class="hljs-operator">^=</span> s.<span class="hljs-number">3</span>
    s.<span class="hljs-number">2</span> <span class="hljs-operator">^=</span> t
    s.<span class="hljs-number">3</span> <span class="hljs-operator">=</span> rotl(s.<span class="hljs-number">3</span>, <span class="hljs-number">45</span>)

    <span class="hljs-keyword">return</span> result
}

<span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">rotl</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">x</span>: <span class="hljs-type">UInt64</span>, <span class="hljs-keyword">_</span> <span class="hljs-params">k</span>: <span class="hljs-type">Int</span>) -> <span class="hljs-type">UInt64</span> {
    <span class="hljs-keyword">return</span> (x <span class="hljs-operator">&#x3C;&#x3C;</span> k) <span class="hljs-operator">|</span> (x <span class="hljs-operator">>></span> (<span class="hljs-number">64</span> <span class="hljs-operator">-</span> k))
}
</code></pre>
<p>}</p>
<pre><code class="hljs language-vbnet">
This <span class="hljs-built_in">is</span> just a simple example, but it<span class="hljs-comment">'s sufficient to explain how public and private interfaces</span>
work <span class="hljs-keyword">in</span> C <span class="hljs-built_in">and</span> how <span class="hljs-keyword">to</span> <span class="hljs-keyword">structure</span> your Swift package. The C code <span class="hljs-built_in">is</span> still
around <span class="hljs-number">10%</span> faster than the Swift version.

You can <span class="hljs-keyword">get</span> the original Xoshiro code at [Xoshiro Generators](https://prng.di.unimi.it/).


## How <span class="hljs-keyword">to</span> use C <span class="hljs-keyword">in</span> Swift packages

Before anything <span class="hljs-keyword">else</span> <span class="hljs-keyword">let</span><span class="hljs-comment">'s create the library package.</span>

```bash
$ mkdir Xoshiro &#x26;&#x26; cd Xoshiro
$ swift package init --name <span class="hljs-string">"Xoshiro"</span> --type library
$ open Package.swift
</code></pre>
<p>Add two new <strong>targets</strong>, <code>CLib</code> and <code>CLibTests</code> and create the
respective folders.</p>
<pre><code class="hljs language-swift">.target(
    name: <span class="hljs-string">"CLib"</span>
),
.testTarget(
    name: <span class="hljs-string">"CLibTests"</span>,
    dependencies: [<span class="hljs-string">"CLib"</span>]
),
</code></pre>
<p>Then let's add the actual code. By default, all header files added in
the include folder are public.</p>
<h4>Small helper function for rotating bits left</h4>
<pre><code class="hljs language-c"><span class="hljs-comment">// Xoshiro/Sources/CLib/include/bitops.h</span>
<span class="hljs-meta">#<span class="hljs-keyword">ifndef</span> bitops_h</span>
<span class="hljs-meta">#<span class="hljs-keyword">define</span> bitops_h</span>

<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;stdint.h></span></span>

<span class="hljs-keyword">extern</span> <span class="hljs-type">uint64_t</span> <span class="hljs-title function_">rotl</span><span class="hljs-params">(<span class="hljs-type">const</span> <span class="hljs-type">uint64_t</span> x, <span class="hljs-type">int</span> k)</span>;

<span class="hljs-meta">#<span class="hljs-keyword">endif</span> <span class="hljs-comment">/* bitops_h */</span></span>
</code></pre>
<pre><code class="hljs language-c"><span class="hljs-comment">// Xoshiro/Sources/CLib/bitops.c</span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"bitops.h"</span></span>

<span class="hljs-keyword">inline</span> <span class="hljs-type">uint64_t</span> <span class="hljs-title function_">rotl</span><span class="hljs-params">(<span class="hljs-type">const</span> <span class="hljs-type">uint64_t</span> x, <span class="hljs-type">int</span> k)</span> {
    <span class="hljs-keyword">return</span> (x &#x3C;&#x3C; k) | (x >> (<span class="hljs-number">64</span> - k));
}
</code></pre>
<h4>Xoshiro256++ PRNG</h4>
<pre><code class="hljs language-c"><span class="hljs-comment">// Xoshiro/Sources/CLib/include/xoshiro256.h</span>
<span class="hljs-meta">#<span class="hljs-keyword">ifndef</span> xoshiro256_h</span>
<span class="hljs-meta">#<span class="hljs-keyword">define</span> xoshiro256_h</span>

<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;stdint.h></span></span>

<span class="hljs-type">void</span> <span class="hljs-title function_">xoshiro256_seed</span><span class="hljs-params">(<span class="hljs-type">uint64_t</span> s1, <span class="hljs-type">uint64_t</span> s2, <span class="hljs-type">uint64_t</span> s3, <span class="hljs-type">uint64_t</span> s4)</span>;
<span class="hljs-type">uint64_t</span> <span class="hljs-title function_">xoshiro256_next</span><span class="hljs-params">(<span class="hljs-type">void</span>)</span>;

<span class="hljs-meta">#<span class="hljs-keyword">endif</span> <span class="hljs-comment">/* xoshiro256_h */</span></span>
</code></pre>
<pre><code class="hljs language-c"><span class="hljs-comment">// Xoshiro/Sources/CLib/xoshiro256.c</span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"xoshiro256.h"</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"bitops.h"</span></span>

<span class="hljs-type">static</span> <span class="hljs-type">uint64_t</span> s[<span class="hljs-number">4</span>];

<span class="hljs-type">void</span> <span class="hljs-title function_">xoshiro256_seed</span><span class="hljs-params">(<span class="hljs-type">uint64_t</span> s1, <span class="hljs-type">uint64_t</span> s2, <span class="hljs-type">uint64_t</span> s3, <span class="hljs-type">uint64_t</span> s4)</span> {
    s[<span class="hljs-number">0</span>] = s1;
    s[<span class="hljs-number">1</span>] = s2;
    s[<span class="hljs-number">2</span>] = s3;
    s[<span class="hljs-number">3</span>] = s4;
}

<span class="hljs-type">uint64_t</span> <span class="hljs-title function_">xoshiro256_next</span><span class="hljs-params">(<span class="hljs-type">void</span>)</span> {
    <span class="hljs-type">const</span> <span class="hljs-type">uint64_t</span> result = rotl(s[<span class="hljs-number">0</span>] + s[<span class="hljs-number">3</span>], <span class="hljs-number">23</span>) + s[<span class="hljs-number">0</span>];
    <span class="hljs-type">const</span> <span class="hljs-type">uint64_t</span> t = s[<span class="hljs-number">1</span>] &#x3C;&#x3C; <span class="hljs-number">17</span>;

    s[<span class="hljs-number">2</span>] ^= s[<span class="hljs-number">0</span>];
    s[<span class="hljs-number">3</span>] ^= s[<span class="hljs-number">1</span>];
    s[<span class="hljs-number">1</span>] ^= s[<span class="hljs-number">2</span>];
    s[<span class="hljs-number">0</span>] ^= s[<span class="hljs-number">3</span>];
    s[<span class="hljs-number">2</span>] ^= t;
    s[<span class="hljs-number">3</span>] = rotl(s[<span class="hljs-number">3</span>], <span class="hljs-number">45</span>);

    <span class="hljs-keyword">return</span> result;
}
</code></pre>
<p>We can easily test if this is generating uniform distributions by
creating a simple test that checks when we generate N random
numbers if all possible results share roughly the same percentage.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> XCTest
<span class="hljs-keyword">@testable</span> <span class="hljs-keyword">import</span> CLib

<span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">CLibTests</span>: <span class="hljs-title class_">XCTestCase</span> {
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">testXoshiro</span>() <span class="hljs-keyword">throws</span> {
        <span class="hljs-keyword">let</span> s <span class="hljs-operator">=</span> (<span class="hljs-number">1</span><span class="hljs-operator">...</span><span class="hljs-number">4</span>).map { <span class="hljs-keyword">_</span> <span class="hljs-keyword">in</span> <span class="hljs-type">UInt64</span>.random(in: <span class="hljs-type">UInt64</span>.min<span class="hljs-operator">...</span><span class="hljs-type">UInt64</span>.max) }

        xoshiro256_seed(s[<span class="hljs-number">0</span>], s[<span class="hljs-number">1</span>], s[<span class="hljs-number">2</span>], s[<span class="hljs-number">3</span>])

        <span class="hljs-keyword">let</span> results <span class="hljs-operator">=</span> <span class="hljs-type">NSCountedSet</span>()
        <span class="hljs-keyword">let</span> options: <span class="hljs-type">UInt64</span> <span class="hljs-operator">=</span> <span class="hljs-number">100</span>
        <span class="hljs-keyword">let</span> samples <span class="hljs-operator">=</span> <span class="hljs-number">1_000_000</span>

        <span class="hljs-keyword">for</span> <span class="hljs-keyword">_</span> <span class="hljs-keyword">in</span> <span class="hljs-number">0</span><span class="hljs-operator">..&#x3C;</span>samples {
            results.add(<span class="hljs-type">UInt8</span>(xoshiro256_next() <span class="hljs-operator">%</span> options))
        }

        <span class="hljs-keyword">for</span> option <span class="hljs-keyword">in</span> <span class="hljs-number">0</span><span class="hljs-operator">..&#x3C;</span>options {
            <span class="hljs-keyword">let</span> actual <span class="hljs-operator">=</span> <span class="hljs-type">Double</span>(results.count(for: option)) <span class="hljs-operator">/</span> <span class="hljs-type">Double</span>(samples)
            <span class="hljs-keyword">let</span> expected <span class="hljs-operator">=</span> <span class="hljs-number">1.0</span> <span class="hljs-operator">/</span> <span class="hljs-type">Double</span>(options)

            <span class="hljs-type">XCTAssertEqual</span>(actual, expected, accuracy: <span class="hljs-number">0.01</span>)
        }
    }
}
</code></pre>
<p>There's one problem though, we have put everything in the <code>include</code>
folder which means every function in both <code>bitops.h</code> and <code>xoshiro256.h</code>
are public.
The user doesn't need to know about <code>uint64_t rotl(const uint64_t x, int k)</code>
in <code>bitops.h</code>, so how do we make it private?</p>
<p>One simple way is to move it to the root folder of its target,
like this.</p>
<pre><code class="hljs language-commandline">Xoshiro
├── Package.swift
└── Sources
    ├── CLib
    │   ├── bitops.c
    │   ├── bitops.h
    │   ├── xoshiro256.c
    │   ├── include
    │   │   └── xoshiro256.h
    └── Xoshiro
        └── Xoshiro.swift
</code></pre>
<p>This causes a <code>'bitops.h' file not found</code> error.</p>
<figure>
<img src="/assets/img/articles/2023-12-22-C-In-SPM/FileNotFound.webp">
</figure>
<p>To fix this you have to tell SPM where to search for header files
by adding a header search path.</p>
<pre><code class="hljs language-swift">.target(
    name: <span class="hljs-string">"CLib"</span>,
    cSettings: [
        .headerSearchPath(<span class="hljs-string">"."</span>)
    ]
),
</code></pre>
<p>Personally, I prefer being more explicit and make it clear what's
public and private by creating <code>private</code> and <code>public</code> folders.</p>
<pre><code class="hljs language-commandline">Xoshiro
├── Package.swift
└── Sources
    ├── CLib
    │   ├── private
    │   │   ├── bitops.c
    │   │   ├── bitops.h
    │   │   └── xoshiro256.c
    │   └── public
    │       └── xoshiro256.h
    └── Xoshiro
        └── Xoshiro.swift
</code></pre>
<p>And then explicitly set the public headers' path.</p>
<pre><code class="hljs language-swift">.target(
    name: <span class="hljs-string">"CLib"</span>,
    publicHeadersPath: <span class="hljs-string">"public"</span>,
    cSettings: [
        .headerSearchPath(<span class="hljs-string">"private"</span>)
    ]
),
</code></pre>
<h2>How to add a Swift interface</h2>
<p>Now the easiest part, wrapping the C interface with a Swift interface.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> CLib

<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Xoshiro256</span>: <span class="hljs-title class_">RandomNumberGenerator</span> {
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">typealias</span> <span class="hljs-type">Seed</span> <span class="hljs-operator">=</span> (<span class="hljs-type">UInt64</span>, <span class="hljs-type">UInt64</span>, <span class="hljs-type">UInt64</span>, <span class="hljs-type">UInt64</span>)

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">init</span>(<span class="hljs-params">seed</span>: <span class="hljs-type">Seed</span>? <span class="hljs-operator">=</span> <span class="hljs-literal">nil</span>) {
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> seed {
            xoshiro256_seed(seed.<span class="hljs-number">0</span>, seed.<span class="hljs-number">1</span>, seed.<span class="hljs-number">2</span>, seed.<span class="hljs-number">3</span>)
        } <span class="hljs-keyword">else</span> {
            xoshiro256_seed(
                <span class="hljs-type">UInt64</span>.random(in: <span class="hljs-type">UInt64</span>.min<span class="hljs-operator">...</span><span class="hljs-type">UInt64</span>.max),
                <span class="hljs-type">UInt64</span>.random(in: <span class="hljs-type">UInt64</span>.min<span class="hljs-operator">...</span><span class="hljs-type">UInt64</span>.max),
                <span class="hljs-type">UInt64</span>.random(in: <span class="hljs-type">UInt64</span>.min<span class="hljs-operator">...</span><span class="hljs-type">UInt64</span>.max),
                <span class="hljs-type">UInt64</span>.random(in: <span class="hljs-type">UInt64</span>.min<span class="hljs-operator">...</span><span class="hljs-type">UInt64</span>.max)
            )
        }
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">next</span>() -> <span class="hljs-type">UInt64</span> {
        <span class="hljs-keyword">return</span> xoshiro256_next()
    }
}
</code></pre>
<h1>Conclusion</h1>
<p>Using C in Swift packages is pretty straightforward once you
know how to structure your code. Being able to use C is especially
useful when dealing with low-level code that needs to be performant.
Unsafe Swift syntax/API is also a bit too verbose for my liking, so I
think it's great that I get to call out to C whenever needed.</p>
<p>I hope this tutorial has made integrating C in Swift packages less
daunting.</p>
<h3>Related Articles</h3>
<ul>
<li><a href="https://developer.apple.com/documentation/swift/c-interoperability">Apple - C Interoperability</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2020/10648/">Apple - Unsafe Swift</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2020/10167/">Apple - Safely manage pointers in Swift</a></li>
</ul>
<p><em>Article Photo by <a href="https://unsplash.com/@inakihxz">Iñaki del Olmo</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to setup CI/CD pipeline for WordPress with GitHub Actions and AWS (Part 2)]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/12/14/How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS-part-2</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/12/14/How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS-part-2</guid>
            <pubDate>Thu, 14 Dec 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>In the <a href="/en/post/2023/01/02/How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS">previous article</a>, we discussed how to set up a <strong>"GitHub hosted runner"</strong> with WordPress. Now, in this part, we will explore how to achieve the same deployment process using a <strong>"Self-hosted runner"</strong></p>
<h2>GitHub Branches and Web Servers Architecture Recap:</h2>
<p>Before diving into the self-hosted runner setup, let's quickly recap our GitHub branches and web servers architecture. We have multiple branches, including <strong>"dev-deploy"</strong> and <strong>"prod-deploy."</strong> The <strong>"dev-deploy"</strong> branch has a dedicated GitHub runner for the development server. After successful testing on the dev server, we are ready to deploy changes to the production server using the <strong>"prod-deploy"</strong> branch.</p>
<p>![Github Branches](/assets/img/articles/2023-12-14-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS-part-2/github.webp</p>

























<table><thead><tr><th>Branch Name</th><th>Our Actions Based on it</th></tr></thead><tbody><tr><td><strong><em>master</em></strong></td><td>Here we keep all up-to-date codebase.</td></tr><tr><td><strong><em>prod-deploy</em></strong></td><td>If any code is merge in this branch, We run a GitHub Action for the production deployment.</td></tr><tr><td><strong><em>dev</em></strong></td><td>We work on this branch for fixing issues and building features.</td></tr><tr><td><strong><em>dev-deploy</em></strong></td><td>If any code is merge in this branch, We run a GitHub Action for development deployment.</td></tr></tbody></table>
<br>
In our production environment, we have an added layer of security provided by a VPN. This VPN connection is necessary to access the webserver, and it is the primary reason why we opted for a self-hosted runner this time.
<p>![server architecture](/assets/img/articles/2023-12-14-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS-part-2/aws.webp</p>
<h2>Options for Self-Hosted Runners:</h2>
<p>With self-hosted runners, we have three options to consider:</p>
<ol>
<li><strong>Repository-level:</strong> Runners dedicated to a single repository.</li>
<li><strong>Organization-level:</strong> Runners capable of processing jobs for multiple repositories within an organization.</li>
<li><strong>Enterprise-level:</strong> Runners assigned to multiple organizations in an enterprise account.
Our example will be based on the repository-level, but you can choose the most suitable option based on your requirements.</li>
</ol>
<h2>Setting Up the Self-Hosted Runner:</h2>
<p>Navigate to the repository settings page and click on <strong>"Actions,"</strong> then select <strong>"Self-Hosted Runner."</strong>
![Github Settings](/assets/img/articles/2023-12-14-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS-part-2/github-settings.webp</p>
<p>Choose the appropriate <strong>"Runner image</strong>" available on your server and select the corresponding <strong>"Architecture"</strong> (e.g., "Linux" and "x64").
![select image](/assets/img/articles/2023-12-14-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS-part-2/image-select.webp</p>
<p>The installation and configuration of the self-hosted runner involve three steps:</p>
<ol>
<li>Download</li>
<li>Configure</li>
<li>Using your self-hosted runner.</li>
</ol>
<p>![self hosted runner](/assets/img/articles/2023-12-14-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS-part-2/Self-Hosted-Runner.webp</p>
<h5>Download:</h5>
<p>In this step, we need to log in to our own server and execute given commands.</p>
<h5>Configure:</h5>
<p>During the configuration, you may encounter some prompts. For the <strong>"group name,"</strong> you can use the default one. Give a name to the runner, such as <strong>"production-deploy,"</strong> which will appear in your runner page on the Github. Finally, set an label, for example, <strong>"prod-deploy."</strong>
![github action runner configure](/assets/img/articles/2023-12-14-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS-part-2/configure.webp</p>
<h5>Using your Self-Hosted Runner:</h5>
<p>Now, you can start listening for job requests and to ensure continuous listening in the background by typing</p>
<pre><code class="hljs language-sh">sudo ./svc.sh install
sudo ./svc.sh start
</code></pre>
<p>With the self-hosted runner successfully configured and listening, any code changes merged into the <strong>"prod-deploy"</strong> branch will trigger the runner, facilitating deployment to our production server.
![github action configure](/assets/img/articles/2023-12-14-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS-part-2/configured-successfull.webp</p>
<h2>Creating the Deployment Automation YML File:</h2>
<h6>Step 1:</h6>
<p>Create a folder named <strong>".github"</strong> in the root of project directory. Inside the <strong>".github"</strong> directory, create another folder named <strong>"workflows."</strong> Within this <strong>"workflows"</strong> folder, create a file named <strong>"prod_deploy.yml."</strong> The file extension must be <strong>".yml."</strong> All the deployment automation code will be written in this file.</p>
<h6>Step 2:</h6>
<p>Provide a meaningful "name" for the workflow. This will help in identifying the workflow in the GitHub Actions interface.</p>
<p>![github action name](/assets/img/articles/2023-12-14-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS-part-2/action-name.webp</p>
<h6>Step 3:</h6>
<p>Configure the <strong>"on"</strong> event to specify when the GitHub Action should run. For instance, we want the workflow to trigger when code is merged into the <strong>"prod-deploy"</strong> branch.</p>
<p>![github action name](/assets/img/articles/2023-12-14-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS-part-2/action-run.webp</p>
<h6>Step 4:</h6>
<p>Set a default directory before running the <strong>"jobs"</strong> Since our root directory hasn't the WordPress theme, we can set the theme directory to avoid changing directories in each step.</p>
<p>![github action name](/assets/img/articles/2023-12-14-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS-part-2/working-directory.webp</p>
<h6>Step 5:</h6>
<p>Define the <strong>"jobs"</strong> within the GitHub workflow. Each job contains a series of <strong>"steps,"</strong> which can be actions or shell scripts. Here, we create a job and specify that it should run only if code is merged into the <strong>"prod-deploy"</strong> branch. We also add the <strong>"prod-deploy"</strong> label, which ensures that the self-hosted runner runs on the associated server.</p>
<p>Now let's see what it looks like when we write all the code together.</p>
<script src="https://gist.github.com/Rasel-Mahmud/bdbe8200c07121a59870379b524e5844.js"></script>
<h5>Let's break down each step in the deployment process:</h5>
<ul>
<li>
<p><strong><code>checkout:</code></strong> This step involves checking out our files into the virtual server using the GitHub checkout action from <a href="https://github.com/actions/checkout">https://github.com/actions/checkout</a>. This ensures that we have the latest code available for the deployment process.</p>
</li>
<li>
<p><strong><code>cache:</code></strong> The cache step is used to store cached data, allowing the theme build process to be faster after the first execution, provided the cache exists. For more information on the cache action, you can refer to <a href="https://github.com/actions/cache">https://github.com/actions/cache</a>.</p>
</li>
<li>
<p><strong><code>Install Node dependency:</code></strong> In this step, we run an action to install the npm package dependencies required for our project. This ensures that the necessary Node modules are available for the subsequent build process.</p>
</li>
<li>
<p><strong><code>Install PHP dependency:</code></strong> Here, we execute an action to install the PHP package dependencies that our WordPress theme relies on. This ensures that the required PHP libraries and components are available for the theme build.</p>
</li>
<li>
<p><strong><code>Build The Theme:</code></strong> This step involves running an internal package command to build our WordPress theme. For this purpose, we are using the <a href="https://labs.tonik.pl/theme/">"Tonic starter"</a> theme, which provides a solid foundation for our WordPress Theme development.</p>
</li>
<li>
<p><strong><code>Move Theme:</code></strong> After the theme is successfully built, we need to move it to the appropriate location on our server. This step utilizes basic shell commands to carry out the process. For example, it may involve removing the existing theme using and moving the current build folder to the appropriate location using</p>
</li>
</ul>
<pre><code class="hljs language-sh">sudo rm -rf /theme-location/theme-name/
sudo mv * /theme-location/theme-name/
</code></pre>
<p>These commands should not be exposed publicly as they reveal sensitive server file paths. To keep these details secure, we can follow the steps below:</p>
<ol>
<li>Go to the Repository and click on the <strong>"Settings"</strong> tab
![github Settings](/assets/img/articles/2023-12-14-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS-part-2/setting-one.webp</li>
<li>In the left side: menu, you will find <strong><code>"actions"</code></strong> under the <strong><code>"secrets"</code></strong> menu.
![github setting](/assets/img/articles/2023-12-14-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS-part-2/setting-two.webp</li>
<li>Click on <strong><code>"New repository secret"</code></strong>.</li>
<li>Provide a <strong><code>"name"</code></strong> on that specific <strong><code>"secret"</code></strong> which we can access it by its name <strong><em><code>${{ secrets.THEME_LOCATION }}</code></em></strong> here <strong><code>"THEME_LOCATION"</code></strong> was the name. <br></li>
</ol>
<p>![github setting](/assets/img/articles/2023-12-14-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS-part-2/theme-location.webp</p>
<p>With the self-hosted runner and GitHub workflow configured, we have established an automated deployment process for our WordPress theme. The self-hosted runner listens for changes in the "prod-deploy" branch, allowing us to deploy the latest changes to our production server. This setup ensures smoother and more efficient deployment while maintaining an extra layer of security through the VPN connection.</p>
<h2>Resources</h2>
<p><em>Article Photo by <a href="https://unsplash.com/@praveentcom">Praveen Thirumurugan</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Mastering Modern JavaScript: From Core Concepts to Advanced Techniques]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/12/06/Mastering-Modern-JavaScript-from-Core-Concepts-to-Advanced-Techniques</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/12/06/Mastering-Modern-JavaScript-from-Core-Concepts-to-Advanced-Techniques</guid>
            <pubDate>Wed, 06 Dec 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Welcome, fellow developers! Here's a deep dive into the core concepts of JavaScript and how TypeScript could enhance them. Regardless of the level of knowledge, mastering the foundations of JavaScript is essential. We want to delve deep into the core concepts of JavaScript and understand how TypeScript changes them. A superset of JavaScript, TypeScript delivers substantial enhancements that give us a full view of the language's capabilities.</p>
<p>This journey's goal is to teach you a thorough understanding of JavaScript's underpinnings as well as the significant enhancements made by TypeScript. By the time you complete reading this article, you will have the knowledge required to apply both in your development projects. Now let's start this illuminating voyage.</p>
<h1><strong>Basic principles of JavaScript</strong></h1>
<p>JavaScript was first primarily used as a simple scripting language to improve web interactivity. However, it lacked a number of sophisticated capabilities found in contemporary programming languages. JavaScript has now evolved into a potent tool for front-end and back-end development with the addition of TypeScript.</p>
<p>Static typing, interfaces, enums, and generics are among the fundamental concepts introduced by TypeScript, a statically typed superset of JavaScript.</p>
<p>It is now an essential component of contemporary online and application development and supports functional, object-oriented, and asynchronous programming paradigms.</p>
<p>There are three main parts to a core JavaScript engine:</p>
<ul>
<li>Call stack</li>
<li>Memory/variable environment</li>
<li>Thread of execution</li>
</ul>
<p>We will address each of these elements in turn in the following sections, offering in-depth analyses for a thorough grasp of how TypeScript improves JavaScript development.</p>
<h3>Unlocking the Power of Reusable Code: Exploring JavaScript Functions with TypeScript</h3>
<p>Within the realm of programming, functions perform as standalone programs when utilised, particularly in JavaScript (which is now further enhanced by TypeScript), and also code becomes more robust and dependable with greater type safety.</p>
<p>Two key considerations need to be made in order for this to function in TypeScript:</p>
<ul>
<li>
<p><strong>Thread of Execution:</strong> This idea entails going through the TypeScript code for the function line by line, much like when you follow a recipe to the letter.</p>
</li>
<li>
<p><strong>Memory:</strong> This memory space enables the function to handle and store crucial information or data needed for its activities, much like a defined storage area. Type annotations in TypeScript facilitate the definition and administration of data structures, improving memory management.</p>
</li>
</ul>
<p><strong>Execution context</strong> is the term used in TypeScript to describe the confluence of these two fundamental components. To ensure that the function does its assigned tasks precisely and type-safely, this framework serves as its TypeScript operating environment.</p>
<h3>A Detailed Overview of JavaScript and TypeScript Function Definition and Usage</h3>
<p>JavaScript and its powerful extension TypeScript both employ the 'function' keyword and the function name to build functions. The syntax for the short arrow function (=>) is an alternative.</p>
<p>Just remember that in order to use a function in TypeScript—that is, to call, execute, or run it—it must always be enclosed in parenthesis. Code reliability and maintainability are enhanced when appropriate types of arguments are passed, thanks to TypeScript's type system. With TypeScript, you may define and use functions with more confidence that your code is valid.</p>
<h3>Tracking Functions in the JavaScript CallStack</h3>
<p>The "global" main function in JavaScript is where all code runs. To comprehend how JavaScript manages this global environment during the execution of a function, consider the "Call Stack".</p>
<p>The stack of plates that represents the call stack is made up of individual function calls. As an illustration:</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">function</span> <span class="hljs-title function_">greet</span>(<span class="hljs-params">name</span>) {
  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`Hello, <span class="hljs-subst">${name}</span>!`</span>);
}

<span class="hljs-keyword">function</span> <span class="hljs-title function_">sayHello</span>(<span class="hljs-params"></span>) {
  <span class="hljs-title function_">greet</span>(<span class="hljs-string">"Alice"</span>);
  <span class="hljs-title function_">greet</span>(<span class="hljs-string">"Bob"</span>);
}

<span class="hljs-title function_">sayHello</span>();
</code></pre>
<ul>
<li>The execution of <code>sayHello()</code> adds to the call stack. Two calls performed inside of <code>sayHello()</code> to <code>greet()</code> have also been added to the Call Stack. The Call Stack tracks the order in which functions are executed.</li>
<li>Handling Nested Functions: The Call Stack efficiently handles nested function management. Every function is removed from the stack after it is finished. For example:
<ul>
<li><code>sayHello()</code> is now included in the Call Stack.</li>
<li><code>sayHello()</code> now includes <code>greet("Alice")</code>.</li>
<li><code>greet("Alice")</code> terminates and is removed.</li>
<li>An additional <code>greet("Bob")</code> is attached.</li>
<li><code>greet("Bob")</code> terminates and is removed.</li>
<li>Upon completion of <code>sayHello()</code>, the Call Stack is cleaned.</li>
</ul>
</li>
</ul>
<p>The Call Stack is essential for debugging and code optimisation since it ensures that functions in JavaScript's global context are executed in a logical order.</p>
<h1><strong>Comprehending Higher Order Functions &#x26; the JavaScript Callback</strong></h1>
<p>Passing a function as an argument to another function can look strange, especially when execution happens later. However, <strong>"callback functions."</strong> is the foundation for asynchronous programming with TypeScript and JavaScript.</p>
<p>Callback functions, or functions that are passed as arguments, are crucial for managing asynchronous tasks in JavaScript and TypeScript. For example, they specify what should happen after an API request is processed or a user activity is finished.</p>
<p>These days, higher-order functions like map, filter, and reduce are part of the toolkit in TypeScript and JavaScript. Also, Promises and the succinct async/await syntax have strong type support in TypeScript.</p>
<h3>Demystifying JavaScript's Functional Programming: Unleashing Pro-Level Functions and Technical Interview Success.</h3>
<p>Using functions, which makes it possible to use strong pro-level functions like reduce, filter, and map, is one of the most misunderstood topics in JavaScript. Functions are important to functional programming. A function is the foundation of a professional mid- to senior-level engineering interview and the means by which our code becomes more declarative and legible in Codesmith.</p>
<p>Using the following syntax, one could write a function that returns the square of 10:</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">function</span> <span class="hljs-title function_">tenSquared</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">return</span> <span class="hljs-number">10</span>*<span class="hljs-number">10</span>;
}

<span class="hljs-title function_">tenSquared</span>() <span class="hljs-comment">// 100</span>
</code></pre>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">function</span> <span class="hljs-title function_">nineSquared</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">return</span> <span class="hljs-number">9</span>*<span class="hljs-number">9</span>;
}

<span class="hljs-title function_">nineSquared</span>() <span class="hljs-comment">// 81 😞</span>

<span class="hljs-comment">// And an 8 squared function? 125 squared?</span>
<span class="hljs-comment">// What principle are we breaking? DRY (Don't Repeat Yourself)</span>
</code></pre>
<pre><code class="hljs language-javascript"><span class="hljs-comment">// We can generalize the function to make it reusable</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">squareNum</span>(<span class="hljs-params">num</span>) {
  <span class="hljs-keyword">return</span> num*num;
}

<span class="hljs-title function_">squareNum</span>(<span class="hljs-number">10</span>); <span class="hljs-comment">// 100</span>
<span class="hljs-title function_">squareNum</span>(<span class="hljs-number">9</span>);  <span class="hljs-comment">// 81</span>
<span class="hljs-title function_">squareNum</span>(<span class="hljs-number">8</span>);  <span class="hljs-comment">// 64</span>
</code></pre>
<h3>Generalizing Functions</h3>
<p>Code flexibility in JavaScript and TypeScript is largely dependent on the idea of generalising functions. "Parameters" (placeholders) allow us to perform our functionality on any data without having to decide on it beforehand.</p>
<p>When we run the function, supply a real value ('argument').</p>
<p>This same approach applies to higher-order functions.</p>
<ul>
<li>Until we run our function, we might not want to decide exactly what part of our functionality is.</li>
</ul>
<h3>Understanding Higher-Order Functions in JavaScript &#x26; TypeScript</h3>
<p>Higher-order functions are essential to TypeScript and functional programming alike. They take functions as parameters and return functions as outcomes, which improves the flexibility and reusability of the code.
Code is kept DRY (Don't Repeat Yourself) by using these utilities to simplify it. They make legible code possible through reduce, filter, and map operations. Add to this combination type safety with TypeScript.</p>
<p>Knowing higher-order functions is essential for professional interviews. Additionally, they are essential to asynchronous JavaScript, supporting ideas like async/await and Promises.</p>
<p>Whether for interviews or contemporary online programmes, knowing higher-order functions is essential to becoming proficient in JavaScript and TypeScript.</p>
<p>Understanding JavaScript's Arrow Functions:</p>
<pre><code class="hljs language-javascript"><span class="hljs-comment">// Introducing arrow functions - a shorthand way to save functions</span>

<span class="hljs-keyword">function</span> <span class="hljs-title function_">multiplyBy2</span>(<span class="hljs-params">input</span>) { <span class="hljs-keyword">return</span> input * <span class="hljs-number">2</span>; }

<span class="hljs-keyword">const</span> <span class="hljs-title function_">multiplyBy2</span> = (<span class="hljs-params">input</span>) => { <span class="hljs-keyword">return</span> input*<span class="hljs-number">2</span> }

<span class="hljs-keyword">const</span> <span class="hljs-title function_">multiplyBy2</span> = (<span class="hljs-params">input</span>) => input*<span class="hljs-number">2</span>

<span class="hljs-keyword">const</span> <span class="hljs-title function_">multiplyBy2</span> = input => input*<span class="hljs-number">2</span>

<span class="hljs-keyword">const</span> output = <span class="hljs-title function_">multiplyBy2</span>(<span class="hljs-number">3</span>) <span class="hljs-comment">// 6</span>
</code></pre>
<p><code>createMultiplier</code>, a higher-order function, uses arrow syntax to return another function. This function multiplies a number by a predetermined amount. One specific example of a <code>createMultiplier</code> that doubles any input integer is <code>multiplyBy2</code>. By utilising <code>multiplyBy2(3)</code>, it is evident that arrow functions can be employed to generate specialised functions that exhibit elegant behaviour, such as returning 6.</p>
<h1><strong>Understanding JavaScript Closures (Scope and Execution Context)</strong></h1>
<p>A closure in JavaScript is a function that works even after the outer function has finished running, allowing it to access variables from its outer scope.</p>
<p>In JavaScript, a closure is a function that returns from the execution of the outer function with access to variables from its outer scope.</p>
<p>Closures are highly helpful even though they were once regarded to be tough. They are now commonly understood. In order to construct reusable modules and improve code structure, they are usually required to maintain private and well-organized data. The usage and understanding of closures in modern JavaScript has increased.</p>
<p>Closures perform Memory-Related tasks</p>
<ul>
<li>We establish a live store of data (local memory/variable environment/state) for the function's execution context when our functions are invoked.</li>
<li>After the function has finished running, its local memory is removed (apart from the returned value).</li>
</ul>
<p>But what if our functions could save real-time data between calls?</p>
<p>This would allow our function definitions to be linked to a cache or permanent memory.</p>
<p>But it all starts with us returning a function from another function.</p>
<p>The Connection:</p>
<p>Upon definition, a function is bound to the Local Memory (sometimes called the "Variable Environment") in which it is defined.</p>
<p>The "Backpack":</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">function</span> <span class="hljs-title function_">outer</span>(<span class="hljs-params"></span>) {
    <span class="hljs-keyword">let</span> counter = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">function</span> <span class="hljs-title function_">incrementCounter</span>(<span class="hljs-params"></span>) {
        counter++;
    }
    <span class="hljs-keyword">return</span> incrementCounter;
}

<span class="hljs-keyword">const</span> myNewFunction = <span class="hljs-title function_">outer</span>();
<span class="hljs-title function_">myNewFunction</span>();
<span class="hljs-title function_">myNewFunction</span>();

<span class="hljs-keyword">const</span> anotherFunction = <span class="hljs-title function_">outer</span>();
<span class="hljs-title function_">anotherFunction</span>();
<span class="hljs-title function_">anotherFunction</span>();
</code></pre>
<p>We obtain <code>incrementCounter's</code> function definition from <code>outer</code> and give it a new name, <code>myNewFunction</code>.</p>
<ul>
<li>The link to <code>outer's</code> live local memory is maintained; it is 'returned out' and attached to <code>incrementCounter's</code> function definition.</li>
<li>As a result, the function <code>outer's</code> local memory is still linked to <code>myNewFunction</code> even if its execution context has long ago terminated.</li>
<li>Running <code>myNewFunction</code> in global mode causes it to look first in its own local memory and then in its <code>"backpack"</code>, as would be expected.</li>
</ul>
<p>Thanks to closure, our functions now have enduring memory and an entire new toolkit for writing well-written code.</p>
<h1><strong>Understanding Asynchronous and Event Loop in JavaScript and TypeScript</strong></h1>
<p>Using TypeScript and JavaScript for asynchronous programming is necessary to create applications that are responsive and efficient. Asynchronous operations enable activities to be completed concurrently without interfering with the main thread of processing, in contrast to synchronous operations that run code one after the other.</p>
<p>An essential part of TypeScript and JavaScript, the event loop makes sure that asynchronous operations are carried out without blocking, resulting in responsive behaviour and economical use of resources.</p>
<p>Promises, the async/await syntax, and the event loop simplify asynchronous tasks in modern JavaScript and TypeScript. This improves the readability and maintainability of the code while guaranteeing smooth performance.</p>
<p><strong>Important elements in this procedure consist of:</strong></p>
<p>One essential ES6 feature that makes writing asynchronous programming easier is promises.</p>
<ul>
<li>Asynchronicity: The ability to handle concurrent tasks is what enables dynamic web applications.</li>
<li>Event Loop: The asynchronous task management triage mechanism built into JavaScript.
Web browser APIs, microtask queues, and callback queues are crucial parts of asynchronous process management.</li>
</ul>
<p>The three primary components of the JavaScript engine are as follows:</p>
<ul>
<li>Thread of execution</li>
<li>Memory and variable environment</li>
<li>Call stack</li>
</ul>
<p>Beyond the capabilities of ECMAScript 5 (ES5), advancements such as Promises and a more efficient Event Loop have been included in more recent iterations of JavaScript and TypeScript. In comparison to the ES5 era, these improvements increase the efficiency and maintainability of handling asynchronous operations and apply to both Node.js and web browsers.</p>
<p><strong>ES6+ Solution with Promises</strong></p>
<p>Promises provide a two-pronged approach to asynchronous task handling in modern JavaScript and TypeScript.
They start web browser work in the background.</p>
<ul>
<li>Without delay, they provide a placeholder object, or promise.</li>
</ul>
<p>These Promises adhere to the following guidelines for running code that is deferred asynchronously:
A microtask queue is used to store promise-deferred functions, and a task queue (called the Callback queue) is used for callback functions once the associated Web Browser Feature (API) has finished.
Functions are added to the Call stack (that is, executed) once all global code has run and the Call stack is empty. This state is kept an eye on by the Event Loop.</p>
<p>Promises let JavaScript and TypeScript do the following things when used with Web APIs, Callback &#x26; Microtask Queues, and the Event Loop:</p>
<ul>
<li>Develop non-blocking programmes that enable the execution of multiple threads of code concurrently without waiting.</li>
<li>Take care of scheduling the completion of Browser features automatically, guaranteeing effective code execution.
Act as the foundation for contemporary online applications, making it possible to create web solutions quickly and without stuttering.</li>
</ul>
<h1><strong>Classes &#x26; Prototypes (OOP)</strong></h1>
<p>For object-oriented programming, JavaScript has historically employed prototypes, which may be confusing to developers used to working with class-based systems.</p>
<p>However, object-oriented programming in JavaScript became more recognisable to developers of other languages with the release of ECMAScript 6 (ES6), which introduced a class syntax. Although JavaScript classes still use prototypes internally, the new syntax offers a more organised and understandable method for generating and extending objects.</p>
<p>Important things to remember:</p>
<ul>
<li>Classes are a commonly used paradigm for structuring complex code in TypeScript and JavaScript.</li>
<li>Prototype chain, an efficient tool in and of itself that enables the replication of Object-Oriented Programming (OOP), is one such background-working component.</li>
<li>The difference between <strong>prototype</strong> and <strong>proto</strong> must be understood for effective OOP in JavaScript and TypeScript.</li>
<li>Programming is made easier by using the <code>new</code> and <code>class</code> keywords, which automate the generation of objects and methods.</li>
</ul>
<h1><strong>proto</strong>:</h1>
<p>Every JavaScript object, including arrays, functions, and other objects, has a property called <strong>proto</strong>. JavaScript initially verifies that a property or function on an object actually exists before allowing you to access it.</p>
<h1><strong>prototype</strong>:</h1>
<p>In TypeScript and JavaScript, every function has the property prototype. The prototype property of a constructor function becomes the prototype of the newly generated object when it is defined and instances are created using the new keyword.</p>
<p>A Guide for Writing and Using Code</p>
<ol>
<li>Save data (for example, user 1 and user 2's quiz scores).</li>
<li>Use the data to run code (functions) (e.g., enhance user 2's score)</li>
</ol>
<p>Simple! Then why is development challenging? I have to save a lot of users in a quiz game, along with administrators, quiz questions, quiz results, league tables, and other data and functionality.</p>
<p>Where's the functionality when I need it in 100,000 lines of code?</p>
<ul>
<li>How can I ensure that the functionality is only applied to appropriate data?</li>
</ul>
<p>To make things simple, let's say I save each user's specific data in my app:</p>




















<table><thead><tr><th>User</th><th>Name</th><th>Score</th></tr></thead><tbody><tr><td>user1</td><td>Tim</td><td>3</td></tr><tr><td>user2</td><td>Stephanie</td><td>5</td></tr></tbody></table>
<br>
And (simplifying again!) the features I have for every user are: greater functionality (there would actually be a multitude of features!).
<p>How might my entire set of features and information be in one place?</p>
<pre><code class="hljs language-javascript"><span class="hljs-comment">// Solution 1. Generate objects using a function</span>

<span class="hljs-keyword">function</span> <span class="hljs-title function_">userCreator</span>(<span class="hljs-params">name, score</span>) {
    <span class="hljs-keyword">const</span> newUser = {};
    newUser.<span class="hljs-property">name</span> = name;
    newUser.<span class="hljs-property">score</span> = score;
    newUser.<span class="hljs-property">increment</span> = <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) {
        newUser.<span class="hljs-property">score</span>++;
    };
    <span class="hljs-keyword">return</span> newUser;
}

<span class="hljs-keyword">const</span> user1 = <span class="hljs-title function_">userCreator</span>(<span class="hljs-string">"Will"</span>, <span class="hljs-number">3</span>);
<span class="hljs-keyword">const</span> user2 = <span class="hljs-title function_">userCreator</span>(<span class="hljs-string">"Tim"</span>, <span class="hljs-number">5</span>);
user1.<span class="hljs-title function_">increment</span>();
</code></pre>
<p><strong>Problems</strong>: Our computer has to allocate memory for all of our data and features each time we add a new user. But all we are doing is copying functions. Does a better approach exist?</p>
<p><strong>Advantages</strong>: It's easy to understand and consider!</p>
<pre><code class="hljs language-javascript"><span class="hljs-comment">// Solution 2: Using the prototype chain</span>

<span class="hljs-keyword">function</span> <span class="hljs-title function_">userCreator</span>(<span class="hljs-params">name, score</span>) {
    <span class="hljs-keyword">const</span> newUser = <span class="hljs-title class_">Object</span>.<span class="hljs-title function_">create</span>(userFunctionStore);
    newUser.<span class="hljs-property">name</span> = name;
    newUser.<span class="hljs-property">score</span> = score;
    <span class="hljs-keyword">return</span> newUser;
}

<span class="hljs-keyword">const</span> userFunctionStore = {
    <span class="hljs-attr">increment</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) { <span class="hljs-variable language_">this</span>.<span class="hljs-property">score</span>++; },
    <span class="hljs-attr">login</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) { <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">"Logged in"</span>); }
};

<span class="hljs-keyword">const</span> user1 = <span class="hljs-title function_">userCreator</span>(<span class="hljs-string">"Will"</span>, <span class="hljs-number">3</span>);
<span class="hljs-keyword">const</span> user2 = <span class="hljs-title function_">userCreator</span>(<span class="hljs-string">"Tim"</span>, <span class="hljs-number">5</span>);
user1.<span class="hljs-title function_">increment</span>();
</code></pre>
<h2>Understanding TypeScript to Enhance JavaScript Development</h2>
<p>We studied the hard JavaScript sections as well as the basic concepts and challenges of this dynamic language. All things considered, the rise in popularity of TypeScript, is one of the most significant advancements in JavaScript lately.</p>
<p>Adding TypeScript to a project has several advantages:</p>
<ol>
<li>Maintainability of Code</li>
<li>Enhanced Developer Productivity</li>
<li>Improved Collaboration</li>
<li>Scalability</li>
<li>Reduced Number of Bugs</li>
<li>Ecosystem and Community</li>
</ol>
<p>Furthermore, TypeScript facilitates faster code reviews, more comprehensible code, and easier onboarding of new team members—all of which enhance developer collaboration.</p>
<h1>Conclusion</h1>
<p>As a result of our thorough investigation of JavaScript and its potent extension, TypeScript, we have gained a profound understanding of the underlying ideas that underpin this adaptable programming language. We have studied the management of intricate processes by JavaScript, thanks to its asynchronous features such as callbacks and higher-order functions. The event loop and closures, which are crucial to comprehending JavaScript's execution mechanism, have also been clarified.</p>
<p>We are more equipped to take on challenging coding tasks as we continue to explore the nuances of JavaScript, enhanced by TypeScript's extensions. Our objective is to develop applications that are resilient, adaptable, and scalable to the demands of the ever-changing digital landscape. So, fellow coders, let's embrace TypeScript and JavaScript to the fullest and use their combined strength to innovate and change the digital landscape.</p>
<p>Happy Coding!</p>
<p><em>Article Photo by <a href="https://unsplash.com">Unsplash</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[First look at AndroidX Bluetooth]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/11/09/First-Look-at-AndroidX-Bluetooth</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/11/09/First-Look-at-AndroidX-Bluetooth</guid>
            <pubDate>Thu, 09 Nov 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>AndroidX Bluetooth is a new addition to the <a href="https://developer.android.com/jetpack">Jetpack Suite</a> of libraries. While currently in its alpha stage, the system already provides robust safety measures addressing <a href="https://punchthrough.com/android-ble-development-tips/">common pitfalls</a> in Android BLE development. Moreover, it establishes a clear trajectory for further improvements. Official Android Developer Documentation describes AndroidX Bluetooth as:</p>
<blockquote>
<p>This is the initial release of AndroidX Bluetooth APIs that provides a Kotlin API surface covering Bluetooth LE scanning and advertising, and GATT client and server use cases. It provides a minimal API surface, clear thread model with async and sync operations, and ensures all methods be executed and provides the results.</p>
</blockquote>
<p>Sounds very promising, but less talk, more action, let's dive deeper into what AndroidX Bluetooth library brings to the table!</p>
<h2>How to use AndroidX Bluetooth library</h2>
<p>First of all, import the artifact in your <code>build.gradle</code>.</p>
<pre><code class="hljs language-groovy">implementation <span class="hljs-string">"androidx.bluetooth:bluetooth:1.0.0-alpha01"</span>
</code></pre>
<p><em>Note: current minSdk of the artifact might be too high (33 at the point of writing this article) and it is intended. They are gradually lowering the minSdk as part of integration testing/for people to not use it in production since it is still in alpha.</em></p>
<h2>Working with the library</h2>
<p>With the basic introductions done, it is time to buckle up and start coding! For the sake of simplifying the article we will skip the permission handling, checking whether device has bluetooth and if it is enabled and move straight to the specifics of the AndroidX Bluetooth library.</p>
<h3><code>BluetoothLe</code> class</h3>
<p><code>BluetoothLe</code> class is the main entry point of the library. Internally, it contains <code>GattClient</code> and <code>GattServer</code> classes which are responsible for the GATT client and GATT server role, respectively. Every usecase you have will go through this class.</p>
<p>Currently <code>BluetoothLe</code> class has 4 main methods:</p>
<ul>
<li><code>scan</code> - launches peripheral scanner</li>
<li><code>connectGatt</code> - handles the connection to the peripheral</li>
<li><code>advertise</code> - starts BLE advertiser</li>
<li><code>openGattServer</code> - starts GATT server</li>
</ul>
<p>To instantiate <code>BluetoothLe</code> class, simply call its constructor where you have to supply it with <code>Context</code>.</p>
<h2>GATT Client/Central</h2>
<p>Now that we have our <code>BluetoothLe</code> class set up, let's dive deeper into GATT client/central role and its main responsibilities. That includes scanning for peripherals, connecting and communicating with them via read/write operations.</p>
<h3>Scanning</h3>
<p>Let's start with the first operation we have to do when doing any business with the bluetooth devices - the scan.</p>
<p>To launch a scan, simply call the <code>bluetoothLe.scan(List&#x3C;ScanFilter>)</code> method. It has one parameter, which is a list of scan filters and it returns a <code>Flow</code> of <code>ScanResult</code>s, which is very convenient! No more callback hells, instead we have a <strong>cold</strong> flow that emits devices once they are detected by the hardware scanner (and passes your scan filters).</p>
<p>You can notice that there is no way to set  <code>ScanSettings</code> for the scan since it doesn't exist as a parameter for the function. <code>ScanSettings</code> allows developers to set the scan mode, match mode, PHY, etc. Since under the hood it still uses framework's <code>bluetoothLeScanner</code>, it is still mandatory to specify, but for now it is not implemented in AndroidX library and it uses default <code>ScanSettings</code> instead:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">val</span> scanSettings = ScanSettings.Builder().build()  
bleScanner?.startScan(fwkFilters, scanSettings, callback)
</code></pre>
<p>There is also no method to stop the scan, but by checking the code inside the library we can see the following code:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">scan</span><span class="hljs-params">(filters: <span class="hljs-type">List</span>&#x3C;<span class="hljs-type">ScanFilter</span>> = emptyList()</span></span>): Flow&#x3C;ScanResult> = callbackFlow {

  <span class="hljs-comment">// scan method body</span>
  
  awaitClose {  
    bleScanner?.stopScan(callback)  
  }  
}
</code></pre>
<p>Notice the <code>awaitClose</code>? It suspends the function/coroutine until it is either closed or cancelled and invokes the code block. Therefore in order to stop the scan we would need to cancel the <code>Job</code> that collects the scan results. A draft of the code would look like this:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> scanJob: Job? = <span class="hljs-literal">null</span>

<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">startScanning</span><span class="hljs-params">()</span></span> {  
  scanJob = viewModelScope.launch {
    <span class="hljs-keyword">try</span> {
      bluetoothLe.scan().collect { scanResult ->
        <span class="hljs-comment">// do something with the scan results</span>
      } 
    } <span class="hljs-keyword">catch</span> (exception: Exception) {
      <span class="hljs-comment">// handle scan errors</span>
    }
  }
}  
  
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">stopScanning</span><span class="hljs-params">()</span></span> {  
  scanJob?.cancel()  
  scanJob = <span class="hljs-literal">null</span>  
}
</code></pre>
<p>Looks quite tidy and clean! Now you can take your device from the scan results and connect to it.</p>
<h3>Connecting</h3>
<p>To connect to the peripheral, one should make use of the <code>connectGatt</code> method.</p>
<pre><code class="hljs language-kotlin">bluetoothLe.connectGatt(BluetoothDevice, <span class="hljs-keyword">suspend</span> GattClientScope.() -> R)`
</code></pre>
<p>There are two parameters for developers to fiddle with:</p>
<ul>
<li><code>BluetoothDevice</code> - library's wrapper around framework's <code>BluetoothDevice</code>. You can receive it from the <code>ScanResult</code> when scanning for peripherals (more details in the scanning section above)</li>
<li><code>suspend GattClientScope.() -> R</code> - lambda which is marked as suspend. It invokes once the connection with the peripheral is established. Since it is also an extension of <code>GattClientScope</code> you can call various methods on it to communicate with the remote device.</li>
</ul>
<p>Therefore, to connect to the peripheral, you'd need to have something like the following:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-comment">// every communication is going through this object once the device is connected  </span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> gattClient: BluetoothLe.GattClientScope? = <span class="hljs-literal">null</span>

<span class="hljs-keyword">try</span> {
  bluetoothLe.connectGatt(bluetoothDevice) {  
    gattClient = <span class="hljs-keyword">this</span>
  }
} <span class="hljs-keyword">catch</span> (exception: Exception) {
  <span class="hljs-comment">// handle connection errors</span>
}
</code></pre>
<p>Since lambda is an extension of <code>GattClientScope</code>, we can save it to the outside variable, similar to how you'd save <code>BluetoothGatt</code> object when working with the framework's BLE SDK.</p>
<p>One thing to keep in mind is that <strong>the connection will be dropped the moment</strong> <code>suspend GattClientScope.() -> R</code> <strong>lambda finishes</strong>. To negate that, you need to <strong>suspend</strong> the lambda at the end by calling <code>awaitCancellation()</code>. That will also allow you to control when to disconnect from the device, similarly to how we stopped the scan earlier. Therefore, the connectivity block might look like this:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-comment">// every communication is going through this object once the device is connected  </span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> gattClient: BluetoothLe.GattClientScope? = <span class="hljs-literal">null</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> connectJob: Job? = <span class="hljs-literal">null</span>

<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">connect</span><span class="hljs-params">()</span></span> {
  connectJob = coroutineScope.launch {
    <span class="hljs-keyword">try</span> {
      bluetoothLe.connectGatt(bluetoothDevice) {  
        gattClient = <span class="hljs-keyword">this</span>
        awaitCancellation()
      }
    } <span class="hljs-keyword">catch</span> (exception: Exception) {
      <span class="hljs-comment">// handle connection errors</span>
    }
  }
}

<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">disconnect</span><span class="hljs-params">()</span></span> {
  connectJob?.cancel()  
  connectJob = <span class="hljs-literal">null</span>  
}
</code></pre>
<p>After it establishes the connection, the library automatically does the initial flow of setting up the GATT. The flow looks like this: <em>connection success >> request MTU >> 515 >> discover services >> we're ready!</em> The only flaw in this is that if the MTU request fails, then the whole connection is dropped. Granted that the MTU request is not a mandatory operation, that seems quite extreme (at least I wasn't been able to connect to my virtual peripheral, because it was failing the MTU request and had to find another way to host a GATT server)</p>
<p>Other than that, the whole connectivity process was smooth and I was able to have a working connectable device in no time.</p>
<h3>Communicating</h3>
<p>Once the connection is established, it is time to do some communication, i.e. read/write. Previously we saved <code>GattClientScope</code> to an outer variable. This class has a minimum amount of capabilities to start communicating with the remote device. For example, you can check all the characteristics of a certain service by calling the following method:</p>
<pre><code class="hljs language-kotlin">gattClient.services.first().characteristics
</code></pre>
<p>Or when you need to read from characteristic or write to it, you can do it all by using the same <code>GattClientScope</code>:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-comment">// Find the characteristic</span>
<span class="hljs-comment">// Please note, that this is just an example, you would need to find characteristic</span>
<span class="hljs-comment">// by its' UUID</span>
<span class="hljs-keyword">val</span> myCharacteristic = gattClient.services.first().characteristics.first()

<span class="hljs-comment">// read characteristic; this is a suspend function</span>
<span class="hljs-keyword">val</span> result = gattClient.readCharacteristic(myCharacteristic)

<span class="hljs-comment">// write characteristic; this is a suspend function</span>
gattClient.writeCharacteristic(characteristic, valueToWrite.toByteArray())
</code></pre>
<p>Besides those standard operations, <code>GattClientScope</code> also able to subscribe to characteristic and return flow of values:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-comment">// not a suspend function; returns Flow instead</span>
<span class="hljs-keyword">val</span> byteArrayFlow = gattClient.subscribeToCharacteristic(myCharacteristic)
coroutineScope.launch {
  byteArrayFlow.collect { byteArray ->
    <span class="hljs-comment">// process byte arrays coming from peripheral's notifications </span>
  }
}
</code></pre>
<h3>Operation queuing</h3>
<p>Developers who worked with Android BLE SDK know that it is forbidden to perform operations in rapid-fire succession. Essentially, if you are not using some third-party library, you would need to implement a queuing mechanism that would execute your BLE operations sequentially, one-by-one (any operation, such as discover services, request MTU, read/write, etc). If you don't do that, chances are that some of the operations will be simply dropped/ignored causing software misbehavior.</p>
<p>In AndroidX Bluetooth that seems to be finally taken care of and developers can finally not worry about creating a queuing mechanism for their operations. That is due to the fact that internally the library uses Kotlin's <code>Mutex</code>.  <a href="https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/-mutex/">Mutex</a> essentially suspends the function until the shared resource is free. But the important part there is that the created mutex is fair: lock is granted in first come, first served order. In another words, Mutex allows us to create a queue for BLE operations. The code snippet below was taken from the AndroidX Bluetooth source code, modified for article's simplicity and enriched with some comments explaining how it works.</p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">val</span> gattScope = <span class="hljs-keyword">object</span> : BluetoothLe.GattClientScope {

  <span class="hljs-comment">// create Mutex</span>
  <span class="hljs-keyword">val</span> taskMutex = Mutex()
  
  <span class="hljs-comment">// helper function to make use of Mutex</span>
  <span class="hljs-comment">// will be used down below in readCharacteristic function</span>
  <span class="hljs-keyword">suspend</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-type">&#x3C;R></span> <span class="hljs-title">runTask</span><span class="hljs-params">(block: <span class="hljs-type">suspend</span> () -> <span class="hljs-type">R</span>)</span></span>: R {  
    taskMutex.withLock {  
      <span class="hljs-keyword">return</span> block()  
    }  
  }  
  
  <span class="hljs-keyword">override</span> <span class="hljs-keyword">suspend</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">readCharacteristic</span><span class="hljs-params">(characteristic: <span class="hljs-type">GattCharacteristic</span>)</span></span>: Result&#x3C;ByteArray> {  
    <span class="hljs-comment">// the runTask function locks the mutex  </span>
    <span class="hljs-keyword">return</span> runTask {
      <span class="hljs-comment">// read the characteristic safely, inside the mutex</span>
      fwkAdapter.readCharacteristic(characteristic.fwkCharacteristic)  
      <span class="hljs-comment">// the function below suspends until we get the result from the read operation </span>
      <span class="hljs-keyword">val</span> res = takeMatchingResult&#x3C;GattClient.CallbackResult.OnCharacteristicRead>(  
        callbackResultsFlow  
      ) {  
        it.characteristic == characteristic  
      }  
  
      <span class="hljs-comment">// return the result, unlock the mutex</span>
    }  
  }  
}
</code></pre>
<p>Mutex is used in read, write and subscribe operations, which means that if you somehow rapid-fire read operations along with discover services, request MTU or some other, then you still can theoretically lose some requests, but we are going into an edge case now since AndroidX Bluetooth doesn't currently have the capability to enqueue discover services request through their API.</p>
<h2>GATT Server/Peripheral</h2>
<p>Besides being the client most of the time, Android can also be a server! Since Android 5+ some of the devices can act as peripheral/server, but be aware that not all of the devices support that on the hardware level. You can find what phones support that in <a href="https://altbeacon.github.io/android-beacon-library/beacon-transmitter-devices.html">that list</a>.</p>
<p>To open the GATT server, you would need to call <code>openGattServer</code> method.</p>
<pre><code class="hljs language-kotlin">bluetoothLe.openGattServer(List&#x3C;GattService>, <span class="hljs-keyword">suspend</span> GattServerConnectScope.() -> R)
</code></pre>
<p>There are multiple design similarities with the previously described operations. Let's dig deeper! There are two parameters, similarly to how client connection works:</p>
<ul>
<li><code>List&#x3C;GattService></code> - services and characteristics that the server will hold.</li>
<li><code>suspend GattServerConnectScope.() -> R</code> - lambda which is marked as suspend. It executes once the server is opened. Again, similarly to how client connection works, it's also an extension of <code>GattServerConnectScope</code>.</li>
</ul>
<p><code>GattServerConnectScope</code> doesn't have much to offer from the first glance. You can update services by invoking the corresponding method <code>updateServices(List&#x3C;GattService>)</code> and listen to the connect requests by utilizing <code>connectRequests</code> Flow. The latter one is a little bit more interesting since you are collecting <code>GattServerConnectRequest</code>s. It contains the <code>BluetoothDevice</code> from which the request is coming from and methods to accept (has yet another lambda) or reject the request. If you accept the request, you are basically establishing the connection with the client and can start processing read/write requests, notify the client via BLE Notifications, etc.</p>
<p>Therefore, to open the GATT server, you'd need to have something like the following:</p>
<pre><code class="hljs language-kotlin">bluetoothLe.openGattServer(gattServerServices) { <span class="hljs-comment">// opens GATT server</span>
   connectRequests.collect { connectRequest ->   <span class="hljs-comment">// collets clients' connection requests</span>
     connectRequest.accept {                     <span class="hljs-comment">// accepts the connection request (you can also reject it)</span>
       requests.collect { serverRequest ->       <span class="hljs-comment">// collects client's read/write requests, etc</span>
         <span class="hljs-comment">// do something with server request</span>
       }
     }
   }
}
</code></pre>
<p>You can further improve the snippet above by adding <code>CoroutineScope.launch</code> before opening GATT server like we did in the scanning/connectivity before, cancelling it whenever you need to close the server and/or adding parallelism by launching a separate coroutine for each <code>connectRequest</code>, etc. The sky is the limit!</p>
<h2>Advertiser</h2>
<p>Lastly, you can also have a device advertiser. You can do it by calling the <code>advertise</code> method.</p>
<pre><code class="hljs language-kotlin">bluetoothLe.advertise(AdvertiseParams, (<span class="hljs-keyword">suspend</span> (<span class="hljs-meta">@AdvertiseResult</span> <span class="hljs-built_in">Int</span>) -> <span class="hljs-built_in">Unit</span>)?)
</code></pre>
<p>This method has two parameters:</p>
<ul>
<li><code>AdvertiseParams</code> - provides a way to adjust advertising preferences and advertise data packet.</li>
<li><code>(suspend (@AdvertiseResult Int) -> Unit)?</code> - an optional block of code that is invoked when advertising is started or failed.</li>
</ul>
<p><code>AdvertiseParams</code> has a lot of preferences to modify, let's go over them one-by-one:</p>
<ul>
<li><code>shouldIncludeDeviceAddress</code> - sets whether the device address will be included in the advertisement packet.</li>
<li><code>shouldIncludeDeviceName</code> - sets whether the device name will be included in the advertisement packet.</li>
<li><code>isConnectable</code> - sets whether the advertisement will indicate connectable.</li>
<li><code>isDiscoverable</code> - sets whether the advertisement will be discoverable. Note that it would be ignored under API level 34 and <code>isConnectable</code> would be used instead.</li>
<li><code>durationMillis</code> - sets advertising duration in milliseconds. Supports values from 0 to 655350.</li>
<li><code>manufacturerData</code> - sets a map of company identifiers to manufacturer specific data.</li>
<li><code>serviceData</code> - sets a map of 16-bit UUIDs of the services to corresponding additional service data.</li>
<li><code>serviceUuids</code> - sets a list of service UUIDs to advertise.</li>
</ul>
<p>As you can see, there are a lot of preferences to fiddle with, but more importantly, under the hood, it has all the conditionals to ensure backwards compatibility so you don't need to have a hell of API-level version checks. It is all already done! And that applies to all of the operations we have gone through in this article such as scanning, connectivity, hosting a GATT server, etc!</p>
<p>Lastly, this is how the code would look like if you need to advertise your device:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">val</span> advertiseParams = AdvertiseParams(  
  shouldIncludeDeviceAddress = <span class="hljs-literal">false</span>,  
  shouldIncludeDeviceName = <span class="hljs-literal">false</span>,  
  isConnectable = <span class="hljs-literal">false</span>,  
  isDiscoverable = <span class="hljs-literal">false</span>,  
  durationMillis = <span class="hljs-number">0</span>,  
  manufacturerData = mutableMapOf(),  
  serviceData = mutableMapOf(),  
  serviceUuids = mutableListOf(),  
)
bluetoothLe.advertise(advertiseParams) {
  <span class="hljs-comment">// do something with the AdvertiseResult</span>
}
</code></pre>
<h2>Room for Improvement</h2>
<p>There is definitely room for improvement for the library. Some of them include:</p>
<ul>
<li>Consistency. Some methods of <code>BluetoothLe</code> suspend the function and you need to cancel it to interrupt the operation and other methods don't suspend the function and you have to add <code>awaitCancellation</code> yourself (looking at you <code>connectGatt</code> method 👀).</li>
<li>Make request MTU not mandatory when establishing the connection with the peripheral.</li>
<li>Allow developers to modify <code>ScanSettings</code> when scanning for peripherals, but I am sure it will come in future versions.</li>
</ul>
<h2>Conclusion</h2>
<p>To wrap it up, here are the key takeaways for the AndroidX Bluetooth library:</p>
<ul>
<li>AndroidX Bluetooth is a Jetpack library that adds synchronous way to handle bluetooth operations.</li>
<li>AndroidX Bluetooth allows developers to scan, connect, communicate, create their own GATT servers and advertise their devices.</li>
<li>AndroidX Bluetooth is heavily leveraging Kotlin Coroutines, which makes collecting results and/or cancelling operations relatively easy.</li>
<li>AndroidX Bluetooth handles all API level version specifics for you. Less version checks conditionals = good.</li>
<li>It is still in alpha and a lot of features are missing and there is room for improvement, but it is a very good start and makes third-party Android BLE libraries a bit less mandatory for the real world development</li>
</ul>
<p>_ Article Photo by <a href="https://unsplash.com/@stenslens">Sten Ritterfeld</a> on <a href="https://unsplash.com/photos/black-and-white-remote-control-psKil0FkS58">Unsplash</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Utilising the iOS Secure Enclave with your data]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/10/31/Utilising-the-iOS-Secure-Enclave-with-your-data</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/10/31/Utilising-the-iOS-Secure-Enclave-with-your-data</guid>
            <pubDate>Tue, 31 Oct 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h1>The Secure Enclave</h1>
<p>The Secure Enclave is a pivotal part of most modern Apple devices. On your iPhone, it's what keeps secure your most sensitive information. Information such as your biometric data, to use with Face &#x26; Touch ID and your payment information that can be stored within Apple Pay. It's a dedicated distinct hardware component, isolated from the main processor making it super secure. It being isolated, means it can't be directly accessed by the main processor or any other applications that may find a way to gain un-authorized access. It also means this data will only ever be accessible via the device that encrypted it. In essence, the Secure Enclave acts as a secure vault within the iPhone's hardware architecture, dedicated to managing and protecting sensitive information critical to the device's security features.</p>
<p>Often people's first assumption is that the Secure Enclave includes storage for all types of data, similar to like how we use Apple Keychain or UserDefaults services, but it doesn't. What it can do is use Apple's distinct Secure Enclave algorithms to provide and store encryption keys and other cryptographic material. The Secure Enclave only supports P256 elliptic curve keys and offers around 4MB of storage, which is plenty for key storage.</p>
<p>Recently we had a task to utilise the secure enclave to enhance security for the data an application stored and provide an extra layer to protect that sensitive information. Interested in knowing how we did it? Then let's go...</p>
<h1>How we can utilise it</h1>
<p>As previously mentioned, the Secure Enclave is not somewhere we can simply store any type of data we have.</p>
<p>In fact, for our utilisation, we are going to continue to use the keychain for storage, but instead of simply storing the raw data in the keychain, we are going to build a wrapper that can make use of the Secure Enclave to encrypt data before it's stored and decrypt at the point it's fetched. This provides us with the security that even if someone somehow accesses that data in the software, you can rest assured that it is encrypted and protected at the hardware level. That's pretty neat. Not only that, with features provided by Apple's CryptoKit library, we can even put restraints on when this data can be decrypted, for example, when the device is locked or when your app is in the background.</p>
<p>We're going to step through the code seeing how we can store our data while encrypting and decrypting it with the Secure Enclave. If you would like to see the full <code>EnclaveWrapper</code> code, it is available at the bottom of this page.</p>
<h1>Initialising our <code>EnclaveWrapper</code></h1>
<p>Before we can start, make sure you've imported <code>CryptoKit</code>, <code>LocalAuthentication</code> &#x26; <code>KeychainAccess</code> from Apple. We will be making use of all of them in our <code>EnclaveWrapper</code>.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> Foundation
<span class="hljs-keyword">import</span> CryptoKit
<span class="hljs-keyword">import</span> LocalAuthentication
<span class="hljs-keyword">import</span> KeychainAccess
</code></pre>
<p>Our <code>EnclaveWrapper</code> requires a dependency on somewhere to store our EnclaveKey. In our case, we want to use our <code>KeychainWrapper</code> but you may have other places you want to store it or you may want this dependency decoupled for testing purposes. Because of this, the <code>EnclaveWrapper</code> has a property <code>keyAccess</code> that conforms to the <code>EnclaveWrapperKeyAccess</code> protocol.</p>
<p>The <code>EnclaveWrapperKeyAccess</code> protocol allows us to read and write our <code>EnclaveWrapperKey</code> when we wrap and unwrap our data. Below we have a class that uses our <code>KeychainWrapper</code> to align with the <code>EnclaveWrapperKeyAccess</code> protocol for use with the <code>EnclaveWrapper</code>.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">EnclaveWrapper</span> {
    <span class="hljs-keyword">let</span> keyAccess: <span class="hljs-type">EnclaveWrapperKeyAccess</span>
    
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">init</span>(<span class="hljs-params">keyAccess</span>: <span class="hljs-type">EnclaveWrapperKeyAccess</span>) {
        <span class="hljs-keyword">self</span>.keyAccess <span class="hljs-operator">=</span> keyAccess
    }
    
    <span class="hljs-operator">...</span>
}    
</code></pre>
<pre><code class="hljs language-swift"><span class="hljs-keyword">public</span> <span class="hljs-keyword">protocol</span> <span class="hljs-title class_">EnclaveWrapperKeyAccess</span> {
    <span class="hljs-keyword">var</span> enclaveKey: <span class="hljs-type">EnclaveWrapperKey</span>? { <span class="hljs-keyword">get</span> }
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">updateKey</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">key</span>: <span class="hljs-type">EnclaveWrapperKey</span>) <span class="hljs-keyword">throws</span>
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">EnclaveKeychainAccess</span>: <span class="hljs-title class_">EnclaveWrapperKeyAccess</span> {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> privateKeyTag <span class="hljs-operator">=</span> <span class="hljs-string">"com.demoApp.secureEnclaveDemo.secureEnclavePrivateKey"</span>
    
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">init</span>() {}
    
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> enclaveKey: <span class="hljs-type">EnclaveWrapperKey</span>? {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">try?</span> <span class="hljs-type">Keychain</span>.shared.get(key: privateKeyTag, ofType: <span class="hljs-type">EnclaveWrapperKey</span>.<span class="hljs-keyword">self</span>)
    }
    
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">updateKey</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">key</span>: <span class="hljs-type">EnclaveWrapperKey</span>) <span class="hljs-keyword">throws</span> {
        <span class="hljs-keyword">try</span> <span class="hljs-type">Keychain</span>.shared.set(value: key, key: privateKeyTag)
    }
}
</code></pre>
<h1>Sealing our data</h1>
<p>The seal data function we're going to look into...</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">//seals data in AES.GCM seal and returns combined Data</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">sealData</span>&#x3C;<span class="hljs-type">T</span>: <span class="hljs-type">Encodable</span>>(<span class="hljs-params">data</span>: <span class="hljs-type">T</span>) <span class="hljs-keyword">throws</span> -> <span class="hljs-type">Data</span> {
    <span class="hljs-keyword">guard</span> <span class="hljs-type">SecureEnclave</span>.isAvailable <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">throw</span> <span class="hljs-type">EnclaveWrapperError</span>.secureEnclaveUnavailable
    }
    
    <span class="hljs-comment">//fetch cached key if we have one</span>
    <span class="hljs-keyword">var</span> enclaveKey: <span class="hljs-type">EnclaveWrapperKey</span>? <span class="hljs-operator">=</span> keyAccess.enclaveKey
    
    <span class="hljs-comment">//if we dont have a cached key, generate a new one</span>
    <span class="hljs-keyword">if</span> enclaveKey <span class="hljs-operator">==</span> <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">let</span> newPrivateKey <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">SecureEnclave</span>.<span class="hljs-type">P256</span>.<span class="hljs-type">KeyAgreement</span>.<span class="hljs-type">PrivateKey</span>()
        <span class="hljs-keyword">let</span> newKey <span class="hljs-operator">=</span> <span class="hljs-type">EnclaveWrapperKey</span>(privateKeyDataRepresentation: newPrivateKey.dataRepresentation,
                                       salt: generateRandomSalt())
        <span class="hljs-comment">//cache it for future use</span>
        <span class="hljs-keyword">try</span> keyAccess.updateKey(newKey)
        enclaveKey <span class="hljs-operator">=</span> newKey
    }
    
    <span class="hljs-comment">//check we have keys at this point, either from cache or generated</span>
    <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> validKey <span class="hljs-operator">=</span> enclaveKey  <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">throw</span> <span class="hljs-type">EnclaveWrapperError</span>.keyGenerationFailed
    }
    
    <span class="hljs-keyword">let</span> symmetricKey <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> validKey.symmetricKey()
    <span class="hljs-keyword">let</span> encodedData <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">JSONEncoder</span>().encode(data)
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> <span class="hljs-type">AES</span>.<span class="hljs-type">GCM</span>.seal(encodedData, using: symmetricKey).combined<span class="hljs-operator">!</span>
}
</code></pre>
<h2>Hardware checks</h2>
<p>First off we need to check the secure enclave is available on the device we're using. This will be any device that uses the A7 chip (iPhone 5S or later). If we can't use the enclave, we simply throw an error.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">guard</span> <span class="hljs-type">SecureEnclave</span>.isAvailable <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">throw</span> <span class="hljs-type">EnclaveWrapperError</span>.secureEnclaveUnavailable
}
</code></pre>
<h2>Key Handling</h2>
<p>Secondly, we will check if we have an enclave private key already cached. We do this by using our <code>keyAccess</code> property we set up at initialisation.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">//fetch cached key if we have one</span>
<span class="hljs-keyword">var</span> enclaveKey: <span class="hljs-type">EnclaveWrapperKey</span>? <span class="hljs-operator">=</span> keyAccess.enclaveKey
</code></pre>
<h3>Generating a key</h3>
<p>In the case that this is a users first run through, they wont have a stored key so we will need to create one for them.</p>
<p>For this we have the <code>EnclaveWrapperKey</code> struct. This combines our private key along with salt data that will later be used to generate a symmetric key for sealing and unlocking our data.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">public</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">EnclaveWrapperKey</span>: <span class="hljs-title class_">Codable</span> {
    <span class="hljs-keyword">let</span> privateKeyDataRepresentation: <span class="hljs-type">Data</span>
    <span class="hljs-keyword">let</span> salt: <span class="hljs-type">Data</span>
}
</code></pre>
<p>Below you can see how we create a new <code>EnclaveWrapperKey</code>.</p>
<ul>
<li>We use CryptoKit to generate a new public key using the Secure Enclave.</li>
</ul>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> newPrivateKey <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">SecureEnclave</span>.<span class="hljs-type">P256</span>.<span class="hljs-type">KeyAgreement</span>.<span class="hljs-type">PrivateKey</span>()
</code></pre>
<ul>
<li>We then generate random salt data that is used for the derivation of the symmetric key.</li>
</ul>
<pre><code class="hljs language-swift"><span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">generateRandomSalt</span>() -> <span class="hljs-type">Data</span> {
    <span class="hljs-keyword">var</span> data <span class="hljs-operator">=</span> <span class="hljs-type">Data</span>(count: <span class="hljs-number">32</span>)
    <span class="hljs-keyword">_</span> <span class="hljs-operator">=</span> data.withUnsafeMutableBytes { buffer <span class="hljs-keyword">in</span>
        <span class="hljs-type">SecRandomCopyBytes</span>(kSecRandomDefault, <span class="hljs-number">32</span>, buffer.baseAddress<span class="hljs-operator">!</span>)
    }
    <span class="hljs-keyword">return</span> data
}
</code></pre>
<ul>
<li>We then combine the two to make our new <code>EnclaveWrapperKey</code>. We then need to cache a representation of this key using <code>keyAccess</code> once more. It's important to remember that the data representation stored in the Keychain will not simple be enough to unlock our data. We will need to interact with the Secure Enclave using this data to get our PrivateKey.</li>
</ul>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> newKey <span class="hljs-operator">=</span> <span class="hljs-type">EnclaveWrapperKey</span>(privateKeyDataRepresentation: newPrivateKey.dataRepresentation,
                                       salt: generateRandomSalt())
<span class="hljs-comment">//cache it for future use</span>
<span class="hljs-keyword">try</span> keyAccess.updateKey(newKey)
</code></pre>
<h3>Utilising a cached key</h3>
<p>Presuming this is our second time storing data and we have already generated a key previously, meaning one is available in the Keychain. We can use the cached key found in our <code>keyAccess</code> dependency.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">var</span> enclaveKey: <span class="hljs-type">EnclaveWrapperKey</span>? <span class="hljs-operator">=</span> keyAccess.enclaveKey
</code></pre>
<p>Our <code>EnclaveWrapperKey</code> has two helpful functions. The first being a private function, that allows us to retrieve our SecureEnclave key.</p>
<p><code>func privateKey() throws -> SecureEnclave.P256.KeyAgreement.PrivateKey</code></p>
<p>This function can use the <code>EnclaveWrapperKey.privateKeyDataRepresentation</code> to interact with the SecureEnclave using CryptoKit and return our <code>SecureEnclave.P256.KeyAgreement.PrivateKey</code>.</p>
<p>Another thing to note is, you need to provide an authentication context whenever accessing these private keys. Depending on the key and configurations, you may need to display context and reasoning as to why you need to authenticate to access this key using biometric or password checks.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">privateKey</span>() <span class="hljs-keyword">throws</span> -> <span class="hljs-type">SecureEnclave</span>.<span class="hljs-type">P256</span>.<span class="hljs-type">KeyAgreement</span>.<span class="hljs-type">PrivateKey</span> {
    <span class="hljs-keyword">let</span> context <span class="hljs-operator">=</span> <span class="hljs-type">LAContext</span>()
    context.localizedReason <span class="hljs-operator">=</span> <span class="hljs-string">"Authenticate to retrieve the private key"</span>
    <span class="hljs-keyword">let</span> privateKey <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">SecureEnclave</span>.<span class="hljs-type">P256</span>.<span class="hljs-type">KeyAgreement</span>.<span class="hljs-type">PrivateKey</span>(dataRepresentation: <span class="hljs-keyword">self</span>.privateKeyDataRepresentation, authenticationContext: context)
    <span class="hljs-keyword">return</span> privateKey
}
</code></pre>
<h3>Symmetric Keys</h3>
<p>The second <code>EnclaveWrapperKey</code> function is publicly accessible. This is because it generates a symmetric key by combining the <code>privateKey()</code> function key and the salt data stored within the <code>EnclaveWrapperKey</code>. It first takes a private key and generates a shared secret using its private and public key combined. With this shared secret, we can then derive a symmetric key using SHA256 and the salt. We store the salt and the key together in our <code>EnclaveWrapperKey</code> as one without the other would not work. Both are required to derive our symmetric key. This symmetric key is what will be required for sealing and opening our data.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">symmetricKey</span>() <span class="hljs-keyword">throws</span> -> <span class="hljs-type">SymmetricKey</span> {
    <span class="hljs-comment">//generate symmetric key from shared secret</span>
    <span class="hljs-keyword">let</span> privateKey <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> privateKey()
    <span class="hljs-keyword">let</span> sharedSecret <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> privateKey.sharedSecretFromKeyAgreement(with: privateKey.publicKey)
    
    <span class="hljs-comment">// Perform HKDF to derive the symmetric key from the shared secret</span>
    <span class="hljs-keyword">return</span> sharedSecret.hkdfDerivedSymmetricKey(using: <span class="hljs-type">SHA256</span>.<span class="hljs-keyword">self</span>,
                                                salt: <span class="hljs-keyword">self</span>.salt,
                                                sharedInfo: <span class="hljs-type">Data</span>(),
                                                outputByteCount: <span class="hljs-number">32</span>)
}
</code></pre>
<p>For our case, we can use the public key that is accessible via the private key as we are not sharing this data with anyone else. It is purely for security enhancements within our device. Often when sharing data with others, users will share their public keys while keeping their private keys, exactly that, private! Once this key exchange has taken place, both parties will be able to access the data as long as the shared secret is a combination of both their keys. This would be a more common use case and why we need to interact with the Secure Enclave in this way. You can read more about the <a href="https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange">Diffie–Hellman key exchange here</a>.</p>
<h2>The concealing</h2>
<p>Now we have our symmetric key, we can encode and then seal our data up by calling <code>AES.GCM.seal(encodedData, using: symmetricKey).combined!</code></p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> encodedData <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">JSONEncoder</span>().encode(data)
<span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> <span class="hljs-type">AES</span>.<span class="hljs-type">GCM</span>.seal(encodedData, using: symmetricKey).combined<span class="hljs-operator">!</span>
</code></pre>
<p>Now I have my seal data function, I can simply call it in my app on any <code>Encodable</code> object and store my encrypted data like so...</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> wrappedData <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">EnclaveWrapper</span>.sealData(data: user)
storage.set(wrappedData, key: <span class="hljs-type">LocalConstants</span>.userStorageKey)
</code></pre>
<h1>Opening our sealed data</h1>
<pre><code class="hljs language-swift"><span class="hljs-keyword">public</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">openSealedData</span>&#x3C;<span class="hljs-type">T</span>: <span class="hljs-type">Decodable</span>>(<span class="hljs-params">type</span>: <span class="hljs-type">T</span>.<span class="hljs-keyword">Type</span>, <span class="hljs-params">data</span>: <span class="hljs-type">Data</span>) <span class="hljs-keyword">throws</span> -> <span class="hljs-type">T</span> {
    <span class="hljs-keyword">guard</span> <span class="hljs-type">SecureEnclave</span>.isAvailable <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">throw</span> <span class="hljs-type">EnclaveWrapperError</span>.secureEnclaveUnavailable
    }
    
    <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> enclaveKey <span class="hljs-operator">=</span> keyAccess.enclaveKey <span class="hljs-keyword">else</span> {
        <span class="hljs-comment">//if we dont have a cached key, opening sealed data will not work</span>
        <span class="hljs-keyword">throw</span> <span class="hljs-type">EnclaveWrapperError</span>.keysNotFound
    }
    
    <span class="hljs-keyword">let</span> symmetricKey <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> enclaveKey.symmetricKey()
    
    <span class="hljs-keyword">let</span> sealedBox <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">AES</span>.<span class="hljs-type">GCM</span>.<span class="hljs-type">SealedBox</span>(combined: data)
    <span class="hljs-keyword">let</span> data <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">AES</span>.<span class="hljs-type">GCM</span>.open(sealedBox, using: symmetricKey)
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> <span class="hljs-type">JSONDecoder</span>().decode(<span class="hljs-type">T</span>.<span class="hljs-keyword">self</span>, from: data)
}
</code></pre>
<p>Now we've got our sealed data, there will come a time, where we need to unlock it and access it once more. Lets look into how we can do that.</p>
<p>Just like before, we first need to check the enclave is available and fetch our cached <code>enclaveKey</code> using <code>keyAccess.enclaveKey</code>. Unlike before there is no need to generate new keys if it can't be found. There is no way we'll be able to unlock our data if we don't have the keys and salt that were used to seal it. In this case, we will just throw an error.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> enclaveKey <span class="hljs-operator">=</span> keyAccess.enclaveKey <span class="hljs-keyword">else</span> {
    <span class="hljs-comment">//if we dont have a cached key, opening sealed data will not work</span>
    <span class="hljs-keyword">throw</span> <span class="hljs-type">EnclaveWrapperError</span>.keysNotFound
}
</code></pre>
<p>We then access our symmetric key using our same <code>symmetricKey()</code> function.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> symmetricKey <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> enclaveKey.symmetricKey()
</code></pre>
<p>Finally, we can simply create a sealed box using the data passed in like so...<br>
<code>try AES.GCM.SealedBox(combined: data)</code></p>
<p>We can then open the box with our symmetric key and decode our data. It's important to remember that in order to use our Enclave Wrapper, all data types will need to conform to <code>Codable</code>.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> sealedBox <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">AES</span>.<span class="hljs-type">GCM</span>.<span class="hljs-type">SealedBox</span>(combined: data)
<span class="hljs-keyword">let</span> data <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">AES</span>.<span class="hljs-type">GCM</span>.open(sealedBox, using: symmetricKey)
<span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> <span class="hljs-type">JSONDecoder</span>().decode(<span class="hljs-type">T</span>.<span class="hljs-keyword">self</span>, from: data)
</code></pre>
<p>With this function complete, I can use it in my app to fetch back my User object (that conforms to codable) that I sealed previously...</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">getUser</span>() -> <span class="hljs-type">User</span>? {
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> wrappedData <span class="hljs-operator">=</span> storage.get(key: <span class="hljs-type">LocalConstants</span>.userStorageKey, ofType: <span class="hljs-type">Data</span>.<span class="hljs-keyword">self</span>) {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">try?</span> <span class="hljs-type">EnclaveWrapper</span>.openSealedData(type: <span class="hljs-type">User</span>.<span class="hljs-keyword">self</span>, data: wrappedData)
    }

    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}
</code></pre>
<h1>Conclusion</h1>
<p>Now we have an EnclaveWrapper and have decoupled our dependecies on Keychain, we can even write a test to assert our sealing and opening of our data works as expected. Below we use the <code>EnclaveWrapper</code> to encrypt our Codable data, and once encrypted it can't be decoded into its original form until it's opened with the <code>EnclaveWrapper</code>. We can then test the data matches.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> XCTest
<span class="hljs-keyword">@testable</span> <span class="hljs-keyword">import</span> EnclaveWrapper

<span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">EnclaveWrapperTests</span>: <span class="hljs-title class_">XCTestCase</span> {

    <span class="hljs-keyword">struct</span> <span class="hljs-title class_">User</span>: <span class="hljs-title class_">Codable</span> {
        <span class="hljs-keyword">let</span> firstName: <span class="hljs-type">String</span>
        <span class="hljs-keyword">let</span> lastName: <span class="hljs-type">String</span>
    }
    
    <span class="hljs-keyword">class</span> <span class="hljs-title class_">MockEnclaveKeychainAccess</span>: <span class="hljs-title class_">EnclaveWrapperKeyAccess</span> {
        <span class="hljs-keyword">var</span> enclaveKey: <span class="hljs-type">EnclaveWrapperKey</span>?
        
        <span class="hljs-keyword">func</span> <span class="hljs-title function_">updateKey</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">key</span>: <span class="hljs-type">EnclaveWrapperKey</span>) <span class="hljs-keyword">throws</span> {
            enclaveKey <span class="hljs-operator">=</span> key
        }
    }
    
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">testNewEncryption</span>() {
        <span class="hljs-comment">//mock object we will encrypt and test with</span>
        <span class="hljs-keyword">let</span> user <span class="hljs-operator">=</span> <span class="hljs-type">User</span>(firstName: <span class="hljs-string">"Joe"</span>, lastName: <span class="hljs-string">"Bloggs"</span>)
        
        <span class="hljs-keyword">let</span> enclaveWrapper <span class="hljs-operator">=</span> <span class="hljs-type">EnclaveWrapper</span>(keyAccess: <span class="hljs-type">MockEnclaveKeychainAccess</span>())
        
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> encryptedUser <span class="hljs-operator">=</span> <span class="hljs-keyword">try?</span> enclaveWrapper.sealData(data: user) <span class="hljs-keyword">else</span> {
            <span class="hljs-type">XCTFail</span>(<span class="hljs-string">"Failed to encrypt object"</span>)
            <span class="hljs-keyword">return</span>
        }
        
        <span class="hljs-comment">//prove we can't use encypted data</span>
        <span class="hljs-keyword">let</span> encryptedUserDecoded <span class="hljs-operator">=</span> <span class="hljs-keyword">try?</span> <span class="hljs-type">JSONDecoder</span>().decode(<span class="hljs-type">User</span>.<span class="hljs-keyword">self</span>, from: encryptedUser)
        <span class="hljs-type">XCTAssertNil</span>(encryptedUserDecoded)
        
        <span class="hljs-comment">//now decrypt user using open sealed data</span>
        <span class="hljs-keyword">let</span> decryptedUser <span class="hljs-operator">=</span> <span class="hljs-keyword">try?</span> enclaveWrapper.openSealedData(type: <span class="hljs-type">User</span>.<span class="hljs-keyword">self</span>, data: encryptedUser)
        
        <span class="hljs-comment">//assert data is equal</span>
        <span class="hljs-type">XCTAssertEqual</span>(user.firstName, decryptedUser<span class="hljs-operator">?</span>.firstName)
        <span class="hljs-type">XCTAssertEqual</span>(user.lastName, decryptedUser<span class="hljs-operator">?</span>.lastName)
    }
}
</code></pre>
<p>That about wraps it up! (Excuse the pun).</p>
<p>With just those few functions, I can now safely cache all my data with the added dependency of the Secure Enclave hardware capabilities. If the data in the keychain was ever compromised, we can now safely know, it will be encrypted with the Secure Enclave. If you want to read more about the secure enclave, you can find some links to more information below. You can also find the entire EnclaveWrapper code along with a KeychainWrapper that I used with it.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> Foundation
<span class="hljs-keyword">import</span> CryptoKit
<span class="hljs-keyword">import</span> LocalAuthentication
<span class="hljs-keyword">import</span> KeychainAccess

<span class="hljs-comment">//MARK: - EnclaveWrapperKey</span>

<span class="hljs-keyword">public</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">EnclaveWrapperKey</span>: <span class="hljs-title class_">Codable</span> {
    <span class="hljs-keyword">let</span> privateKeyDataRepresentation: <span class="hljs-type">Data</span>
    <span class="hljs-keyword">let</span> salt: <span class="hljs-type">Data</span>
}

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">EnclaveWrapperKey</span> {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">privateKey</span>() <span class="hljs-keyword">throws</span> -> <span class="hljs-type">SecureEnclave</span>.<span class="hljs-type">P256</span>.<span class="hljs-type">KeyAgreement</span>.<span class="hljs-type">PrivateKey</span> {
        <span class="hljs-keyword">let</span> context <span class="hljs-operator">=</span> <span class="hljs-type">LAContext</span>()
        context.localizedReason <span class="hljs-operator">=</span> <span class="hljs-string">"Authenticate to retrieve the private key"</span>
        <span class="hljs-keyword">let</span> privateKey <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">SecureEnclave</span>.<span class="hljs-type">P256</span>.<span class="hljs-type">KeyAgreement</span>.<span class="hljs-type">PrivateKey</span>(dataRepresentation: <span class="hljs-keyword">self</span>.privateKeyDataRepresentation, authenticationContext: context)
        <span class="hljs-keyword">return</span> privateKey
    }
    
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">symmetricKey</span>() <span class="hljs-keyword">throws</span> -> <span class="hljs-type">SymmetricKey</span> {
        <span class="hljs-comment">//generate symmetric key from shared secret</span>
        <span class="hljs-keyword">let</span> privateKey <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> privateKey()
        <span class="hljs-keyword">let</span> sharedSecret <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> privateKey.sharedSecretFromKeyAgreement(with: privateKey.publicKey)
        
        <span class="hljs-comment">// Perform HKDF to derive the symmetric key from the shared secret</span>
        <span class="hljs-keyword">return</span> sharedSecret.hkdfDerivedSymmetricKey(using: <span class="hljs-type">SHA256</span>.<span class="hljs-keyword">self</span>,
                                                    salt: <span class="hljs-keyword">self</span>.salt,
                                                    sharedInfo: <span class="hljs-type">Data</span>(),
                                                    outputByteCount: <span class="hljs-number">32</span>)
    }
}

<span class="hljs-comment">//MARK: - EnclaveWrapper</span>

<span class="hljs-keyword">public</span> <span class="hljs-keyword">enum</span> <span class="hljs-title class_">EnclaveWrapperError</span>: <span class="hljs-title class_">Error</span> {
    <span class="hljs-keyword">case</span> secureEnclaveUnavailable
    <span class="hljs-keyword">case</span> saltNotFound
    <span class="hljs-keyword">case</span> keyGenerationFailed
    <span class="hljs-keyword">case</span> keysNotFound
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">EnclaveWrapper</span> {
    <span class="hljs-keyword">let</span> keyAccess: <span class="hljs-type">EnclaveWrapperKeyAccess</span>
    
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">init</span>(<span class="hljs-params">keyAccess</span>: <span class="hljs-type">EnclaveWrapperKeyAccess</span>) {
        <span class="hljs-keyword">self</span>.keyAccess <span class="hljs-operator">=</span> keyAccess
    }
    
    <span class="hljs-comment">//MARK: - Public functions</span>
    
    <span class="hljs-comment">//seals data in AES.GCM seal and returns combined Data</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">sealData</span>&#x3C;<span class="hljs-type">T</span>: <span class="hljs-type">Encodable</span>>(<span class="hljs-params">data</span>: <span class="hljs-type">T</span>) <span class="hljs-keyword">throws</span> -> <span class="hljs-type">Data</span> {
        <span class="hljs-keyword">guard</span> <span class="hljs-type">SecureEnclave</span>.isAvailable <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">throw</span> <span class="hljs-type">EnclaveWrapperError</span>.secureEnclaveUnavailable
        }
        
        <span class="hljs-comment">//fetch cached key if we have one</span>
        <span class="hljs-keyword">var</span> enclaveKey: <span class="hljs-type">EnclaveWrapperKey</span>? <span class="hljs-operator">=</span> keyAccess.enclaveKey
        
        <span class="hljs-comment">//if we don't have a cached key, generate a new one</span>
        <span class="hljs-keyword">if</span> enclaveKey <span class="hljs-operator">==</span> <span class="hljs-literal">nil</span> {
            <span class="hljs-keyword">let</span> newPrivateKey <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">SecureEnclave</span>.<span class="hljs-type">P256</span>.<span class="hljs-type">KeyAgreement</span>.<span class="hljs-type">PrivateKey</span>()
            <span class="hljs-keyword">let</span> newKey <span class="hljs-operator">=</span> <span class="hljs-type">EnclaveWrapperKey</span>(privateKeyDataRepresentation: newPrivateKey.dataRepresentation,
                                           salt: generateRandomSalt())
            <span class="hljs-comment">//cache it for future use</span>
            <span class="hljs-keyword">try</span> keyAccess.updateKey(newKey)
            enclaveKey <span class="hljs-operator">=</span> newKey
        }
        
        <span class="hljs-comment">//check we have keys at this point, either from cache or generated</span>
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> validKey <span class="hljs-operator">=</span> enclaveKey  <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">throw</span> <span class="hljs-type">EnclaveWrapperError</span>.keyGenerationFailed
        }
        
        <span class="hljs-keyword">let</span> symmetricKey <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> validKey.symmetricKey()
        <span class="hljs-keyword">let</span> encodedData <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">JSONEncoder</span>().encode(data)
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> <span class="hljs-type">AES</span>.<span class="hljs-type">GCM</span>.seal(encodedData, using: symmetricKey).combined<span class="hljs-operator">!</span>
    }
    
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">openSealedData</span>&#x3C;<span class="hljs-type">T</span>: <span class="hljs-type">Decodable</span>>(<span class="hljs-params">type</span>: <span class="hljs-type">T</span>.<span class="hljs-keyword">Type</span>, <span class="hljs-params">data</span>: <span class="hljs-type">Data</span>) <span class="hljs-keyword">throws</span> -> <span class="hljs-type">T</span> {
        <span class="hljs-keyword">guard</span> <span class="hljs-type">SecureEnclave</span>.isAvailable <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">throw</span> <span class="hljs-type">EnclaveWrapperError</span>.secureEnclaveUnavailable
        }
        
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> enclaveKey <span class="hljs-operator">=</span> keyAccess.enclaveKey <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">//if we dont have a cached key, opening sealed data will not work</span>
            <span class="hljs-keyword">throw</span> <span class="hljs-type">EnclaveWrapperError</span>.keysNotFound
        }
        
        <span class="hljs-keyword">let</span> symmetricKey <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> enclaveKey.symmetricKey()
        
        <span class="hljs-keyword">let</span> sealedBox <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">AES</span>.<span class="hljs-type">GCM</span>.<span class="hljs-type">SealedBox</span>(combined: data)
        <span class="hljs-keyword">let</span> data <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">AES</span>.<span class="hljs-type">GCM</span>.open(sealedBox, using: symmetricKey)
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> <span class="hljs-type">JSONDecoder</span>().decode(<span class="hljs-type">T</span>.<span class="hljs-keyword">self</span>, from: data)
    }
    
    <span class="hljs-comment">//MARK: - Private functions</span>

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">generateRandomSalt</span>() -> <span class="hljs-type">Data</span> {
        <span class="hljs-keyword">var</span> data <span class="hljs-operator">=</span> <span class="hljs-type">Data</span>(count: <span class="hljs-number">32</span>)
        <span class="hljs-keyword">_</span> <span class="hljs-operator">=</span> data.withUnsafeMutableBytes { buffer <span class="hljs-keyword">in</span>
            <span class="hljs-type">SecRandomCopyBytes</span>(kSecRandomDefault, <span class="hljs-number">32</span>, buffer.baseAddress<span class="hljs-operator">!</span>)
        }
        <span class="hljs-keyword">return</span> data
    }
}

<span class="hljs-comment">// MARK: - EnclaveKeychainAccess</span>

<span class="hljs-keyword">public</span> <span class="hljs-keyword">protocol</span> <span class="hljs-title class_">EnclaveWrapperKeyAccess</span> {
    <span class="hljs-keyword">var</span> enclaveKey: <span class="hljs-type">EnclaveWrapperKey</span>? { <span class="hljs-keyword">get</span> }
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">updateKey</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">key</span>: <span class="hljs-type">EnclaveWrapperKey</span>) <span class="hljs-keyword">throws</span>
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">EnclaveKeychainAccess</span>: <span class="hljs-title class_">EnclaveWrapperKeyAccess</span> {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> privateKeyTag <span class="hljs-operator">=</span> <span class="hljs-string">"com.demoApp.secureEnclaveDemo.secureEnclavePrivateKey"</span>
    
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">init</span>() {}
    
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> enclaveKey: <span class="hljs-type">EnclaveWrapperKey</span>? {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">try?</span> <span class="hljs-type">Keychain</span>.shared.get(key: privateKeyTag, ofType: <span class="hljs-type">EnclaveWrapperKey</span>.<span class="hljs-keyword">self</span>)
    }
    
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">updateKey</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">key</span>: <span class="hljs-type">EnclaveWrapperKey</span>) <span class="hljs-keyword">throws</span> {
        <span class="hljs-keyword">try</span> <span class="hljs-type">Keychain</span>.shared.set(value: key, key: privateKeyTag)
    }
}

<span class="hljs-comment">// MARK: - KeychainWrapper</span>

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Keychain</span> {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> keychain: <span class="hljs-type">KeychainAccess</span>.<span class="hljs-type">Keychain</span>
    
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">init</span>(<span class="hljs-params">service</span>: <span class="hljs-type">String</span>) {
        <span class="hljs-keyword">self</span>.keychain <span class="hljs-operator">=</span> .<span class="hljs-keyword">init</span>(service: service)
    }
    
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">get</span>&#x3C;<span class="hljs-type">T</span>: <span class="hljs-type">Codable</span>>(<span class="hljs-params">key</span>: <span class="hljs-type">String</span>, <span class="hljs-params">ofType</span> <span class="hljs-params">type</span>: <span class="hljs-type">T</span>.<span class="hljs-keyword">Type</span>) <span class="hljs-keyword">throws</span> -> <span class="hljs-type">T</span>? {
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> data <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> keychain.getData(key) <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
        }
        
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> <span class="hljs-type">JSONDecoder</span>().decode(<span class="hljs-type">T</span>.<span class="hljs-keyword">self</span>, from: data)
    }
    
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">set</span>&#x3C;<span class="hljs-type">T</span>: <span class="hljs-type">Codable</span>>(<span class="hljs-params">value</span>: <span class="hljs-type">T</span>?, <span class="hljs-params">key</span>: <span class="hljs-type">String</span>) <span class="hljs-keyword">throws</span> {
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> value <span class="hljs-operator">=</span> value <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">try</span> keychain.remove(key)
            <span class="hljs-keyword">return</span>
        }

        <span class="hljs-keyword">let</span> data <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">JSONEncoder</span>().encode(value)
        
        <span class="hljs-keyword">try</span> keychain.set(data, key: key)
    }
    
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">remove</span>(<span class="hljs-params">key</span>: <span class="hljs-type">String</span>) <span class="hljs-keyword">throws</span> {
        <span class="hljs-keyword">try</span> keychain.remove(key)
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">extension</span> <span class="hljs-title class_">Keychain</span> {
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> shared: <span class="hljs-type">Keychain</span> <span class="hljs-operator">=</span> <span class="hljs-type">Keychain</span>(service: <span class="hljs-string">"com.demoApp.secureEnclaveDemo"</span>)
}

</code></pre>
<h4></h4>
<h2>Related Articles:</h2>
<ul>
<li><a href="https://support.apple.com/en-gb/guide/security/sec59b0b31ff/web">Apple: Secure Enclave</a></li>
<li><a href="https://developer.apple.com/documentation/cryptokit/sharedsecret">Apple: Shared Secret</a></li>
<li><a href="https://www.andyibanez.com/posts/cryptokit-secure-enclave/">Andy Ibanez: CryptoKit &#x26; Secure Enclave</a></li>
</ul>
<p><em>Article Photo by <a href="https://tbtech.co/wp-content/uploads/2021/04/Group-109-1200x548.png">Getty Images</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Streamlining Infrastructure as Code Testing: Harnessing Terratest, Terraform, and AWS Go SDKs for Automation]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/10/27/Streamlining-Infrastructure-as-Code-Testing-Harnessing-Terratest-Terraform-and-AWS-Go-SDKs-for-Automation</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/10/27/Streamlining-Infrastructure-as-Code-Testing-Harnessing-Terratest-Terraform-and-AWS-Go-SDKs-for-Automation</guid>
            <pubDate>Fri, 27 Oct 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>When cloud infrastructure is created using IaC with Terraform, how can you know it’s working as intended?</p>
<figure>
<img src="/assets/img/articles/2023-09-25-Streamlining-Infrastructure-as-Code-Testing-Harnessing-Terratest-Terraform-and-AWS-Go-SDKs-for-Automation/meme.webp">
<figcaption></figcaption>
</figure>
<p>I can recall many times when I deployed services with Terraform only to find out it didn’t work as intended. I would then sometimes spend hours pecking through the cloud UI looking for a missing configuration or what could possibly cause the issue. Sometimes it would even be the same missing configuration for a previous Terraform deployment. You know, like those elusive Security Group's egress/ingress rules. Only if I had an automated way to test for configurations that should be set or services that should be active. Let me introduce unit and integration tests using <a href="https://terratest.gruntwork.io/">Terratest from Gruntwork</a>.</p>
<h2>Unit Tests</h2>
<p>Unit tests are automated tests to ensure that a section of an application (known as the "unit") meets its design and functions as intended. With Terraform this can really only be done by deploying to a real environment. When deploying infrastructure, it is very rarely just a single unit, however more an integration test of a few to many services. For that reason, there is no true unit testing with IaC.</p>
<p>So how can this be done? How can we test our infrastructure in the smallest unit possible? Let’s take this simple infrastructure example.</p>
<figure>
<img src="/assets/img/articles/2023-09-25-Streamlining-Infrastructure-as-Code-Testing-Harnessing-Terratest-Terraform-and-AWS-Go-SDKs-for-Automation/infra.webp">
<figcaption></figcaption>
</figure>
<p>Testing this entire infrastructure would take too long and would have a high risk of failure. This is why relying on <a href="https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html">end to end testing for infrastructure is not advised</a>, and should be broken up into smaller unit and integration tests. In this example, we can do this by making sure each service is broken up into modules.</p>
<figure>
<img src="/assets/img/articles/2023-09-25-Streamlining-Infrastructure-as-Code-Testing-Harnessing-Terratest-Terraform-and-AWS-Go-SDKs-for-Automation/infra-modules.webp">
<figcaption></figcaption>
</figure>
<p>By testing each module we can deploy our infrastructure, validate it works, and then destroy it. Here is a snippet of code on performing this test on an AWS VPC with Terratest.</p>
<pre><code class="hljs language-css">  defer terraform<span class="hljs-selector-class">.Destroy</span>(t, terraformOptions)
  terraform<span class="hljs-selector-class">.InitAndApply</span>(t, terraformOptions)
  outputs, err := <span class="hljs-built_in">TerraformOutputAll</span>(t, terraformOptions)
  <span class="hljs-built_in">CheckIfError</span>(err)
  <span class="hljs-built_in">validateVPC</span>(t, outputs)
</code></pre>
<p>Let's walk through each line. Most of these lines have a <code>terraformOptions</code> these are options that are passed. These include the Terraform directory, and environment variables, or any Terraform variables to pass. They also may have a <code>t</code>, which is the <a href="https://pkg.go.dev/testing">testing</a> package in Go.</p>
<ol>
<li>In Golang, the defer keyword is used to delay the execution of a function or a statement until the nearby functions return. Meaning that the Terraform will destroy the deployed infrastructure once the deployment and validation is completed, successfully, or not.</li>
<li>The Terraform <code>init</code> and <code>apply</code> commands are run, which will deploy the infrastructure.</li>
<li>Outputs are injected into a variable to be used to validate our tests. Some examples are VPC ID, public subnets, and private subnets.</li>
<li>A wrapper to check for any errors.</li>
<li>Run Go tests to validate that the VPC is working as intended.</li>
</ol>
<p>Here is an example of two tests. One that will verify that the public subnets are indeed public, and the other verifying that the privates are in fact private. Don't focus too much on the code, what I want to point out is that we can take the Terraform outputs, and assert that they are public, or private by using the <code>assert.True</code> or <code>assert.False</code>. Meaning if a private subnet that was created was public, it would fail the test.</p>
<pre><code class="hljs language-go">  <span class="hljs-comment">// Verify if the network that is supposed to be public is really public</span>
  <span class="hljs-keyword">for</span> _, v := <span class="hljs-keyword">range</span> outputs.AWSVPCPublicSubnets {
    assert.True(
      t,
      aws.IsPublicSubnet(
        t,
        <span class="hljs-type">string</span>(v),
        awsRegion,
      ),
    )
  }

  <span class="hljs-comment">// Verify if the network that is supposed to be private is really private</span>
  <span class="hljs-keyword">for</span> _, v := <span class="hljs-keyword">range</span> outputs.AWSVPCPrivateSubnets {
    assert.False(
      t,
      aws.IsPublicSubnet(
        t,
        <span class="hljs-type">string</span>(v),
        awsRegion,
      ),
    )
  }
</code></pre>
<p>When I run the tests on the VPC and they all pass, I would get the results like so.</p>
<pre><code class="hljs language-diff"><span class="hljs-comment">=== RUN   TestVPC/Subnets</span>
<span class="hljs-comment">--- PASS: TestVPC (7.11s)</span>
    --- PASS: TestVPC/Subnets (0.93s)
PASS
ok      example 7.474s
</code></pre>
<p>The above example is testing the subnets within a VPC. Which, I would call an integration test. since our VPC module includes an option to create subnets, the unit in question is the module itself. Before we go into what integration tests would look like, let's go over the stages in Terratest.</p>
<h2>Terratest Stages</h2>
<p>Terratest has a helper function <code>RunTestStage</code>. This help function is used as a variable as <code>stage</code>. The quoted text is the name of the stage, which we can now skip by passing an environment variable. If we wanted to deploy this VPC, however, didn't want to destroy it after the run is completed, we can export the env var <code>SKIP_destroy_vpc</code>. This is important to include in the integration tests to avoid destroying infrastructure that was created in a separate module and needed for other services.</p>
<pre><code class="hljs language-scss">	stage := test_structure.RunTestStage

	defer stage(t, <span class="hljs-string">"destroy_vpc"</span>, func() {
		<span class="hljs-built_in">DestroyTerraform</span>(t, terraformOptions)
	})

	<span class="hljs-built_in">stage</span>(t, "apply_vpc", func() {
		outputs = <span class="hljs-built_in">ApplyTerraform</span>(t, terraformOptions)
	})

	<span class="hljs-built_in">stage</span>(t, "validate_vpc", func() {
		<span class="hljs-built_in">validateVPC</span>(t, outputs)
	})
</code></pre>
<p>The output now shows that the <code>destroy_vpc</code> was skipped:</p>
<pre><code class="hljs language-csharp">The <span class="hljs-string">'SKIP_apply_vpc'</span> environment variable <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">set</span>, so executing stage <span class="hljs-string">'apply_vpc'</span>.
applying...
The <span class="hljs-string">'SKIP_validate_vpc'</span> environment variable <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">set</span>, so executing stage <span class="hljs-string">'validate_vpc'</span>.
=== RUN   TestVPC/Subnets
The <span class="hljs-string">'SKIP_destroy_vpc'</span> environment variable <span class="hljs-keyword">is</span> <span class="hljs-keyword">set</span>, so skipping stage <span class="hljs-string">'destroy_vpc'</span>.
--- PASS: TestVPC (<span class="hljs-number">7.59</span>s)
    --- PASS: TestVPC/Subnets (<span class="hljs-number">0.96</span>s)
PASS
ok      example <span class="hljs-number">7.925</span>s
</code></pre>
<h2>Integrations Tests</h2>
<p>Here is a real-world example of how integration tests would work. We want to deploy an ECS cluster, and we want to make sure that the container on the service is running, and that the ingress/egress security group rules for the ALB and ECS are set correctly. We already deployed other modules needed and left them deployed by using the <code>SKIP_destroy_foo</code> environment variables. Now running the tests will skip any stage name that matches the environment variables, in this example <code>SKIP_destroy_ecs</code>.</p>
<pre><code class="hljs language-scss">	stage := test_structure.RunTestStage

	var outputs TerraformOutputs

	defer stage(t, <span class="hljs-string">"destroy_ecs"</span>, func() {
		<span class="hljs-built_in">DestroyTerraform</span>(t, terraformOptions)
	})

	<span class="hljs-built_in">stage</span>(t, "apply_ecs", func() {
		outputs = <span class="hljs-built_in">ApplyTerraform</span>(t, terraformOptions)
	})

	<span class="hljs-built_in">stage</span>(t, "validate_ecs", func() {
		<span class="hljs-built_in">validateECS</span>(t, outputs)
	})

	<span class="hljs-built_in">stage</span>(t, "integration_ecs", func() {
		<span class="hljs-built_in">integrationECS</span>(t, outputs)
	})
</code></pre>
<p>Within the <code>integration_ecs</code> we will be testing:</p>
<ul>
<li>VPC and subnets</li>
<li>Security group ingress/egress rules</li>
<li>ECS service running</li>
</ul>
<pre><code class="hljs language-csharp">The <span class="hljs-string">'SKIP_apply_ecs'</span> environment variable <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">set</span>, so executing stage <span class="hljs-string">'apply_ecs'</span>.
apply...
The <span class="hljs-string">'SKIP_validate_ecs'</span> environment variable <span class="hljs-keyword">is</span> <span class="hljs-keyword">set</span>, so skipping stage <span class="hljs-string">'validate_ecs'</span>.
The <span class="hljs-string">'SKIP_integration_ecs'</span> environment variable <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">set</span>, so executing stage <span class="hljs-string">'integration_ecs'</span>.
=== RUN   TestECS/ECS_Cluster
=== RUN   TestECS/VPC
=== RUN   TestECS/Security_Groups
The <span class="hljs-string">'SKIP_destroy_ecs'</span> environment variable <span class="hljs-keyword">is</span> <span class="hljs-keyword">set</span>, so skipping stage <span class="hljs-string">'destroy_ecs'</span>.
--- PASS: TestECS (<span class="hljs-number">12.30</span>s)
    --- PASS: TestECS/ECS_Cluster (<span class="hljs-number">0.28</span>s)
    --- PASS: TestECS/VPC (<span class="hljs-number">0.99</span>s)
    --- PASS: TestECS/Security_Groups (<span class="hljs-number">0.21</span>s)
PASS
ok      example <span class="hljs-number">12.718</span>s
</code></pre>
<h2>Conclusion</h2>
<p>I can now with confidence test deployed infrastructure using Terraform by using Terratest. I can have Terratest deploy in a separate sandbox environment, validate it works with set checks, and finally destroy the created infrastructure once done. A few takeaways I have from testing IaC with automation are to avoid end-to-end testing, make sure to set a bigger time out on the Go test command with <code>-timeout</code>, and spend time thinking of checks that you want to include in your tests.</p>
<p><strong>Note</strong></p>
<p>HashiCorp has switched from <a href="https://www.mozilla.org/en-US/MPL/2.0/FAQ/">MPL v2 license</a> to a “Business Source License” (BSL). This will have an impact on Terratest going forward past Terraform version 1.5.5, so just make sure for the time being not to upgrade past that version if using Terratest. If interested, look into the <a href="https://opentofu.org/">OpenTofu Linux Project</a>, which has a forked Terraform, and is dedicated to open source.</p>
<p><em>Article Photo by James von Hagel</em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Responsive Web Design: Techniques and Best Practices]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/10/26/Responsive-web-design</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/10/26/Responsive-web-design</guid>
            <pubDate>Thu, 26 Oct 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h1>Responsive Web Design: Techniques and Best Practices</h1>
<p>Responsive web design is a term frequently used in web design and development. But what exactly does it entail? This blog post will introduce the multitude of facets of responsive web design, as it covers more ground than one would initially think.</p>
<h2>Definition</h2>
<blockquote>
<p>"Responsive web design (RWD) is a web development approach that creates dynamic changes to the appearance of a website, depending on the screen size and orientation of the device being used to view it."<br>
- <a href="https://www.nngroup.com/articles/responsive-web-design-definition/">Nielsen Norman Group</a></p>
</blockquote>
<p>While this definition emphasizes screen size, a broader perspective considers the diverse contexts in which a website is accessed:</p>
<blockquote>
<p>Responsive web design is an approach that factors in all the different environments in which a website is displayed</p>
</blockquote>
<h2>Benefits</h2>
<p>In modern web development, focused on metrics, responsive web design is somewhat overlooked. Due to its complex and interpretive nature and the countless possible environments it can be challenging to implement, test, and measure.<br>
It is clear however that, every user having their own unique environment, good responsiveness will have a positive impact.
Each aspect of this guide will feature a brief introduction, methods, guidelines, and potential pitfalls for an effective implementation.</p>
<h2>Display &#x26; Window Parameters</h2>
<p>When it comes to responsive web design, the most crucial parameters are the display and window parameters. These include:</p>
<h3>Width</h3>
<p>Width is the primary consideration in responsive web design. It involves several key elements:</p>
<ul>
<li>Design: Different base designs for various width categories.</li>
<li>Breakpoints: Define the points at which each design is applied based on screen width.</li>
<li>Adaptation: Specify how the design should adjust between these breakpoints. For example, if you have a mobile design at 375px with a breakpoint at 768px, it's essential to define how this design adapts between 375px and 768px.</li>
</ul>
<figure>
<img src="/assets/img/articles/2023-10-26-Responsive-Web-Design-Techniques-and-Best-Practices/breakpoint.webp">
<figcaption>Mobile &#x26; Desktop design</figcaption>
</figure>
<h4>Grid Layout</h4>
<p>One approach to handling these adaptations is by using a <a href="https://m2.material.io/design/layout/responsive-layout-grid.html">grid layout</a> to structure your web page effectively.</p>
<figure>
<img src="/assets/img/articles/2023-10-26-Responsive-Web-Design-Techniques-and-Best-Practices/grid.webp">
<figcaption>Grid layout</figcaption>
</figure>
<h3>Height</h3>
<p>Height impacts what is visible "<a href="https://www.nngroup.com/articles/scrolling-and-attention">above the fold</a>" - the content users see without scrolling. When using tools like Figma, it's not always clear what users will initially see on the website. However, this is essential for user retention. Additionally, from a graphic design perspective, the balance of the page can change when only a portion is visible.</p>
<figure>
<img src="/assets/img/articles/2023-10-26-Responsive-Web-Design-Techniques-and-Best-Practices/above-the-fold.webp">
<figcaption>Above the fold</figcaption>
</figure>
<p>⚠ Keep in mind that on smartphones, the height can change once users start scrolling or when a keyboard appears.</p>
<h4>Vertical Rhythm</h4>
<p>Much like the grid layout, another typography concept to consider is <a href="https://medium.com/built-to-adapt/8-point-grid-vertical-rhythm-90d05ad95032">vertical rhythm</a>. However, implementing this concept can be challenging due to the presence of numerous non-text elements intertwined with text. Additionally, text elements at the same height may be placed within different containers, causing them to flow vertically independently of one another.</p>
<h3>Pixel Density</h3>
<h4>CSS</h4>
<p>Screens have varying pixel densities. For example, some screens can render a 0.5px border, while others will round it up to 1px. While this difference may seem minor, it can add up and affect the alignment of elements on the screen.</p>
<h4>Images</h4>
<p>The <a href="https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images">srcset attribute</a> is a useful tool for serving different images based on factors like screen size, pixel density, and accepted formats. This ensures that the image displayed is of the right size and quality for the device.</p>
<pre><code class="hljs language-html"><span class="hljs-tag">&#x3C;<span class="hljs-name">picture</span>></span>
  <span class="hljs-tag">&#x3C;<span class="hljs-name">source</span>
    <span class="hljs-attr">media</span>=<span class="hljs-string">"(max-width: 767px)"</span>
    <span class="hljs-attr">type</span>=<span class="hljs-string">"image/webp"</span>
    <span class="hljs-attr">srcset</span>=<span class="hljs-string">"image-mobile.webp, image-mobile@2x.webp 2x"</span>
  /></span>
  <span class="hljs-tag">&#x3C;<span class="hljs-name">source</span>
    <span class="hljs-attr">media</span>=<span class="hljs-string">"(min-width: 768px)"</span>
    <span class="hljs-attr">type</span>=<span class="hljs-string">"image/webp"</span>
    <span class="hljs-attr">srcset</span>=<span class="hljs-string">"image-desktop.webp, image-desktop@2x.webp 2x"</span>
  /></span>
  <span class="hljs-tag">&#x3C;<span class="hljs-name">source</span>
    <span class="hljs-attr">media</span>=<span class="hljs-string">"(max-width: 767px)"</span>
    <span class="hljs-attr">type</span>=<span class="hljs-string">"image/webp"</span>
    <span class="hljs-attr">srcset</span>=<span class="hljs-string">"image-mobile.webp, image-mobile@2x.png 2x"</span>
  /></span>
  <span class="hljs-tag">&#x3C;<span class="hljs-name">source</span>
    <span class="hljs-attr">media</span>=<span class="hljs-string">"(min-width: 768px)"</span>
    <span class="hljs-attr">type</span>=<span class="hljs-string">"image/webp"</span>
    <span class="hljs-attr">srcset</span>=<span class="hljs-string">"image-desktop.webp, image-desktop@2x.png 2x"</span>
  /></span>
  <span class="hljs-tag">&#x3C;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"fallback.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"image"</span> /></span>
<span class="hljs-tag">&#x3C;/<span class="hljs-name">picture</span>></span>
</code></pre>
<p>The above code has a different image on a viewport below 768px and serves <a href="https://developers.google.com/speed/webp">webp</a> and retina images <a href="https://caniuse.com/webp">where appropriate</a>.</p>
<h3>Contrast</h3>
<p>Consider contrast when designing for various screens. Cheaper screens tend to have lower contrast ratios, making it difficult to distinguish between certain colors, especially when looking at a screen at an angle. Tools like <a href="https://developer.chrome.com/docs/devtools/accessibility/contrast/#discover-low-contrast">Lighthouse in Chrome</a> can help identify contrast issues.</p>
<p>⚠ It's possible that backgrounds, like alternating rows, may not contrast sufficiently with their surroundings. This doesn't impact text legibility, so might not be flagged by these tools.</p>
<h4>Text Over a Background Image</h4>
<p>When combining responsive web design and text over images, it may happen that at certain widths, the text appears over a part of the image with low contrast. A little trick to improve readability is to use a text shadow.</p>
<p>Subtle shadow:</p>
<pre><code class="hljs language-css"><span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
<span class="hljs-attribute">text-shadow</span>: <span class="hljs-number">1px</span> <span class="hljs-number">1px</span> <span class="hljs-number">0</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.5</span>);
</code></pre>
<p>More pronounced shadow:</p>
<pre><code class="hljs language-css"><span class="hljs-attribute">text-shadow</span>: <span class="hljs-number">1px</span> <span class="hljs-number">1px</span> <span class="hljs-number">0</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.5</span>), -<span class="hljs-number">1px</span> <span class="hljs-number">1px</span> <span class="hljs-number">0</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.5</span>),
  -<span class="hljs-number">1px</span> -<span class="hljs-number">1px</span> <span class="hljs-number">0</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.5</span>), <span class="hljs-number">1px</span> -<span class="hljs-number">1px</span> <span class="hljs-number">0</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.5</span>);
</code></pre>
<figure>
<img src="/assets/img/articles/2023-10-26-Responsive-Web-Design-Techniques-and-Best-Practices/no-text-shadow.webp">
<figcaption>No text shadow</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2023-10-26-Responsive-Web-Design-Techniques-and-Best-Practices/subtle-text-shadow.webp">
<figcaption>Subtle text shadow</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2023-10-26-Responsive-Web-Design-Techniques-and-Best-Practices/pronounced-text-shadow.webp">
<figcaption>Pronounced text shadow</figcaption>
</figure>
<h3>Orientation / Aspect Ratio</h3>
<p>The user might prefer to view certain types of content, such as graphs, tables, and videos, in landscape mode. In this case, it is crucial to consider that fixed position elements, like the header, leave enough space for the rest of the content.</p>
<figure>
<img src="/assets/img/articles/2023-10-26-Responsive-Web-Design-Techniques-and-Best-Practices/landscape.webp">
<figcaption>Large fixed header in landscape mode</figcaption>
</figure>
<p>⚠ If the text size adapts to the view width, using landscape will create a zooming-in effect, which is unlikely to be the intended result of using landscape mode.</p>
<h2>Sizing Units</h2>
<p>In addition to using pixels for sizing, <a href="https://web.dev/blog/viewport-units">viewport units</a> offer a dynamic way to define element sizes. Viewport units are percentages of the viewport's width or height and adapt to various window sizes. For cases where the viewport changes dynamically, such as when a user scrolls on a smartphone, small, large, or dynamic units can be used.</p>
<p>⚠ Be cautious when using 100vw, as a vertical scrollbar can reduce the available width, causing content overflow and the appearance of a horizontal scrollbar.<br>
The following is a workaround:</p>
<pre><code class="hljs language-css"><span class="hljs-attribute">width</span>: <span class="hljs-built_in">calc</span>(<span class="hljs-number">100vw</span> - (<span class="hljs-number">100vw</span> - <span class="hljs-number">100%</span>));
</code></pre>
<h3>REM</h3>
<p>The rem unit is a flexible way to define sizes based on the font size of the root element. For example, if the root font size is 16px, you can set a paragraph's font size as 1rem, which equals 16px.</p>
<pre><code class="hljs language-css"><span class="hljs-selector-tag">html</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
}

<span class="hljs-selector-tag">p</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1rem</span>;
}
</code></pre>
<p>For a fixed design across screen sizes, a combination of viewport width (vw) and rem units can be used. For instance:</p>
<pre><code class="hljs language-css"><span class="hljs-selector-tag">html</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-built_in">calc</span>(<span class="hljs-number">100vw</span> / <span class="hljs-number">375</span>);
}

<span class="hljs-selector-tag">p</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16rem</span>;
}
</code></pre>
<p>In this case, the paragraph font size will be 16px at a viewport width of 375px and adjust proportionally with changes in the window width. This way it is easy to follow the design and use rem instead of px after all values.</p>
<p>⚠ However, when using rem units, the 100vw workaround unfortunately won't work.</p>
<pre><code class="hljs language-css"><span class="hljs-attribute">font-size</span>: <span class="hljs-built_in">calc</span>((<span class="hljs-number">100vw</span> - (<span class="hljs-number">100vw</span> - <span class="hljs-number">100%</span>)) / <span class="hljs-number">375</span>);
</code></pre>
<h2>Scrolling</h2>
<p>When the content exceeds its container's dimensions, it's essential to define the scroll containers and specify their scrolling behavior, such as "normal," "fixed," or "sticky."</p>
<p>Mobile browsers have a scrollbar with the overlay property, <a href="https://caniuse.com/mdn-css_properties_overlay">a feature not widely available on non-mobile browsers</a>. An alternative solution is the use of <a href="https://caniuse.com/mdn-css_properties_scrollbar-gutter">scrollbar-gutter</a>; however, as of now, it is not universally supported.</p>
<p>On desktop, vertical scrolling is preferred, since it is easily accessible via the scroll wheel. This allows to have a narrower, more discrete vertical scrollbar. A horizontal scrollbar will need to be larger and more obtrusive, since the scrollbar thumb will need to be used.<br>
On mobile on the other hand, is is advised to limit vertical scrolling to the whole page. When the content exceeds its container, horizontal scrolling can be easily done by swiping.</p>
<p>⚠ In both cases, avoid using infinite scrolling if a footer is present, since it will become unreachable.</p>
<p>⚠ Avoid using two nested vertical scrollbars as it can lead to a confusing user experience. Using the mouse wheel inside such an element will cause an inconsistent scrolling behavior depending on the position of both scroll bars.</p>
<h4>Different Scroll Patterns Based on the Same Design</h4>
<p>The following example shows how one design can have a wide array of possible scroll patterns.</p>
<figure>
<img src="/assets/img/articles/2023-10-26-Responsive-Web-Design-Techniques-and-Best-Practices/scroll-design.webp">
<figcaption>Design</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2023-10-26-Responsive-Web-Design-Techniques-and-Best-Practices/scroll-patterns.gif">
<figcaption>Scroll patterns</figcaption>
</figure>
<h3>Scroll Position</h3>
<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API">Intersection Observer API</a> has better performance than checking for the scroll position on scroll and resize. With responsive web design there's the additional benefit that the point at which a certain event is triggered can easily be controlled with an element that is variable in size.</p>
<h2>Browser &#x26; OS</h2>
<h3>Fonts</h3>
<p>When choosing fonts, keep in mind that every OS has its own system fonts and a <a href="https://css-tricks.com/snippets/css/system-font-stack/">font stack</a> is recommended.</p>
<p>To ensure uniformity across different platforms, web fonts can also be used. The fonts can be hosted with the rest of the code.
Alternatively, services like <a href="https://fonts.google.com">Google Fonts</a> provide a convenient way to access a wide variety of fonts. These services often split font files into smaller parts, ensuring that only the necessary font subsets are downloaded. This approach is particularly recommended for extensive font sets, such as Japanese characters, to optimize loading performance.</p>
<p>However, it's worth noting that slight variations in rendering may still occur depending on the browser being used.</p>
<p>The following examples use Google Fonts with a fixed css, but still have slight differences across browsers.</p>
<pre><code class="hljs language-css"><span class="hljs-attribute">font-size</span>: <span class="hljs-number">24px</span>;
<span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.5</span>;
</code></pre>
<figure>
<img src="/assets/img/articles/2023-10-26-Responsive-Web-Design-Techniques-and-Best-Practices/fonts-win-chrome.webp">
<figcaption>Windows Chrome</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2023-10-26-Responsive-Web-Design-Techniques-and-Best-Practices/fonts-win-firefox.webp">
<figcaption>Windows Firefox</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2023-10-26-Responsive-Web-Design-Techniques-and-Best-Practices/fonts-mac-chrome.webp">
<figcaption>Mac Chrome</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2023-10-26-Responsive-Web-Design-Techniques-and-Best-Practices/fonts-mac-firefox.webp">
<figcaption>Mac Firefox</figcaption>
</figure>
<figure>
<img srcset="/assets/img/articles/2023-10-26-Responsive-Web-Design-Techniques-and-Best-Practices/fonts-mac-safari.webp, /assets/img/articles/2023-10-26-Responsive-Web-Design-Techniques-and-Best-Practices/fonts-mac-safari@2.webp 2x" src="/assets/img/articles/2023-10-26-Responsive-Web-Design-Techniques-and-Best-Practices/fonts-mac-safari.webp">
<figcaption>Mac Safari</figcaption>
</figure>
<h3>Graceful Degradation &#x26; Progressive Enhancement</h3>
<p><a href="https://developer.mozilla.org/en-US/docs/Glossary/Graceful_degradation">Graceful degradation</a> and <a href="https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement">progressive enhancement</a> are design principles that utilize the latest browser features while maintaining functionality on legacy browsers, at the cost of performance and design variations. It can be applied to both javascript and css features.<br>
For instance, consider the <a href="https://caniuse.com/flexbox-gap">flex box gap property</a>, which is unavailable on browsers earlier than iOS 14.5. This will result in layout differences, but won't disrupt the user experience, and takes less time to implement than the old method.</p>
<p><a href="https://caniuse.com">CanIUse</a> is the reference for assessing browser compatibility.<br>
Tools like <a href="https://modernizr.com">Modernizr</a> offer an extensive list of checks to implement in your website, enabling you to handle exceptions in your styling and code effectively.</p>
<p>This approach isn't just limited to browser functionalities; it can also be applied to screen sizes to ensure the layout remains functional on less common sizes, even if it's not pixel-perfect in all cases.</p>
<h2>Touch Screens</h2>
<p>Designing for different screen types involves considering how users interact with the content.</p>
<p>On conventional screens, hover states can convey information, they are, however, unavailable on touch screens. This distinction must be taken into account when designing user interfaces.</p>
<p>Touch screens on the other hand have other events like swipes and pinches, which aren't available on conventional screens. Also since swiping is used for scrolling, the presence or not of scrollbars doesn't have to be accounted for on mobile designs.</p>
<p>Additionally, touch screen devices offer unique input behaviors, such as keyboard types and select lists. When designing for mobile screens, it's often beneficial to prioritize using native select elements rather than custom-styled ones.</p>
<h2>Media Queries</h2>
<p>While they are commonly used to adjust styles based on screen width and print, properties such as orientation, resolution, pointer type, hover capability, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries">and many more are available</a>.</p>
<p>When to use media queries and when other solutions are preferred is discussed further in the Design System section of this article.</p>
<h2>Component Drive Design</h2>
<p><a href="https://www.componentdriven.org/">Component Driven Design</a> is a methodology similar to atomic design, where applications are built starting from the smallest components. This approach emphasizes that a component should adapt to its container. Consequently, a web page primarily serves as a container for arranging these components, simplifying the process of achieving responsiveness.</p>
<h3>Design Systems</h3>
<p>Whether you're using a component library like Material or Ant Design, or creating your own there are 2 possible approaches to responsive web design.</p>
<ol>
<li>Use the same style on all sizes and differentiate with properties. e.g. a large button on mobile and a medium one on desktop.</li>
<li>Different styles separated by media queries. e.g. a large button on all screen sizes, but with a different style.</li>
</ol>
<p>The second method is preferred since it doesn't rely on javascript and isn't impacted by server side rendering.</p>
<p>As a general rule use media queries when components have a different base design, bug avoid using them for design differences based on the available space.</p>
<p>Consider the following case, an article component has an image and text. The image is to the left and the text to the right, but when the width is limited, the image takes the full width and the text comes below it. Since we don't know how much space will be available we can't rely on media queries (its container can have several columns, or there can be a side menu).<br>
Below is an example how this can be solved, using css values like <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/min">min</a></p>
<pre><code class="hljs language-html"><span class="hljs-tag">&#x3C;<span class="hljs-name">article</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"article"</span>></span>
  <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"article_image_container"</span>></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"article_image"</span> /></span>
  <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
  <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"article_text"</span>></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"article_title"</span>></span>Lorem ipsum dolor sit amet<span class="hljs-tag">&#x3C;/<span class="hljs-name">h1</span>></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"article_description"</span>></span>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
      tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
      veniam, quis nostrud exercitation
    <span class="hljs-tag">&#x3C;/<span class="hljs-name">p</span>></span>
  <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
<span class="hljs-tag">&#x3C;/<span class="hljs-name">article</span>></span>
</code></pre>
<pre><code class="hljs language-css"><span class="hljs-selector-class">.article</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">align-content</span>: flex-start;
  <span class="hljs-comment">/* Wrap allows the text to be next or under the image */</span>
  <span class="hljs-attribute">flex-wrap</span>: wrap;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ccc</span>;
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">16px</span>;
}

<span class="hljs-selector-class">.article_image_container</span> {
  <span class="hljs-comment">/* 
    With flex the image will grow vertically with the text
    With block there is an extra space below the image
  */</span>
  <span class="hljs-attribute">display</span>: grid;
  <span class="hljs-comment">/* Width of the image in horizontal layout */</span>
  <span class="hljs-attribute">min-width</span>: <span class="hljs-number">200px</span>;
  <span class="hljs-attribute">flex</span>: <span class="hljs-number">1</span>;
}
<span class="hljs-selector-class">.article_image</span> {
  aspect-ratio: <span class="hljs-number">4</span>/<span class="hljs-number">3</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">background</span>: <span class="hljs-number">#ccc</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">16px</span>;
}

<span class="hljs-selector-class">.article_text</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">flex</span>: <span class="hljs-number">1</span>;
  <span class="hljs-comment">/*
    If the container is smaller than the image min-width and gap (216px) + 240px
    the text container will be under the image
    If the container is wider, the text will take its full width - (216px)
  */</span>
  <span class="hljs-attribute">min-width</span>: <span class="hljs-built_in">max</span>(<span class="hljs-number">240px</span>, <span class="hljs-built_in">calc</span>(<span class="hljs-number">100%</span> - <span class="hljs-number">216px</span>));
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">16px</span>;
}

<span class="hljs-selector-class">.article_title</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">24px</span>;
  <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.3</span>;
}

<span class="hljs-selector-class">.article_description</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">text-align</span>: justify;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
  <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.5</span>;
}
</code></pre>
<figure>
<img src="/assets/img/articles/2023-10-26-Responsive-Web-Design-Techniques-and-Best-Practices/component.gif">
<figcaption>Responsive component</figcaption>
</figure>
<h4>Libraries</h4>
<p>Libraries like Material (<a href="https://mui.com">React</a>, <a href="https://Vuetifyjs.com">Vue</a>, <a href="https://material.angular.io">Angular</a>) and <a href="https://ant.design">Ant Design</a> form a good basis for medium-sized projects. (They can be a bit heavy for smaller projects and larger projects might benefit more from a tailor-made in-house library).<br>
They've been extensively tested across diverse environments, making them reliable choices for responsive web design.<br>
They can be customized to fit most designs, but the more they're modified, the higher the risk of introducing bugs. It is therefore important to align with the design team at the start of the project, what library to use, and review together what can be customized at what cost.<br>
They have the additional benefit that the components already use best practices. There also is less need for the UI and UX team to define all states and use cases, nor for the developers to implement them from scratch.</p>
<h3>Storybook</h3>
<p><a href="https://storybook.js.org">Storybook</a>, which is a tool for constructing and testing UI components and pages in isolation, and is ideal for Component Driven Design.
Additionally, Storybook enables <a href="https://storybook.js.org/docs/web-components/writing-tests/visual-testing">visual testing</a> for different browsers and various <a href="https://storybook.js.org/docs/web-components/essentials/viewport">viewport sizes</a>.</p>
<h2>Accessibility</h2>
<p>Accessibility deserves its own in-depth article, but here are some points to consider when implementing responsive web design.</p>
<h3>Screen Readers</h3>
<p>In the process of implementing responsive layouts, it's sometimes necessary to adjust the order of elements. It's crucial to remember that these changes can also affect the experience of users relying on screen readers as well as SEO.</p>
<h3>Font Size</h3>
<p>When using font sizes adapting to screen width, make sure that the minimum possible font size is still readable.</p>
<pre><code class="hljs language-css"><span class="hljs-selector-tag">p</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-built_in">max</span>(<span class="hljs-number">16rem</span>, <span class="hljs-number">12px</span>);
}
</code></pre>
<h2>Testing Environment</h2>
<p>Tools like <a href="https://www.browserstack.com">browserstack</a> can be used for cross-browser and cross-device testing. They're helpful for ensuring web applications function correctly and appear consistent on various browsers, devices, and OS. Additionally, they provide debugging and inspection tools to streamline issue resolution and quality assurance in web development.</p>
<h3>Google Analytics</h3>
<p>It can be costly and time-consuming to achieve a perfect result across all environments, it is therefore important to prioritize. <a href="https://analytics.google.com">Google Analytics</a> provides valuable data on the devices, operating systems, and screen sizes that your audience uses.
When making decisions about design parameters and testing, analyze this data to help achieve the most efficient coverage.</p>
<h2>SEO</h2>
<p>Google takes into account mobile-friendliness to rank websites. Since performance is also an important factor, it is crucial to strike the right balance between serving the right content quickly for the right environment, while limiting the burden of having content served for other environments or too many javascript checks when loading the page.</p>
<h2>Text Ellipsis</h2>
<p>Text ellipsis can be used when text overflows, but scrolling is not desired (small containers like chips or table cells). CSS (<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/text-overflow">ellipsis</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-line-clamp">line clamp</a>) offers several advantages over a fixed javascript string length, since it adapts to different character and container sizes.</p>
<p>⚠ Make sure that, when using an ellipsis, there is a way for users to visualize the full text.
In some cases, the capability to select and copy the full text should also be maintained. This can be an issue with tooltips.</p>
<h2>Collaborating With UI &#x26; UX Designers</h2>
<p>The earlier in the project there is an alignment between the developers and designers, the better. Understanding each other's viewpoints and issues will help the implementation. A common view on the design philosophy and clear initial guidelines will also make it possible for developers to implement the designs without having to burden the designers with creating every possible state and use case for every page and component.</p>
<h2>References</h2>
<ul>
<li><a href="https://www.nngroup.com/articles/responsive-web-design-definition">Responsive Web Design (RWD) and User Experience</a></li>
<li><a href="https://m2.material.io/design/layout/responsive-layout-grid.html">Responsive layout grid</a></li>
<li><a href="https://www.nngroup.com/articles/scrolling-and-attention/">Scrolling and Attention</a></li>
<li><a href="https://medium.com/built-to-adapt/8-point-grid-vertical-rhythm-90d05ad95032">8-Point Grid: Vertical Rhythm</a></li>
<li><a href="https://developer.mozilla.org/en-US/">Mozilla Resources for Developers, by Developers</a></li>
<li><a href="https://developers.google.com/speed/webp">An image format for the Web</a></li>
<li><a href="https://caniuse.com">Can I use</a></li>
<li><a href="https://developer.chrome.com/docs/devtools/accessibility/contrast">Make your website more readable</a></li>
<li><a href="https://web.dev/blog/viewport-units">The large, small, and dynamic viewport units</a></li>
<li><a href="https://css-tricks.com/snippets/css/system-font-stack/">System Font Stack</a></li>
<li><a href="https://modernizr.com/">Modernizr</a></li>
<li><a href="https://www.componentdriven.org/">Component Driven User Interfaces</a></li>
<li><a href="https://storybook.js.org/">Storybook</a></li>
</ul>
<p><em>Header, grid and responsive design images by <a href="https://design-journal.monstar-lab.com/category/journal/writer/misono/">Hanaka Misono</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Composable architecture and TDD. Can you make it work?]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/10/26/The-Composable-architecture-and-TDD</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/10/26/The-Composable-architecture-and-TDD</guid>
            <pubDate>Thu, 26 Oct 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Recently, our team's attention shifted towards The Composable architecture. This is a very powerful framework to build iOS applications. The goal of this article is to show that you can build the whole app logic without creating any UI with the help of TCA and TDD. It will not cover TCA basics and will be focused more on showing testing capabilities of TCA.</p>
<h3>Theory walkthrough</h3>
<h4>TCA</h4>
<p>If you are reading this article I would assume that you already have some knowledge about what is TCA. Anyway here below you can find a quick brief with a diagram about this architecture.
![TCA Diagram](/assets/img/articles/2023-10-26-The-Composable-architecture-and-TDD/TCA-diagram.webp
The Composable Architecture is a framework that can be used to build state-driven, testable applications. In TCA, each feature is represented by a Store that holds the feature's State and Reducer function. Feature State is updated by sending an Action to the Reducer function. The Reducer function takes the current state and action as parameters and mutates the current state to produce a new state. The View observes the state and updates itself when the state changes. <br>
Additionally, the Store manages feature Environment dependencies (API client, database client, location manager, etc.) that can be invoked by the Reducer function, potentially producing Effects. Effects can, in turn, trigger another action to be sent to the Reducer. This simple flow ensures that feature data flows in a unidirectional manner and decouples the View from the business logic, which is crucial when developing a mobile application. <br>
Keeping this in mind, we can recognise one of the key advantages of TCA - Testability. TCA strictly separates the view from business logic, enabling it to be tested without the need to create any views or provide any runtime dependencies. <br>
If you want to dive into the TCA basics, here is a list of available resources that you can explore:</p>
<ul>
<li><a href="https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture">TCA Documentation</a></li>
<li><a href="https://github.com/pointfreeco/swift-composable-architecture">GitHub repo with CaseStudies</a></li>
<li><a href="https://www.pointfree.co/collections/composable-architecture">Pointfree videos</a></li>
</ul>
<h4>TDD</h4>
<p>I think that the best definition of what is TDD is given in <em>Test-Driven iOS Development with Swift</em> book by Dr. Dominik Hauser:</p>
<blockquote>
<p>As the name suggests, in TDD, tests drive the development. This means that the developer writes code only because there is a test that fails. The tests dictate whether the code has to be written, and they also provide a measure when a feature is "done" - it is done when all tests for the feature pass.</p>
</blockquote>
<p>The TDD flow is simple and consists of only three steps:</p>
<ul>
<li><strong>Red</strong> - you start by writing a failing test. It needs to test a required feature of the software product that is not already implemented or an edge case that you want to make sure is covered.</li>
<li><strong>Green</strong> - in the green step, you write the simplest code that makes the test pass.</li>
<li><strong>Refactor</strong> - in the refactor step you improve the code. You remove duplication, extract common values, and so on. You should never skip this step and always think how you can improve your code. When you came up with an improvement, first make sure it's reflected in your test and then refactor an implementation. <strong>Not vice-versa!</strong></li>
</ul>
<h3>Practice</h3>
<h4>Testing State changes</h4>
<p>Let's say I want to have a TextField where user will input a name of their friend. There also will be a Discover button that should be enabled only when input name is not empty.</p>
<h5>❌ Red:</h5>
<p>First, let's start with implementing a failing test. Here, in the screenshot below, you can see how the TCA library conveniently displays failing tests. It highlights all state changes and fields that differ from the expected output.
![Failing test](/assets/img/articles/2023-10-26-The-Composable-architecture-and-TDD/failing-test-screenshot.webp</p>
<h5>✅ Green:</h5>
<p>Now let's make this test pass by implementing the logic:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">public</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">HomeFeature</span>: <span class="hljs-title class_">Reducer</span> {
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">State</span>: <span class="hljs-title class_">Equatable</span> {
        <span class="hljs-meta">@BindingState</span> <span class="hljs-keyword">var</span> name: <span class="hljs-type">String</span> <span class="hljs-operator">=</span> <span class="hljs-string">""</span>
        <span class="hljs-keyword">var</span> discoverButtonDisabled: <span class="hljs-type">Bool</span> <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>
    }
    
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">enum</span> <span class="hljs-title class_">Action</span>: <span class="hljs-title class_">Equatable</span>, <span class="hljs-title class_">BindableAction</span> {
        <span class="hljs-keyword">case</span> binding(<span class="hljs-type">BindingAction</span>&#x3C;<span class="hljs-type">State</span>>)
    }
    
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">ReducerOf</span>&#x3C;<span class="hljs-keyword">Self</span>> {
        <span class="hljs-type">BindingReducer</span>()
        <span class="hljs-type">Reduce</span> { state, action <span class="hljs-keyword">in</span>
            <span class="hljs-keyword">switch</span> action {
            <span class="hljs-keyword">case</span> .binding(\.<span class="hljs-variable">$name</span>):
                state.discoverButtonDisabled <span class="hljs-operator">=</span> state.name.isEmpty
                <span class="hljs-keyword">return</span> .none
            <span class="hljs-keyword">case</span> .binding(<span class="hljs-keyword">_</span>):
                <span class="hljs-keyword">return</span> .none
            }
        }
    }
}
</code></pre>
<p>All good, now the test passes:
![Passing test](/assets/img/articles/2023-10-26-The-Composable-architecture-and-TDD/passing-test-screenshot.webp</p>
<h5>📈 Refactor:</h5>
<p>I want to refactor the <code>discoverButtonDisabled</code> boolean in this section. I anticipate that in the future, this Discover button will trigger an API call and be replaced by a <code>ProgressView</code>. To prepare for this, let's convert the boolean into an enum value. Again, first start by changing your test implementation and then move to the Feature. <br>
<strong>Test:</strong></p>
<pre><code class="hljs language-swift">    <span class="hljs-keyword">func</span> <span class="hljs-title function_">testNameInput</span>() <span class="hljs-keyword">async</span> {
        <span class="hljs-keyword">let</span> store <span class="hljs-operator">=</span> <span class="hljs-type">TestStore</span>(initialState: <span class="hljs-type">HomeFeature</span>.<span class="hljs-type">State</span>()) { <span class="hljs-comment">// Create test store for HomeFeature</span>
            <span class="hljs-type">HomeFeature</span>()
        }
        
        <span class="hljs-comment">// Send action to imulate user input</span>
        <span class="hljs-keyword">await</span> store.send(.binding(.set(\.<span class="hljs-variable">$name</span>, <span class="hljs-string">"Steave"</span>))) {
            <span class="hljs-comment">// Assert name was changed to 'Steave'</span>
            <span class="hljs-variable">$0</span>.name <span class="hljs-operator">=</span> <span class="hljs-string">"Steave"</span>
            <span class="hljs-comment">// Assert that Discover button was enabled</span>
            <span class="hljs-variable">$0</span>.discoverButtonState <span class="hljs-operator">=</span> .enabled <span class="hljs-comment">// Compiler error will be raised.</span>
        }
    }
</code></pre>
<p><strong>Implementation:</strong></p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">public</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">HomeFeature</span>: <span class="hljs-title class_">Reducer</span> {
    <span class="hljs-keyword">enum</span> <span class="hljs-title class_">ButtonState</span> {
        <span class="hljs-keyword">case</span> enabled
        <span class="hljs-keyword">case</span> disabled
        <span class="hljs-keyword">case</span> loading
    }
    
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">State</span>: <span class="hljs-title class_">Equatable</span> {
        <span class="hljs-meta">@BindingState</span> <span class="hljs-keyword">var</span> name: <span class="hljs-type">String</span> <span class="hljs-operator">=</span> <span class="hljs-string">""</span>
        <span class="hljs-keyword">var</span> discoverButtonState: <span class="hljs-type">ButtonState</span> <span class="hljs-operator">=</span> .disabled
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">enum</span> <span class="hljs-title class_">Action</span>: <span class="hljs-title class_">Equatable</span>, <span class="hljs-title class_">BindableAction</span> {
        <span class="hljs-keyword">case</span> binding(<span class="hljs-type">BindingAction</span>&#x3C;<span class="hljs-type">State</span>>)
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">ReducerOf</span>&#x3C;<span class="hljs-keyword">Self</span>> {
        <span class="hljs-type">BindingReducer</span>()
        <span class="hljs-type">Reduce</span> { state, action <span class="hljs-keyword">in</span>
            <span class="hljs-keyword">switch</span> action {
            <span class="hljs-keyword">case</span> .binding(\.<span class="hljs-variable">$name</span>):
                state.discoverButtonState <span class="hljs-operator">=</span> state.name.isEmpty <span class="hljs-operator">?</span> .disabled : .enabled
                <span class="hljs-keyword">return</span> .none
            <span class="hljs-keyword">case</span> .binding(<span class="hljs-keyword">_</span>):
                <span class="hljs-keyword">return</span> .none
            }
        }
    }
}
</code></pre>
<h4>Testing effects</h4>
<p>In TCA, Effects play a big role, and it is crucial to be able to test them. Let's take a look how this is done. The logic is that when user presses the Discover button, an API request is fired in order to get the data to be shown. If the API request is successful, then a sheet showing the data should be opened. Otherwise, it should show an alert with an error message.</p>
<p><strong>Successfull response</strong></p>
<pre><code class="hljs language-swift">    <span class="hljs-keyword">func</span> <span class="hljs-title function_">testDiscoverWishesTapped</span>() <span class="hljs-keyword">async</span> {
        <span class="hljs-keyword">let</span> store <span class="hljs-operator">=</span> <span class="hljs-type">TestStore</span>(initialState: <span class="hljs-type">HomeFeature</span>.<span class="hljs-type">State</span>()) {
            <span class="hljs-type">HomeFeature</span>()
        } withDependencies: {
            <span class="hljs-variable">$0</span>.apiClient <span class="hljs-operator">=</span> .testValue <span class="hljs-comment">// Use mock API client</span>
        }
        store.exhaustivity <span class="hljs-operator">=</span> .off(showSkippedAssertions: <span class="hljs-literal">true</span>) <span class="hljs-comment">// Test only assertions</span>
        <span class="hljs-keyword">await</span> store.send(.discoverWishesTapped) <span class="hljs-comment">// Nothing happens</span>
        
        <span class="hljs-comment">// User inputs name</span>
        <span class="hljs-keyword">await</span> store.send(.binding(.set(\.<span class="hljs-variable">$name</span>, <span class="hljs-string">"Steave"</span>))) 
        
        <span class="hljs-comment">// expected wishes</span>
        <span class="hljs-keyword">let</span> expected <span class="hljs-operator">=</span> <span class="hljs-keyword">try!</span> <span class="hljs-keyword">await</span> <span class="hljs-type">APIClient</span>.mock.requestWishes()
        
        <span class="hljs-comment">// User taps discover</span>
        <span class="hljs-keyword">await</span> store.send(.discoverWishesTapped) {
            <span class="hljs-variable">$0</span>.discoverButtonState <span class="hljs-operator">=</span> .loading
        }
        <span class="hljs-comment">// Receive wishes and open sheet</span>
        <span class="hljs-keyword">await</span> store.receive(.wishesResponse(.success(expected)), timeout: .seconds(<span class="hljs-number">0.1</span>)) {
            <span class="hljs-variable">$0</span>.discoverButtonState <span class="hljs-operator">=</span> .enabled
            <span class="hljs-variable">$0</span>.destination <span class="hljs-operator">=</span> .submitWish(<span class="hljs-type">SubmitWishFeature</span>.<span class="hljs-type">State</span>(name: <span class="hljs-string">"Steave"</span>, wishes: expected.wishes))
        }
        
        <span class="hljs-comment">// Check if we actually created scoping between two features</span>
        <span class="hljs-comment">// by sending an action to child store through the parant</span>
        <span class="hljs-keyword">await</span> store.send(.destination(.presented(.submitWish(.selectWish(<span class="hljs-number">0</span>))))) {
            <span class="hljs-variable">$0</span>.<span class="hljs-variable">$destination</span>[<span class="hljs-keyword">case</span>: <span class="hljs-operator">/</span><span class="hljs-type">HomeFeature</span>.<span class="hljs-type">Destination</span>.<span class="hljs-type">State</span>.submitWish]<span class="hljs-operator">?</span>
                .selectedWish <span class="hljs-operator">=</span> .<span class="hljs-keyword">init</span>(id: <span class="hljs-string">"mock1"</span>, title: <span class="hljs-string">"Health"</span>, iconName: <span class="hljs-string">"heart.square"</span>)
        }
    }
</code></pre>
<p><strong>Failing response</strong></p>
<pre><code class="hljs language-swift">    <span class="hljs-keyword">func</span> <span class="hljs-title function_">testFailingDiscoverWishes</span>() <span class="hljs-keyword">async</span> {
        <span class="hljs-keyword">let</span> store <span class="hljs-operator">=</span> <span class="hljs-type">TestStore</span>(initialState: <span class="hljs-type">HomeFeature</span>.<span class="hljs-type">State</span>(name: <span class="hljs-string">"Steave"</span>)) {
            <span class="hljs-type">HomeFeature</span>()
        } withDependencies: {
            <span class="hljs-variable">$0</span>.apiClient <span class="hljs-operator">=</span> .failing <span class="hljs-comment">// set API client to failing</span>
        }
        
        <span class="hljs-comment">// Request is triggered</span>
        <span class="hljs-keyword">await</span> store.send(.discoverWishesTapped) {
            <span class="hljs-variable">$0</span>.discoverButtonState <span class="hljs-operator">=</span> .loading
        }
        
        <span class="hljs-comment">// Failed response, expect that apiError will be shown</span>
        <span class="hljs-keyword">await</span> store.receive(.wishesResponse(.failure(<span class="hljs-type">APIError</span>.callUnimplemented)), timeout: .seconds(<span class="hljs-number">0.1</span>)) { 
            <span class="hljs-variable">$0</span>.discoverButtonState <span class="hljs-operator">=</span> .enabled
            <span class="hljs-variable">$0</span>.destination <span class="hljs-operator">=</span> .alert(.apiError)
        }
        
        <span class="hljs-comment">// Test that user can close the alert</span>
        <span class="hljs-keyword">await</span> store.send(.destination(.presented(.alert(.ok)))) {
            <span class="hljs-variable">$0</span>.destination <span class="hljs-operator">=</span> <span class="hljs-literal">nil</span>
        }
    }
</code></pre>
<p>Now let's focus on key TCA features that are used here:</p>
<ul>
<li><code>.receive()</code> - API response produces an effect with the result, wich has to be received in the test. This method will do it for you and you will be able to make assertions agains the new State. On the other hand if there is some Effect that was not received, the test will fail.</li>
<li><code>withDependency</code> - Setting this parameter is a great way to control dependencies you provide to a test. In this example I use different API client implementations. One for mock another one for failing.</li>
<li><code>exhaustivity</code> - When this parameter is set to .on, the test framework checks whether all possible code paths and conditions in your application have been tested. If you skip an assertion for some state value, the test will fail. In cases where it's set to .off, only the assertions you make will be tested.</li>
<li><code>destination</code> - This is a <code>@PresentationState</code> value which determines a state of presented sheet/alert. Nothing is presented when it's set to <code>nil</code>. In addition, it tests whether parent and child domains can exchange actions between each other by sending the <code>.selectWish</code> action from the parent to the child. In TCA, this is basically called scoping.</li>
</ul>
<h4>Testing Navigation stack</h4>
<p>As you might know, the TCA framework provides an out-of-the-box solution for stack navigation with the help of <code>StackState</code>. Essentially, it holds the states of all child domains and allows you to push and pop them, as well as send actions between them. Because it's state-driven, you can easily test it. <br>
In the following example, the user is presented with a list of wishes. The user is able to select only one wish and after pressing the Proceed button, they are taken to results screen. Let's take a look how this behaviour can be tested.</p>
<pre><code class="hljs language-swift">    <span class="hljs-keyword">func</span> <span class="hljs-title function_">testProceedButtonTapped</span>() <span class="hljs-keyword">async</span> {
        <span class="hljs-keyword">let</span> store <span class="hljs-operator">=</span> <span class="hljs-type">TestStore</span>(initialState: <span class="hljs-type">SubmitWishFeature</span>.<span class="hljs-type">State</span>(name: <span class="hljs-string">"Steave"</span>, wishes: wishes)) {
            <span class="hljs-type">SubmitWishFeature</span>()
        }
        <span class="hljs-comment">// Nothing happens if nothing is selected</span>
        <span class="hljs-keyword">await</span> store.send(.proceedButtonTapped)
        
        <span class="hljs-comment">// User selects a wish</span>
        <span class="hljs-keyword">await</span> store.send(.selectWish(<span class="hljs-number">0</span>)) {
            <span class="hljs-variable">$0</span>.buttonState <span class="hljs-operator">=</span> .enabled
            <span class="hljs-variable">$0</span>.selectedWish <span class="hljs-operator">=</span> .<span class="hljs-keyword">init</span>(id: <span class="hljs-string">"mock1"</span>, title: <span class="hljs-string">"Health"</span>, iconName: <span class="hljs-string">"heart.square"</span>)
        }
        
        <span class="hljs-comment">// Request is sent to save selection on backend side</span>
        <span class="hljs-keyword">await</span> store.send(.proceedButtonTapped) {
            <span class="hljs-variable">$0</span>.buttonState <span class="hljs-operator">=</span> .loading
        }
        
        <span class="hljs-comment">// Receive successfull response and open Result screen.</span>
        <span class="hljs-keyword">await</span> store.receive(.saveWishResponse(.success(.<span class="hljs-keyword">init</span>())), timeout: .seconds(<span class="hljs-number">0.1</span>)) {
            <span class="hljs-variable">$0</span>.buttonState <span class="hljs-operator">=</span> .enabled
            <span class="hljs-variable">$0</span>.path[id: <span class="hljs-number">0</span>] <span class="hljs-operator">=</span> .result(.<span class="hljs-keyword">init</span>(name: <span class="hljs-string">"Steave"</span>, wish: .<span class="hljs-keyword">init</span>(id: <span class="hljs-string">"mock1"</span>, title: <span class="hljs-string">"Health"</span>, iconName: <span class="hljs-string">"heart.square"</span>)))
        }
    }
</code></pre>
<p>Once response is received a new screen is pushed to navigation stack. We can test this by getting the first element from the <code>path</code> and asserting against its state.</p>
<p>Moreover, with TCA you can test the navigation by sending push/pop as well as child domain actions directly. Check out the following example:</p>
<pre><code class="hljs language-swift">    <span class="hljs-keyword">func</span> <span class="hljs-title function_">testResultNavigation</span>() <span class="hljs-keyword">async</span> {
        <span class="hljs-keyword">let</span> store <span class="hljs-operator">=</span> <span class="hljs-type">TestStore</span>(initialState: <span class="hljs-type">SubmitWishFeature</span>.<span class="hljs-type">State</span>(name: <span class="hljs-string">"Steave"</span>, wishes: wishes)) {
            <span class="hljs-type">SubmitWishFeature</span>()
        }
      
        <span class="hljs-comment">// Directly send push action to the store</span>
        <span class="hljs-keyword">await</span> store.send(.path(.push(id: <span class="hljs-number">0</span>, state: .result(<span class="hljs-type">ResultWishFeature</span>.<span class="hljs-type">State</span>(name: <span class="hljs-string">"Steave"</span>, wish: .<span class="hljs-keyword">init</span>(id: <span class="hljs-string">"mock1"</span>, title: <span class="hljs-string">"Health"</span>, iconName: <span class="hljs-string">"heart.square"</span>)))))) {
            <span class="hljs-variable">$0</span>.path[id: <span class="hljs-number">0</span>] <span class="hljs-operator">=</span> .result(<span class="hljs-type">ResultWishFeature</span>.<span class="hljs-type">State</span>(name: <span class="hljs-string">"Steave"</span>, wish: .<span class="hljs-keyword">init</span>(id: <span class="hljs-string">"mock1"</span>, title: <span class="hljs-string">"Health"</span>, iconName: <span class="hljs-string">"heart.square"</span>)))
        }
        
        <span class="hljs-comment">// Send action into child domain and assert State changes.</span>
        <span class="hljs-keyword">await</span> store.send(.path(.element(id: <span class="hljs-number">0</span>, action: .result(.shareInMyProfileTapped)))) {
            <span class="hljs-variable">$0</span>.path[id: <span class="hljs-number">0</span>, <span class="hljs-keyword">case</span>: <span class="hljs-operator">/</span><span class="hljs-type">SubmitWishFeature</span>.<span class="hljs-type">Path</span>.<span class="hljs-type">State</span>.result]<span class="hljs-operator">?</span>.isSharedInMyProfile <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>
        }
        
        <span class="hljs-comment">// Pop from child domain and assert navigation stack is empty</span>
        <span class="hljs-keyword">await</span> store.send(.path(.popFrom(id: <span class="hljs-number">0</span>))) {
            <span class="hljs-variable">$0</span>.path <span class="hljs-operator">=</span> <span class="hljs-type">StackState</span>()
        }
    }
</code></pre>
<h4>Conclusion</h4>
<p>Here we've covered testing of the most esential parts of any iOS application:</p>
<ul>
<li>State changes by user input or user interaction</li>
<li>State changes by Effects</li>
<li>Dependency management inside tests</li>
<li>Presentation of sheets and alerts</li>
<li>Navigation stacks testing</li>
</ul>
<p>All of these aspects are covered by the TCA framework out-of-the-box. Combined with a strong separation of business logic from the UI, it ensures the full testability of iOS apps. Moreover, TCA employs scoping, enabling the exchange of data and actions between different parts of the application. Essentially, it transforms the entire app into a global state, consisting of states from standalone features. This allows developers to test comprehensive scenarios involving different features rather than small, isolated parts of the app. This capability of TCA is crucial when employing Test-Driven Development for application development.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Getting started with Swift Charts - part 2]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/10/23/Getting-started-with-Swift-Charts-part-2</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/10/23/Getting-started-with-Swift-Charts-part-2</guid>
            <pubDate>Mon, 23 Oct 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Last time we looked at <a href="/en/post/2023/10/23/Getting-started-with-Swift-Charts">how to get started with Swift Charts</a>. Let's pick up where we left off.</p>
<p>We have the data, we have three ChartTypes, which show temperature (line chart), precipitation (bar chart) and wind (double line chart) and we have customized the axes.</p>
<p><img src="/assets/img/articles/2023-10-23-Getting-started-with-Swift-Charts/5.webp" alt=""></p>
<h2>Data pre-processing</h2>
<p>Looking at the wind chart, with the double line, it is a bit difficult to make sense of it, there's just too much data. So let's see what we can do about it. We have one measurement every ten minutes for 24 hours, so in total, we have 144 measurements. Plotting 144 data points for the average wind speed and for the maximum wind speed on the same chart makes it a bit crowded. But maybe we don't need all those data points. Let's have a look at how we can turn a data set with 144 points into one with only 24, as we want one measurement every hour for the wind.</p>
<p>One way to do it could be to only pick every 6th measurement, and just ignore the rest. But this new dataset could end up showing a different trend than the initial one and is not a good downsampling representation of the original data set. We need to do this in such a way that for every 6 data points in the original data set, we have only one data point, and this new one is an accurate representation or summarization of the original 6. This process is called downsampling.</p>
<p>So let's create a function that takes the original data set and returns the downsampled one:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">downsample</span>(<span class="hljs-params">originalArray</span>: [<span class="hljs-type">WeatherDataPoint</span>]) -> [<span class="hljs-type">WeatherDataPoint</span>] {
  <span class="hljs-keyword">var</span> downsampledArray: [<span class="hljs-type">WeatherDataPoint</span>] <span class="hljs-operator">=</span> []
  <span class="hljs-comment">// .. We'll add some stuff here</span>
  <span class="hljs-keyword">return</span> downsampledArray
}
</code></pre>
<p>We'll use Swift's <code>stride</code> function because it's perfect for this use case. The <code>stride</code> function takes a <code>start</code>, an <code>end</code> and a <code>stride</code>, and returns a sequence from <code>start</code> to, but not including, <code>end</code>, stepping by <code>stride</code>. So for example, <code>stride(from: 0, to: 10, by: 2)</code> will be <code>[0, 2, 4, 6, 8]</code>, and <code>stride(from: 0, to: 10, by: 3)</code> will be <code>[0, 3, 6, 9]</code>. We want to create slices of the original data source, of size 6.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">stride</span>(from: <span class="hljs-number">0</span>, to: downsampledArray.count, by: <span class="hljs-number">6</span>) {
      <span class="hljs-keyword">let</span> endIndex <span class="hljs-operator">=</span> <span class="hljs-built_in">min</span>(i <span class="hljs-operator">+</span> <span class="hljs-number">6</span>, downsampledArray.count)
      <span class="hljs-keyword">let</span> slice <span class="hljs-operator">=</span> <span class="hljs-type">Array</span>(<span class="hljs-keyword">self</span>[i<span class="hljs-operator">..&#x3C;</span>endIndex])
      <span class="hljs-keyword">let</span> aggregatedValue: <span class="hljs-type">WeatherDataPoint</span>
      <span class="hljs-comment">// .. Figure out how to calculate the aggregated value</span>
      downsampledArray.append(aggregatedValue)
  }
</code></pre>
<p>But what is the aggregated value? That's interesting, right? Because it depends. Our data set measures wind average speed, and wind maximum speed. What's a good aggregated value for 6 average speed data points? Well, it's the average of those. What about the maximum speed? It's the maximum of those. Because the average wind speed in an hour is the average of the wind speed in the first 10 minutes, the one in the following 10 minutes, and so on (6 times). And similarly for the maximum speed.</p>
<p>So let's pass the aggregation function as a parameter, and redefine our method like this:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">downsample</span>(<span class="hljs-params">originalArray</span>: [<span class="hljs-type">WeatherDataPoint</span>], <span class="hljs-params">aggregationFunction</span>: ([<span class="hljs-type">WeatherDataPoint</span>]) -> <span class="hljs-type">WeatherDataPoint</span>) -> [<span class="hljs-type">WeatherDataPoint</span>] {
    <span class="hljs-keyword">var</span> downsampledArray: [<span class="hljs-type">WeatherDataPoint</span>] <span class="hljs-operator">=</span> []

    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">stride</span>(from: <span class="hljs-number">0</span>, to: originalArray.count, by: <span class="hljs-number">6</span>) {
        <span class="hljs-keyword">let</span> endIndex <span class="hljs-operator">=</span> <span class="hljs-built_in">min</span>(i <span class="hljs-operator">+</span> <span class="hljs-number">6</span>, originalArray.count)
        <span class="hljs-keyword">let</span> slice <span class="hljs-operator">=</span> <span class="hljs-type">Array</span>(originalArray[i<span class="hljs-operator">..&#x3C;</span>endIndex])
        <span class="hljs-keyword">let</span> aggregatedValue <span class="hljs-operator">=</span> aggregationFunction(slice)
        downsampledArray.append(aggregatedValue)
    }
    <span class="hljs-keyword">return</span> downsampledArray
}
</code></pre>
<p>To make it even nicer, we can make this method an extension on Array and pass the <code>step</code> (the length of the slice) as a parameter too:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">extension</span> <span class="hljs-title class_">Array</span> <span class="hljs-title class_">where</span> <span class="hljs-title class_">Element</span> == <span class="hljs-title class_">WeatherDataPoint</span> {
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">downsample</span>(<span class="hljs-params">step</span>: <span class="hljs-type">Int</span>, <span class="hljs-params">aggregationFunction</span>: ([<span class="hljs-type">WeatherDataPoint</span>]) -> <span class="hljs-type">WeatherDataPoint</span>) -> [<span class="hljs-type">WeatherDataPoint</span>] {
        <span class="hljs-keyword">var</span> downsampledArray: [<span class="hljs-type">WeatherDataPoint</span>] <span class="hljs-operator">=</span> []

        <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">stride</span>(from: <span class="hljs-number">0</span>, to: <span class="hljs-keyword">self</span>.count, by: step) {
            <span class="hljs-keyword">let</span> endIndex <span class="hljs-operator">=</span> <span class="hljs-type">Swift</span>.min(i <span class="hljs-operator">+</span> step, <span class="hljs-keyword">self</span>.count)
            <span class="hljs-keyword">let</span> slice <span class="hljs-operator">=</span> <span class="hljs-type">Array</span>(<span class="hljs-keyword">self</span>[i<span class="hljs-operator">..&#x3C;</span>endIndex])
            <span class="hljs-keyword">let</span> aggregatedValue <span class="hljs-operator">=</span> aggregationFunction(slice)
            downsampledArray.append(aggregatedValue)
        }

        <span class="hljs-keyword">return</span> downsampledArray
    }
}
</code></pre>
<p>We can now use this method to downsample the wind data series, so we add to the <code>StationMeasurements</code> the following:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">var</span> downsampledWindSeries: [<span class="hljs-type">WeatherDataPoint</span>] {
  <span class="hljs-keyword">return</span> windSeries.downsample(step: <span class="hljs-number">6</span>) { slice <span class="hljs-keyword">in</span>
    <span class="hljs-keyword">let</span> count <span class="hljs-operator">=</span> slice.count
    <span class="hljs-keyword">let</span> values <span class="hljs-operator">=</span> slice.map { <span class="hljs-variable">$0</span>.measuredValue }
    <span class="hljs-keyword">let</span> secondaryValues <span class="hljs-operator">=</span> slice.compactMap { <span class="hljs-variable">$0</span>.secondaryMeasuredValue }
    <span class="hljs-keyword">return</span> <span class="hljs-type">WeatherDataPoint</span>(dateTime: slice.safelyAccessElement(at: count<span class="hljs-operator">/</span><span class="hljs-number">2</span>)<span class="hljs-operator">?</span>.dateTime <span class="hljs-operator">??</span> slice[<span class="hljs-number">0</span>].dateTime,
                            measuredValue: values.reduce(<span class="hljs-number">0</span>, <span class="hljs-operator">+</span>) <span class="hljs-operator">/</span> <span class="hljs-type">Double</span>(count),
                            secondaryMeasuredValue: secondaryValues.max())
        }
    }
</code></pre>
<p>Where <code>safelyAccessElement</code> is a helper method which gives us the element at one index, or nil if the index is out of bounds.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">private</span> <span class="hljs-keyword">extension</span> <span class="hljs-title class_">Array</span> {

    <span class="hljs-keyword">func</span> <span class="hljs-title function_">safelyAccessElement</span>(<span class="hljs-params">at</span> <span class="hljs-params">index</span>: <span class="hljs-type">Int</span>) -> <span class="hljs-type">Element</span>? {
        <span class="hljs-keyword">guard</span> indices.contains(index) <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
        }

        <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>[index]
    }

}
</code></pre>
<p>Now we have managed to turn our data set from 144 points to 24 points. We can use this one to draw the wind chart instead, and the chart immediately looks better.</p>
<p><img src="/assets/img/articles/2023-10-23-Getting-started-with-Swift-Charts/6.webp" alt=""></p>
<h2>Interactive charts</h2>
<p>Now that the charts look better, let's make them even more useful: let's show the value for a precise measurement when the user taps on a point in the chart.</p>
<p>We want to detect when the user taps on the chart, show an indicator of the point they tapped, and show the value at that point in a label above the chart. So we add a <code>.chartOverlay</code> which detects the taps and drags to our chart like this:</p>
<pre><code class="hljs language-swift">.chartOverlay { proxy <span class="hljs-keyword">in</span>
    <span class="hljs-type">GeometryReader</span> { geo <span class="hljs-keyword">in</span>
        <span class="hljs-type">ZStack</span>(alignment: .topLeading) {
            <span class="hljs-type">Rectangle</span>().fill(.clear).contentShape(<span class="hljs-type">Rectangle</span>())
                .gesture(
                    <span class="hljs-type">DragGesture</span>(minimumDistance: <span class="hljs-number">0.0</span>)
                        .onChanged { value <span class="hljs-keyword">in</span>
                            <span class="hljs-keyword">switch</span> chartType {
                            <span class="hljs-keyword">case</span> .temperature(<span class="hljs-keyword">let</span> data), .precipitation(<span class="hljs-keyword">let</span> data), .wind(<span class="hljs-keyword">let</span> data):
                                selectedElement <span class="hljs-operator">=</span> findElement(in: data, location: value.location, proxy: proxy, geometry: geo)
                            }
                        }
                        .onEnded { <span class="hljs-keyword">_</span> <span class="hljs-keyword">in</span>
                            selectedElement <span class="hljs-operator">=</span> <span class="hljs-literal">nil</span>
                        }

                )
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> selectedElement {
                <span class="hljs-type">LollipopView</span>(chartType: chartType, selectedElement: selectedElement, chartProxy: proxy, geometryProxy: geo)
            }
        }
    }
}
</code></pre>
<p>We want to keep the currently selected element in the <code>selectedElement</code> <code>@State</code> property, which can be nil if no element is currently selected (if the user isn't currently tapping or dragging their finger on the chart). If the user taps on the chart and an element is selected, we show an indicator of that (vertical line from the X axis to the chart point where the user tapped, and a rounded circle at its top; we call this <code>LollipopView</code>)</p>
<p>For finding the element on which the user tapped, we have to translate the touch point from screen coordinates to chart coordinates and find the closest data point to that. Here is the <code>findElement</code> method:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">findElement</span>(<span class="hljs-params">in</span> <span class="hljs-params">data</span>: [<span class="hljs-type">WeatherDataPoint</span>], <span class="hljs-params">location</span>: <span class="hljs-type">CGPoint</span>, <span class="hljs-params">proxy</span>: <span class="hljs-type">ChartProxy</span>, <span class="hljs-params">geometry</span>: <span class="hljs-type">GeometryProxy</span>) -> <span class="hljs-type">WeatherDataPoint</span>? {
    <span class="hljs-keyword">let</span> relativeXPosition <span class="hljs-operator">=</span> location.x <span class="hljs-operator">-</span> geometry[proxy.plotAreaFrame].origin.x
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> date <span class="hljs-operator">=</span> proxy.value(atX: relativeXPosition) <span class="hljs-keyword">as</span> <span class="hljs-type">Date</span>? {

        <span class="hljs-comment">// Find the closest element.</span>
        <span class="hljs-keyword">var</span> minDistance: <span class="hljs-type">TimeInterval</span> <span class="hljs-operator">=</span> .infinity
        <span class="hljs-keyword">var</span> index: <span class="hljs-type">Int</span>?
        <span class="hljs-keyword">for</span> dataIndex <span class="hljs-keyword">in</span> data.indices {
            <span class="hljs-keyword">let</span> nthDataDistance <span class="hljs-operator">=</span> data[dataIndex].dateTime.distance(to: date)
            <span class="hljs-keyword">if</span> <span class="hljs-built_in">abs</span>(nthDataDistance) <span class="hljs-operator">&#x3C;</span> minDistance {
                minDistance <span class="hljs-operator">=</span> <span class="hljs-built_in">abs</span>(nthDataDistance)
                index <span class="hljs-operator">=</span> dataIndex
            }
        }
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> index {
            <span class="hljs-keyword">return</span> data[index]
        }
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}
</code></pre>
<p>When we have a selected element, we want to show a visual indicator of the tapped point, so here's our LollipopView:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">LollipopView</span>: <span class="hljs-title class_">View</span> {
    <span class="hljs-keyword">let</span> chartType: <span class="hljs-type">ChartType</span>
    <span class="hljs-keyword">let</span> selectedElement: <span class="hljs-type">WeatherDataPoint</span>
    <span class="hljs-keyword">let</span> chartProxy: <span class="hljs-type">ChartProxy</span>
    <span class="hljs-keyword">let</span> geometryProxy: <span class="hljs-type">GeometryProxy</span>

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {

        <span class="hljs-keyword">let</span> dateInterval <span class="hljs-operator">=</span> <span class="hljs-type">Calendar</span>.current.dateInterval(of: .minute, for: selectedElement.dateTime)<span class="hljs-operator">!</span>
        <span class="hljs-keyword">let</span> startPositionX1 <span class="hljs-operator">=</span> chartProxy.position(forX: dateInterval.start) <span class="hljs-operator">??</span> <span class="hljs-number">0</span>

        <span class="hljs-keyword">let</span> lineX <span class="hljs-operator">=</span> startPositionX1 <span class="hljs-operator">+</span> geometryProxy[chartProxy.plotAreaFrame].origin.x
        <span class="hljs-keyword">let</span> chartAreaHeight <span class="hljs-operator">=</span> geometryProxy[chartProxy.plotAreaFrame].maxY
        <span class="hljs-keyword">let</span> plotLineY <span class="hljs-operator">=</span> chartAreaHeight <span class="hljs-operator">-</span> (chartProxy.position(forY: selectedElement.secondaryMeasuredValue <span class="hljs-operator">??</span> selectedElement.measuredValue) <span class="hljs-operator">??</span> <span class="hljs-number">0</span>)

        <span class="hljs-comment">// The vertical line</span>
        <span class="hljs-type">Rectangle</span>()
            .fill(.gray)
            .frame(width: <span class="hljs-number">2</span>, height: plotLineY)
            .position(x: lineX, y: chartAreaHeight <span class="hljs-operator">-</span> plotLineY <span class="hljs-operator">/</span> <span class="hljs-number">2</span>)

        <span class="hljs-comment">// The symbol shown at the point we've tapped on the graph</span>
        <span class="hljs-type">Circle</span>()
            .foregroundColor(.blue)
            .frame(width: <span class="hljs-number">16</span>, height: <span class="hljs-number">16</span>)
            .overlay(
                <span class="hljs-type">Circle</span>()
                    .stroke(<span class="hljs-type">Color</span>.white, lineWidth: <span class="hljs-number">4</span>)
            )
            .position(x: lineX, y: chartProxy.position(forY: selectedElement.measuredValue) <span class="hljs-operator">??</span> <span class="hljs-number">0</span>)

        <span class="hljs-comment">// If we're on the wind data chart, show the diamond on the gust line too</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">case</span> .wind <span class="hljs-operator">=</span> chartType {
            <span class="hljs-type">Rectangle</span>()
                .rotation(<span class="hljs-type">Angle</span>(degrees: <span class="hljs-number">45</span>))
                .aspectRatio(<span class="hljs-number">1.0</span>, contentMode: .fit)
                .foregroundColor(.red)
                .frame(width: <span class="hljs-number">13</span>, height: <span class="hljs-number">13</span>)
                .overlay(
                    <span class="hljs-type">Rectangle</span>()
                        .rotation(<span class="hljs-type">Angle</span>(degrees: <span class="hljs-number">45</span>))
                        .stroke(.white, lineWidth: <span class="hljs-number">4</span>)
                        .aspectRatio(<span class="hljs-number">1.0</span>, contentMode: .fit)
                )
                .position(x: lineX, y: chartProxy.position(forY: selectedElement.secondaryMeasuredValue <span class="hljs-operator">??</span> <span class="hljs-number">0</span>) <span class="hljs-operator">??</span> <span class="hljs-number">0</span>)
        }
    }
}
</code></pre>
<p>On top of what we mentioned, for the wind chart which has 2 line charts, we want to show different symbols on each line: a circle on the average speed and a diamond on the gust. That's why we added the last block of code in the LollipopView.</p>
<p>Printing the latest measurement or the currently selected measurements above the chart should be pretty straightforward, as long as you make sure you format the data correctly.</p>
<p>And here's the LollipopView in action. We just made our charts interactive!</p>
<p><img src="/assets/img/articles/2023-10-23-Getting-started-with-Swift-Charts/7.webp" alt=""></p>
<center><div class="html5-video-container">
        <video width="400" controls>
          <source src="/assets/img/articles/2023-10-23-Getting-started-with-Swift-Charts/8.mov" type="video/mp4">
        Your browser does not support HTML5 video.
        </video>
      </div></center>
<p>Resources:</p>
<ol>
<li><a href="https://github.com/jordibruin/Swift-Charts-Examples">Swift-Charts-Examples</a></li>
</ol>
<p><em>Article photo generated with <a href="https://www.midjourney.com/">Midjourney</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Getting started with Swift Charts - part 1]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/10/23/Getting-started-with-Swift-Charts</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/10/23/Getting-started-with-Swift-Charts</guid>
            <pubDate>Mon, 23 Oct 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Let's have a look at <a href="https://developer.apple.com/documentation/Charts">Swift Charts</a> and see how we can use it to plot weather data.</p>
<p>Apple introduced Swift Charts in iOS 16, and improved it in iOS 17, adding, among others, new chart types. If your app still needs to support iOS 15 or lower versions, you can use a 3rd party library, like <a href="https://github.com/danielgindi/Charts">Daniel Gindi's Charts</a>. In this demo project, we'll focus on the initial release of Swift Charts and well target iOS 16.</p>
<h2>Data</h2>
<p>First, let's find some data to plot. We'll be looking at weather data, and, for the sake of simplicity, we'll just load locally some JSON from <a href="/assets/img/articles/2023-10-23-Getting-started-with-Swift-Charts/StationData.json">this file</a>. The JSON looks like this:</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Københavns Lufthavn"</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"latitude"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">55.6139</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"longitude"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">12.6455</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"temperatureSeries"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">{</span>
        <span class="hljs-attr">"dateTime"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2023-09-21T13:10:00Z"</span><span class="hljs-punctuation">,</span>
        <span class="hljs-attr">"measuredValue"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">21.9</span>
    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span> ...
    <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"precipitationSeries"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">{</span>
        <span class="hljs-attr">"dateTime"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2023-09-21T13:00:00Z"</span><span class="hljs-punctuation">,</span>
        <span class="hljs-attr">"measuredValue"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1.4</span>
    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span> ...
    <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"windSeries"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">{</span>
        <span class="hljs-attr">"dateTime"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2023-09-21T13:10:00Z"</span><span class="hljs-punctuation">,</span>
        <span class="hljs-attr">"measuredValue"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">6.2</span><span class="hljs-punctuation">,</span>
        <span class="hljs-attr">"secondaryMeasuredValue"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">8.2</span>
    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
    <span class="hljs-punctuation">]</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<p>We have the details of a station, with its name, and position, and then we have 3 series of data, one for temperature (measured in Celsius degrees), one for precipitation (measured in millimetres) and one for wind, which contains the value of the average wind and the value of the gust (max wind speed) for that particular time. All entries in the data series have a <code>dateTime</code>. For temperature and wind, we have 144 data points, corresponding to one measurement every 10 minutes, and for precipitation we have 24 data points, corresponding to a measurement every hour.</p>
<p>Let's start a new Xcode project, using SwiftUI, and let's add the JSON file to the project and the target.</p>
<p>We want to parse this data, so we're creating our model. The purpose of this article is to play with Swift Charts; there are other, possibly better-suited ways of modelling the data, but for now, let's create a WeatherDataPoint struct.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">WeatherDataPoint</span>: <span class="hljs-title class_">Codable</span>, <span class="hljs-title class_">Identifiable</span> {
    <span class="hljs-keyword">let</span> dateTime: <span class="hljs-type">Date</span>
    <span class="hljs-keyword">let</span> measuredValue: <span class="hljs-type">Double</span>
    <span class="hljs-keyword">let</span> secondaryMeasuredValue: <span class="hljs-type">Double</span>?

    <span class="hljs-keyword">var</span> id: <span class="hljs-type">Date</span> {
        dateTime
    }
}
</code></pre>
<p>And a StationMeasurements struct:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">StationMeasurements</span>: <span class="hljs-title class_">Codable</span> {
    <span class="hljs-keyword">let</span> name: <span class="hljs-type">String</span>
    <span class="hljs-keyword">let</span> latitude: <span class="hljs-type">Double</span>
    <span class="hljs-keyword">let</span> longitude: <span class="hljs-type">Double</span>
    <span class="hljs-keyword">let</span> temperatureSeries: [<span class="hljs-type">WeatherDataPoint</span>]
    <span class="hljs-keyword">let</span> precipitationSeries: [<span class="hljs-type">WeatherDataPoint</span>]
    <span class="hljs-keyword">let</span> windSeries: [<span class="hljs-type">WeatherDataPoint</span>]
}
</code></pre>
<p>And let's parse the data from the JSON file into a mock:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">extension</span> <span class="hljs-title class_">StationMeasurements</span> {
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> mock: <span class="hljs-type">StationMeasurements</span> {
        <span class="hljs-keyword">let</span> url <span class="hljs-operator">=</span> <span class="hljs-type">Bundle</span>.main.url(forResource: <span class="hljs-string">"StationData"</span>, withExtension: <span class="hljs-string">"json"</span>)<span class="hljs-operator">!</span>
        <span class="hljs-keyword">let</span> data <span class="hljs-operator">=</span> <span class="hljs-keyword">try!</span> <span class="hljs-type">Data</span>(contentsOf: url)
        <span class="hljs-keyword">let</span> decoder <span class="hljs-operator">=</span> <span class="hljs-type">JSONDecoder</span>()
        decoder.dateDecodingStrategy <span class="hljs-operator">=</span> .iso8601
        <span class="hljs-keyword">let</span> jsonData <span class="hljs-operator">=</span> <span class="hljs-keyword">try!</span> decoder.decode(<span class="hljs-type">StationMeasurements</span>.<span class="hljs-keyword">self</span>, from: data)
        <span class="hljs-keyword">return</span> jsonData
    }
}
</code></pre>
<p><img src="/assets/img/articles/2023-10-23-Getting-started-with-Swift-Charts/1.webp" alt=""></p>
<h2>Basic Charts</h2>
<p>We now have the data in our project, we can go ahead and start playing with it.
We want to have three types of charts, a line chart to show the temperature, a bar chart to show precipitations, and another one with two lines on the same chart, so we can see the average and maximum wind on the same graph.</p>
<p>Let's describe that in code, by making an enum with a case for each chart type, and the data as an associated value.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">enum</span> <span class="hljs-title class_">ChartType</span> {
    <span class="hljs-keyword">case</span> temperature(temperatureData: [<span class="hljs-type">WeatherDataPoint</span>])
    <span class="hljs-keyword">case</span> precipitation(precipitationData: [<span class="hljs-type">WeatherDataPoint</span>])
    <span class="hljs-keyword">case</span> wind(windData: [<span class="hljs-type">WeatherDataPoint</span>])
}
</code></pre>
<p>Let's start by creating the temperature chart. We'll make a new SwiftUI View, called ChartView. It will have a <code>ChartType</code>, which we'll also use to pass the data in through the associated value of the enum cases. The temperature chart will be a line chart, so inside the <code>Chart</code> we'll create a <code>LineMark</code>. We can then configure the line style, color and interpolation method, to make it prettier.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">ChartView</span>: <span class="hljs-title class_">View</span> {
    <span class="hljs-keyword">let</span> chartType: <span class="hljs-type">ChartType</span>

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
        <span class="hljs-keyword">switch</span> chartType {
        <span class="hljs-keyword">case</span> .temperature(<span class="hljs-keyword">let</span> temperatureData):
            <span class="hljs-type">Chart</span> {
                <span class="hljs-type">ForEach</span>(temperatureData) { element <span class="hljs-keyword">in</span>
                    <span class="hljs-type">LineMark</span>(
                        x: .value(<span class="hljs-string">"dateTime"</span>, element.dateTime),
                        y: .value(<span class="hljs-string">"temperature"</span>, element.measuredValue)
                    )
                    .lineStyle(<span class="hljs-type">StrokeStyle</span>(lineWidth: <span class="hljs-number">4</span>))
                    .foregroundStyle(<span class="hljs-type">Color</span>.blue)
                    .interpolationMethod(.cardinal)
                }
            }
        <span class="hljs-keyword">case</span> .precipitation(<span class="hljs-keyword">let</span> precipitationData):
            <span class="hljs-type">EmptyView</span>()
        <span class="hljs-keyword">case</span> .wind(<span class="hljs-keyword">let</span> windData):
            <span class="hljs-type">EmptyView</span>()
        }

    }
}
</code></pre>
<p>For the other two chart types, we can return <code>EmptyView()</code> for now. Here is the line chart showing the temperature data:</p>
<p><img src="/assets/img/articles/2023-10-23-Getting-started-with-Swift-Charts/2.webp" alt=""></p>
<p>Let's continue and, similarly, create the bar chart. This time, we have to use the <code>BarMark</code>, but mostly everything else remains the same; we can remove the <code>interpolationMethod</code>, which doesn't make sense for a bar chart, and just change the identifiers.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">case</span> .precipitation(<span class="hljs-keyword">let</span> precipitationData):
    <span class="hljs-type">Chart</span> {
        <span class="hljs-type">ForEach</span>(precipitationData) { element <span class="hljs-keyword">in</span>
            <span class="hljs-type">BarMark</span>(
                x: .value(<span class="hljs-string">"dateTime"</span>, element.dateTime),
                y: .value(<span class="hljs-string">"precipitation"</span>, element.measuredValue)
             )
              .lineStyle(<span class="hljs-type">StrokeStyle</span>(lineWidth: <span class="hljs-number">4</span>))
              .foregroundStyle(<span class="hljs-type">Color</span>.blue)
         }
    }
</code></pre>
<p>The wind chart will be a bit more interesting, it will consist of two line charts. So on the same <code>Chart</code>, we will have two <code>LineMark</code>s. The main difference from the temperature chart is that, for each LineMark, we will have to specify the <code>series</code> argument as well, so they will be plotted as individual series. We want the max wind speed to be plotted as a dashed line, so we also specify the <code>dash</code> parameter in the lineStyle.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">case</span> .wind(<span class="hljs-keyword">let</span> windData):
    <span class="hljs-type">Chart</span> {
        <span class="hljs-type">ForEach</span>(windData) { element <span class="hljs-keyword">in</span>
            <span class="hljs-type">LineMark</span>(
                x: .value(<span class="hljs-string">"dateTime"</span>, element.dateTime),
                y: .value(<span class="hljs-string">"average wind"</span>, element.measuredValue),
                series: .value(<span class="hljs-string">"average"</span>, <span class="hljs-string">"average wind"</span>)
            )
            .lineStyle(<span class="hljs-type">StrokeStyle</span>(lineWidth: <span class="hljs-number">4</span>))
            .foregroundStyle(<span class="hljs-type">Color</span>.blue)
            .interpolationMethod(.cardinal)

            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> secondaryMeasuredValue <span class="hljs-operator">=</span> element.secondaryMeasuredValue {
                <span class="hljs-type">LineMark</span>(
                    x: .value(<span class="hljs-string">"dateTime"</span>, element.dateTime),
                    y: .value(<span class="hljs-string">"max wind"</span>, secondaryMeasuredValue),
                    series: .value(<span class="hljs-string">"gust"</span>, <span class="hljs-string">"gust"</span>)
                )
                .lineStyle(<span class="hljs-type">StrokeStyle</span>(lineWidth: <span class="hljs-number">4</span>, dash: [<span class="hljs-number">6</span>]))
                .foregroundStyle(<span class="hljs-type">Color</span>.red)
                .interpolationMethod(.cardinal)
            }
        }
    }
</code></pre>
<p><img src="/assets/img/articles/2023-10-23-Getting-started-with-Swift-Charts/3.webp" alt=""></p>
<p>Now, we have the data plotted, but we would like to configure the axes. We want the vertical axis to be to the left of the chart, and we want to be able to change the font of the label on the vertical axes. Adding this code after the <code>Chart {...}</code> does the trick.</p>
<pre><code class="hljs language-swift">.chartYAxis {
    <span class="hljs-type">AxisMarks</span>(position: .leading) {
        <span class="hljs-type">AxisValueLabel</span>()
        <span class="hljs-type">AxisGridLine</span>(stroke: <span class="hljs-type">StrokeStyle</span>(lineWidth: <span class="hljs-number">0.5</span>))
    }
}
</code></pre>
<p>But that only customizes one of the three charts, and we don't like to repeat code.</p>
<p><img src="/assets/img/articles/2023-10-23-Getting-started-with-Swift-Charts/4.webp" alt=""></p>
<p>So what we want now is to extract the Chart content, and use only one <code>Chart</code>, to be able to customize the axes the same way for all types of charts without repeating code.</p>
<p>Let's create the chart contents for our three charts.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">LineChartViewContent</span>: <span class="hljs-title class_">ChartContent</span> {
    <span class="hljs-keyword">let</span> data: [<span class="hljs-type">WeatherDataPoint</span>]

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">ChartContent</span> {
        <span class="hljs-type">ForEach</span>(data) { element <span class="hljs-keyword">in</span>
            <span class="hljs-type">LineMark</span>(
                x: .value(<span class="hljs-string">"dateTime"</span>, element.dateTime),
                y: .value(<span class="hljs-string">"temperature"</span>, element.measuredValue)
            )
            .lineStyle(<span class="hljs-type">StrokeStyle</span>(lineWidth: <span class="hljs-number">4</span>))
            .foregroundStyle(<span class="hljs-type">Color</span>.blue)
            .interpolationMethod(.cardinal)
        }
    }
}

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">BarChartViewContent</span>: <span class="hljs-title class_">ChartContent</span> {
    <span class="hljs-keyword">let</span> data: [<span class="hljs-type">WeatherDataPoint</span>]

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">ChartContent</span> {
        <span class="hljs-type">ForEach</span>(data) { element <span class="hljs-keyword">in</span>
            <span class="hljs-type">BarMark</span>(
                x: .value(<span class="hljs-string">"dateTime"</span>, element.dateTime),
                y: .value(<span class="hljs-string">"precipitation"</span>, element.measuredValue)
            )
            .lineStyle(<span class="hljs-type">StrokeStyle</span>(lineWidth: <span class="hljs-number">4</span>))
            .foregroundStyle(<span class="hljs-type">Color</span>.blue)
        }
    }
}

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">DoubleLineChartViewContent</span>: <span class="hljs-title class_">ChartContent</span> {
    <span class="hljs-keyword">let</span> data: [<span class="hljs-type">WeatherDataPoint</span>]

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">ChartContent</span> {
        <span class="hljs-type">ForEach</span>(data) { element <span class="hljs-keyword">in</span>
            <span class="hljs-type">LineMark</span>(
                x: .value(<span class="hljs-string">"dateTime"</span>, element.dateTime),
                y: .value(<span class="hljs-string">"average wind"</span>, element.measuredValue),
                series: .value(<span class="hljs-string">"average"</span>, <span class="hljs-string">"average wind"</span>)
            )
            .lineStyle(<span class="hljs-type">StrokeStyle</span>(lineWidth: <span class="hljs-number">4</span>))
            .foregroundStyle(<span class="hljs-type">Color</span>.blue)
            .interpolationMethod(.cardinal)

            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> secondaryMeasuredValue <span class="hljs-operator">=</span> element.secondaryMeasuredValue {
                <span class="hljs-type">LineMark</span>(
                    x: .value(<span class="hljs-string">"dateTime"</span>, element.dateTime),
                    y: .value(<span class="hljs-string">"max wind"</span>, secondaryMeasuredValue),
                    series: .value(<span class="hljs-string">"gust"</span>, <span class="hljs-string">"gust"</span>)
                )
                .lineStyle(<span class="hljs-type">StrokeStyle</span>(lineWidth: <span class="hljs-number">4</span>, dash: [<span class="hljs-number">6</span>]))
                .foregroundStyle(<span class="hljs-type">Color</span>.red)
                .interpolationMethod(.cardinal)
            }
        }
    }
}
</code></pre>
<h2>Customizing the axes</h2>
<p>And now we can modify the ChartView to use the newly created ChartContents, and then we can customize the axes once for all charts.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">ChartView</span>: <span class="hljs-title class_">View</span> {
    <span class="hljs-keyword">let</span> chartType: <span class="hljs-type">ChartType</span>

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
        <span class="hljs-type">Chart</span> {
            <span class="hljs-keyword">switch</span> chartType {
            <span class="hljs-keyword">case</span> .temperature(<span class="hljs-keyword">let</span> temperatureData):
                <span class="hljs-type">LineChartViewContent</span>(data: temperatureData)
            <span class="hljs-keyword">case</span> .precipitation(<span class="hljs-keyword">let</span> precipitationData):
                <span class="hljs-type">BarChartViewContent</span>(data: precipitationData)
            <span class="hljs-keyword">case</span> .wind(<span class="hljs-keyword">let</span> windData):
                <span class="hljs-type">DoubleLineChartViewContent</span>(data: windData)
            }
        }
        .chartYAxis {
            <span class="hljs-type">AxisMarks</span>(position: .leading) {
                <span class="hljs-type">AxisValueLabel</span>()
                <span class="hljs-type">AxisGridLine</span>(stroke: <span class="hljs-type">StrokeStyle</span>(lineWidth: <span class="hljs-number">0.5</span>))
            }
        }
    }
}
</code></pre>
<p>We also want to customize the horizontal axes. On this one, we display dates. Since the measurements are for 24 hours, and the space on the axis is quite limited, we're only interested in the time. We print one label every 4 hours and only print the time. We also show a grid line every 4 hours.</p>
<pre><code class="hljs language-swift">.chartXAxis {
    <span class="hljs-type">AxisMarks</span>(values: .stride(by: .hour, count: <span class="hljs-number">4</span>)) { value <span class="hljs-keyword">in</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> date <span class="hljs-operator">=</span> value.as(<span class="hljs-type">Date</span>.<span class="hljs-keyword">self</span>) {
            <span class="hljs-keyword">let</span> hour <span class="hljs-operator">=</span> <span class="hljs-type">Calendar</span>.current.component(.hour, from: date)
            <span class="hljs-type">AxisValueLabel</span> {
                <span class="hljs-type">Text</span>(date, style: .time)
            }
            <span class="hljs-type">AxisGridLine</span>()
        }
    }
}
</code></pre>
<p>So here's how far we've come:
<img src="/assets/img/articles/2023-10-23-Getting-started-with-Swift-Charts/5.webp" alt=""></p>
<p>Where can we go from here? It would be nice if we could tap on the chart to see the exact measurement value, right? And maybe we can do something about that wind chart, which looks a bit crowded. We're looking at that in <a href="/en/post/2023/10/23/Getting-started-with-Swift-Charts-part-2">part 2</a>.</p>
<p><em>Article photo generated with <a href="https://www.midjourney.com/">Midjourney</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Strengthening our AI knowledge on mobile apps]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/09/08/Strengthening-our-AI-knowledge-on-mobile-apps</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/09/08/Strengthening-our-AI-knowledge-on-mobile-apps</guid>
            <pubDate>Fri, 08 Sep 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>In today's digital age, mobile applications have become an integral part of our daily lives. From ordering food to managing finances, there seems to be an app for every need. Behind the scenes, these apps are powered by a technology that has revolutionized the way we interact with our smartphones: artificial intelligence (AI). AI has not only transformed the capabilities of mobile apps but has also opened up a world of possibilities for developers and users alike.</p>
<p>We (the MLJP mobile team members) wanted to catch up with AI and be ready to make better proposals for our clients. Therefore, we created a team to research this technology applied to mobile development and come up with an easy idea to get a nurturing experience.</p>
<h1>What is Artificial Intelligence?</h1>
<p>AI is a field of computer science that aims to create intelligent machines capable of performing tasks that typically require human intelligence. AI algorithms are generally based on models trained using specific data sets.</p>
<p>The goal of AI modelling is to generalize instructed knowledge during the inference phase, using data sets provided during the training phase. Training data sets generally consist of pairs of data points and their corresponding ground truth values, enabling the model to learn the relationship between the two and attempt to reproduce it.</p>
<p>Various types of data, such as images, text, or tabular data, can be used in this regard. The input/output data do not necessarily have to be the same. For instance, you can ask a model to classify an image into a specific category (e.g., whether an image contains a cat).</p>
<p>With the project developed in this article, we aim to detect face landmarks in an image. Thus, we feed the model an image and receive tabular data representing the coordinates of these landmarks as an output. The technical details are as follows:</p>
<ul>
<li><strong>Data to be analyzed</strong>: An <em>RGB</em> image.</li>
<li><strong>Model Input data</strong>: A <strong>192 x 192</strong> pixel image, framed and resized around the face.</li>
<li><strong>Model Output data</strong>: <strong>468</strong> face landmarks, each having <strong>X, Y, and Z</strong> coordinates.</li>
<li><strong>Model type</strong>: A deep learning model that uses artificial neural networks based on the TensorFlow Lite framework.</li>
</ul>
<h1>Choose your lipstick</h1>
<p>The app developed is a face makeup, starting with the lipstick color. This application aims to provide users with the ability to change the color of their lips in real-time using advanced pose estimation technology. Here is a basic overview of how this functionality works:</p>
<ul>
<li>
<p><strong>Real-time Lip Detection</strong>: Using a pre-trained pose estimation model from the AI team, we track the position and shape of the user's lips in real-time. The model analyzes the facial landmarks and provides accurate coordinates of the lip contour.</p>
</li>
<li>
<p><strong>Color Palette Selection</strong>: To offer a diverse range of lip colors and shades, we provide users with RGB sliders. Users can interact with the sliders and select any desired lip color.</p>
</li>
<li>
<p><strong>Real-time Color Application</strong>: Once the user selects a lip color from the palette, the application applies the chosen color to the detected lip region in real-time. This is achieved by mapping the color to the specific lip contour coordinates obtained from the AI model.</p>
</li>
</ul>
<p>We picked up this idea, after a long brainstorming, basically because it required low resources (developer’s face and a smartphone frontal camera) and an easy scenario to work with (one existing AI model). In addition, face makeup applications have gained significant popularity in recent years due to the rise of social media platforms and the desire for users to enhance their appearance in a fun and creative way.</p>
<p>In the following sections, we are going to explain the detailed process we followed to achieve the idea proposed and what challenges and issues we found along the way.</p>
<h1>Image Processing Flow Overview</h1>
<p>First of all, to take a video image from a mobile device camera and get some face landmarks, we needed to follow a series of steps. Here's a high-level overview of the main processes involved:</p>
<figure>
<img src="/assets/img/articles/2023-08-31-Strengthening-our-AI-knowledge-on-mobile-apps/image1.webp">
<figcaption></figcaption>
</figure>
<ol>
<li>
<p><strong>Capture the Video</strong>: We use the device’s built-in camera functionality to capture the video. We make sure the video includes the face we want to detect and extract landmarks from whilst being careful to ensure that the face is neither too close nor too far away. We should note that most face detection models also have difficulty matching a face that is looking too far (left, right, up or down), or tilting too far (either side).</p>
</li>
<li>
<p><strong>Extract Frames from the Video</strong>: We convert the video into individual frames, as a video is essentially a sequence of images. We use the native frameworks in both Android and iOS to extract frames from the video, iterate through each frame from the video and perform the subsequent steps on each frame as a static image.</p>
</li>
<li>
<p><strong>Convert Images to TensorFlow Lite Input</strong>: Before using these images in TensorFlow Lite, we need to preprocess them to match the input requirements of our face landmark detection model. This typically involves resizing the images to the model's expected input size and normalizing pixel values. Each model will have its own specific requirements for the images we pass to it, and failing to correctly preprocess our images will either give wildly inaccurate results or be unable to match a face. A more detailed explanation of this step is explained in the following sections (Cropping the Images and Image Processing Techniques).</p>
</li>
<li>
<p><strong>Pass Images to TensorFlow Lite Model</strong>: We load the preprocessed images into our TensorFlow Lite face landmark detection model and run inference on each image. The model will output the predicted landmarks for the face detected in the frame. TensorFlow Lite provides APIs for loading the model and running inferences.</p>
</li>
<li>
<p><strong>Overlay Images on the Video Frames</strong>: Once we have the face landmarks for each frame, we overlay an image or add visual effects using the landmarks as reference points. We use Android and iOS advanced graphic frameworks to do so.</p>
</li>
</ol>
<p>Now that we have a good overview of the steps involved in detecting face landmarks in an image, let’s look at some of the image processing techniques we use to convert our images.</p>
<h1>Cropping the Images</h1>
<p>To improve the accuracy of the face landmark model, we avoid using the entire frame image when we only need the face. Thus, one of the first image-processing tasks we need is to crop the face, so that we are only processing the area we need to send to the model.</p>
<p>It's important to note that face landmark models are typically trained on datasets where the faces are already cropped or centered. Therefore, using cropped face images during inference helps align the input with the data distribution the model was trained on, resulting in better performance.</p>
<p>However, when cropping a face from an image to use in a face landmark model, there are several challenges and issues that need to be addressed. Here are some of the key considerations:</p>
<ul>
<li>
<p><strong>Face Detection Accuracy</strong>: The accuracy of the face landmark model heavily relies on the quality and correctness of the cropped face. If the cropping process is not performed accurately, it can result in incomplete or inaccurate face regions, leading to degraded detection performance. Ensuring precise and comprehensive cropping is crucial to obtain reliable results.</p>
</li>
<li>
<p><strong>Face Localization</strong>: The face landmark model expects the face to be properly centered and aligned. However, variations in pose, scale, rotation, and occlusions in the original image can make face localization challenging. Addressing these factors and implementing robust techniques for face localization is essential to obtain accurate and properly cropped faces.</p>
</li>
<li>
<p><strong>Correct Aspect Ratio</strong>: Maintaining the correct aspect ratio of the face during cropping is important to avoid distortion or stretching. Faces in images can have different orientations and aspect ratios. It's necessary to handle these variations appropriately, ensuring that the cropped face maintains its correct proportions for effective face detection.</p>
</li>
<li>
<p><strong>Background and Context</strong>: Removing the background and irrelevant context from the cropped face will improve the performance of the face landmark model. Eliminating distractions, such as cluttered scenes or irrelevant patterns, that may interfere with the face landmark process, will help improve the model's accuracy.</p>
</li>
<li>
<p><strong>Head Cropping</strong>: Although we talk about face landmarks most models will require the whole head. Just cropping the face area will result in inaccurate results so the crop area should aim to contain the whole head, including the chin, ears and top of the head.</p>
</li>
<li>
<p><strong>Image and Lighting Quality</strong>: The quality of the camera photo image, including factors like resolution, noise, blur, and lighting conditions, can impact the performance of the face detection model. Noise or blur can obscure facial features, while poor lighting conditions may affect the visibility of the face. Properly handling image enhancement techniques, such as de-noising, sharpening, or adjusting brightness/contrast, can help improve the quality of the cropped face for more accurate detection.</p>
</li>
<li>
<p><strong>Occlusions and Partial Faces</strong>: Addressing occlusions, such as hair, glasses, or hands covering parts of the face, is a significant challenge when either trying to crop a face from an image or detect face landmarks. Detecting and accounting for these occlusions is crucial to ensure the cropped face includes the complete facial features required for accurate detection. Advanced techniques like occlusion detection and additional preprocessing steps can assist in handling these situations but it is usually more efficient to simply ensure that the user is aware of the face detection limitations and should avoid covering their face when possible.</p>
</li>
</ul>
<p>With the above in mind, we tried two ways of cropping.</p>
<ol>
<li><strong>Center Cropping</strong>: This is the easiest to implement. We simply crop the largest possible square from the center of the image with its height equal to its width (when the images are in portrait orientation). To prevent the user from accidentally moving their face outside of this area we can overlay guidelines on the video image to indicate to the user the ideal position for their face. This method relies on the user to accurately position their face in the optimal location at the correct distance. If the user has difficulty with this, then we can change to <strong>Center Padding</strong>. This method adds a buffer area to the left and right of the image to turn the rectangle into a square. In this way, the user’s face will never be accidentally cropped but, by increasing the size of the image we will be reducing its accuracy.</li>
</ol>
<figure>
<img src="/assets/img/articles/2023-08-31-Strengthening-our-AI-knowledge-on-mobile-apps/image2.webp">
<figcaption>Center Padding (left) vs Center Cropping (right): Padding will always contain the face but increases the size of the image with unnecessary space. Cropping is more space efficient but has the risk of losing part of face. Devices with very tall and thin screens will also have much more padding.</figcaption>
</figure>
<ol start="2">
<li><strong>Face Detection Cropping</strong>: This uses a face detection model to determine the precise position of the whole face (without individual face landmarks) and will return a bounding rectangle with an origin: (<strong>x, y</strong>) and a size: (<strong>width, height</strong>). This will usually result in a rectangle that will probably need to be padded out on the right and left sides to make a square. We should avoid cropping the top and bottom of the rectangle to get a square, as this will remove the chin, etc, that may confuse the face landmark model. The advantage of this method is that the cropped area will only contain the face area giving the face landmark model the most accurate image possible.</li>
</ol>
<figure>
<img src="/assets/img/articles/2023-08-31-Strengthening-our-AI-knowledge-on-mobile-apps/image3.webp">
<figcaption>Face Detection Cropping: Face detection will only use the face, regardless of size, which significantly increases face landmark detection accuracy but increases CPU computation load considerably.</figcaption>
</figure>
<p>In our project, we decided to use the face detection model. After face cropping, the image is resized smaller for the model. As the images we input to the model are only a few hundred pixels wide, having the face fill as much of the image as possible will give a wider range for the landmark positions to be assigned to. For example, the lips alone can have over 50 landmarks. Another advantage of using a face detection model is that the user is free to move around, and the model will still accurately locate their face. Even when the user is far away from the camera the app will still find and expand the image appropriately. On the other hand, adding another layer of intensive image processing will increase CPU usage resulting in faster battery depletion and an increase in temperature for mobile devices.</p>
<h3>Examples of Center Padding vs Center Cropping vs Face Detection Cropping</h3>
<p>In deciding which cropping method is most suitable for your use case, please look at the below examples. They show exactly how the images will appear depending on how far the person is away from the camera. Note that center cropping can be effective only if the user actively positions themselves within the center cropping area, otherwise the cropped image may be unusable.</p>
<p>The top line shows <strong>Center Padding</strong>, the middle line shows <strong>Center Cropping</strong> and the bottom line shows <strong>Face Detection Cropping</strong>. ✅ is for images suitable for face landmark detection. ⚠️ is for images that may not be usable for face landmark detection (very small or partially cropped faces etc). ❌ is for images that are not usable for face landmark detection (faces that are badly cropped or obscured etc).</p>
<figure>
<img src="/assets/img/articles/2023-08-31-Strengthening-our-AI-knowledge-on-mobile-apps/image4.webp">
<figcaption>Cropping Comparison (Far): The difference in face size is obvious. The face in the Center Padding image is so small compared to the Face Detection image that it may result in the face landmark model not being able to recognize it. The Center Cropped image has accidentally cropped the face rendering it completely unusable by the face landmark model.</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2023-08-31-Strengthening-our-AI-knowledge-on-mobile-apps/image5.webp">
<figcaption>Cropping Comparison (Middle): The difference in face size is not so large. The face in the Center Padding image is about one-fourth the size of the Face Detection image which will result in low-accuracy face landmark results. The Center Cropped image has slightly cropped the head which may render it unusable by the face landmark model or produce incorrect results.</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2023-08-31-Strengthening-our-AI-knowledge-on-mobile-apps/image6.webp">
<figcaption>Cropping Comparison (Close): There is almost no difference in face size. The face in the Center Padding image is the same size as the Face Detection image and will produce accurate results with no additional computational overhead. The Center Cropped image has cropped the top and bottom of the face which will render it unusable by the face landmark model.</figcaption>
</figure>
<h1>Image Processing Techniques</h1>
<p>Even with the face cropped from the image, we will still need to perform some image processing to ensure the image is appropriately prepared for the model to be recognized. Here is a general guide on how to process the cropped image for a TensorFlow Lite face detection model:</p>
<figure>
<img src="/assets/img/articles/2023-08-31-Strengthening-our-AI-knowledge-on-mobile-apps/image7.webp">
<figcaption></figcaption>
</figure>
<ol>
<li>
<p><strong>Resize the Image</strong>: TensorFlow face detection models usually have specific input size requirements, such as a fixed width and height. Resizing the image to these dimensions ensures compatibility with the model. We can use resizing functions provided by the chosen library or process the images with the graphics frameworks provided by our mobile OS. We need to trim the image to the correct shape (usually from a rectangle to a square) and then scale the image down to the correct size.</p>
</li>
<li>
<p><strong>Convert Image to the Required Color Format</strong>: Verify if the model expects the image in a specific color format, such as RGB or BGR. We should convert the image to the appropriate color format if necessary. Model Libraries will usually provide such conversion functions to convert the color format of an image for us. If not, then we would need to find an alternative library or write the necessary color format conversion code ourselves.</p>
</li>
<li>
<p><strong>Normalize Pixel Values</strong>: We normalize the pixel values of the image to a specific range suitable for the model. Commonly used normalization techniques include scaling the pixel values to [<strong>0, 1</strong>]. Normalizing the image is done using mathematical operations or built-in library functions.</p>
</li>
<li>
<p><strong>Convert Image to the Required Data Format</strong>: TensorFlow models typically expect input data in the form of tensors. We convert the preprocessed image to the appropriate data format using functions provided by the library we're using.</p>
</li>
</ol>
<p>We should note that some (if not most) models usually don’t tell you that they have been unable to locate a face, and instead return their default face landmarks. This is when the model gives us a collection of face landmarks, but they usually do not match the user’s face and will not change over time. This can be a cause of confusion and is another good reason to preprocess the video frame image with a face detection model before anything else. This way, it is possible to parse the results and filter out images that have no faces. Also, we can discard faces that are positioned or orientated in ways that we know the face landmark model will be unable to use.</p>
<h1>Face Landmark Position Conversion</h1>
<p>After successfully obtaining the predicted face landmarks from our model, we now have to take the landmark coordinate values that we need, in this case, the positions of the lips, and use these to draw an image on the detected face. To draw the lips on top of the original face, we did the following steps:</p>
<figure>
<img src="/assets/img/articles/2023-08-31-Strengthening-our-AI-knowledge-on-mobile-apps/image8.webp">
<figcaption></figcaption>
</figure>
<ol>
<li>
<p><strong>Obtain Face Landmarks</strong>: After running the face landmark detection model with TensorFlow Lite, we obtain the predicted face landmarks. These landmarks represent specific facial features, such as the corners of the eyes, nose, and lips.</p>
</li>
<li>
<p><strong>Identify Lip Landmark Points</strong>: Determine the specific landmark points that correspond to the lips. The exact landmarks can vary depending on the face landmark model we use, but typically, they include the points representing the outer edges of the lips, such as the corners or key points along the lip contours. Note that both the upper lip and lower lip are separate, so we have edge and corner points for both the top and bottom of the upper and lower lips.</p>
</li>
<li>
<p><strong>Extract Lip Landmark Coordinates</strong>: Retrieve the coordinates of the lip landmark points from the face landmarks output. These coordinates typically consist of an array of (<strong>x, y</strong>) pairs representing the position of each lip landmark point on the image. Some models can predict 3D landmarks as a (<strong>x, y, z</strong>) coordinate but in this case, we are only overlaying a simple image onto a video so we will ignore the depth value (z).</p>
</li>
<li>
<p><strong>Convert the Landmark Coordinates to Screen Coordinates</strong>: After obtaining the results of a TensorFlow Lite face landmark detection model, the coordinates for each landmark still need to be reverted to the correct size and scale of the original image. The reason for this is that if we pass an image to the model that is, for example, <strong>192 x 192</strong> then all of our landmark coordinates are also going to be from <strong>1 to 192</strong>. The model has no knowledge of the size of the video frame image or screen size, so we must revert these values to the correct size with the following steps:</p>
</li>
</ol>
<ul>
<li>Retrieve the width and height of the original image. These dimensions represent the size of the image before any resizing or preprocessing steps are applied.</li>
<li>Compute the scaling factors for both the width and height by dividing the dimensions of the output from the TensorFlow Lite model by the dimensions of the original image. This step helps determine how much the coordinates need to be scaled to match the original image's size.</li>
<li>Multiply each coordinate (<strong>x, y</strong>) obtained from the TensorFlow Lite model by the corresponding scaling factor calculated in the previous step. This scaling operation reverts the coordinates to the correct size and scale of the original image.</li>
</ul>
<ol start="5">
<li><strong>Overlay Lips on the Original Video Image</strong>: Once we have the lip landmark coordinates, we draw and overlay the lips on the image with the following steps:</li>
</ol>
<ul>
<li>Create a new empty image view to use for drawing the lips.</li>
<li>Iterate over the lip landmark coordinates and connect them to form the lip contours for the top and bottom lips. We use standard line <strong>draw</strong> functions to draw the outlines of the lips on our new image.</li>
<li>Fill the lips using the contour points. We use standard <strong>shape-fill</strong> functions to fill the area enclosed by the lip contours.</li>
<li>Adjust the color and opacity of the drawn lips to achieve the desired visual effect.</li>
<li>Once we have drawn the lips on the image view, we overlay it over the video as necessary.</li>
</ul>
<p>Although specific implementation details will vary based on the library you are using and the format of the face landmarks provided by your TensorFlow Lite model by following these general steps, you can utilize the face landmarks obtained from TensorFlow Lite to draw lips on the original image.</p>
<h1>Technological background: Android</h1>
<p>This is the development explanation of our Android app.</p>
<figure>
<img src="/assets/img/articles/2023-08-31-Strengthening-our-AI-knowledge-on-mobile-apps/image9.webp">
<figcaption>Screenshots of the Android app</figcaption>
</figure>
<h3>Implementation</h3>
<figure>
<img src="/assets/img/articles/2023-08-31-Strengthening-our-AI-knowledge-on-mobile-apps/image10.webp">
<figcaption>Android app implementation modules</figcaption>
</figure>
<ul>
<li>
<p><strong>LiveCameraView</strong>: This composable function is responsible for displaying the live camera preview and UI components for adjusting the lip's color using RGB sliders. It uses Jetpack Compose, a modern UI toolkit, to build the UI. It receives data from the LiveCameraViewModel and updates the UI accordingly.</p>
</li>
<li>
<p><strong>LiveCameraViewModel</strong>: This class is responsible for managing the camera preview and applying AR makeup effects. It follows the ViewModel pattern and interacts with the Model to retrieve data and update the UI. It uses the MLKitFaceDetector for face detection and the FaceLandmarkAIInterpreter for face landmark detection.</p>
<ul>
<li><strong>processCameraImage(fullImageBitmap: Bitmap)</strong>: Processes the camera image by detecting faces, extracting the face image, performing face landmark detection, and applying AR makeup effects. It takes the full camera image as a parameter.</li>
<li><strong>arProcessedOutputBitmap: StateFlow&#x3C;Bitmap?></strong>: Represents the AR processed output bitmap, which can be observed by the UI.</li>
</ul>
</li>
<li>
<p><strong>MLKitFaceDetector</strong>: This package contains functions related to face detection using the ML Kit Face Detection API.</p>
<ul>
<li><strong>detectFace(fullImageBitmap: Bitmap, faces: (result: List<face>) -> Unit)</face></strong>: Detects faces in the given <strong>fullImageBitmap</strong> using the ML Kit Face Detection API. It takes the full image bitmap and a callback function <strong>faces</strong> that will be invoked with the list of detected <strong>Face</strong> objects.</li>
<li><strong>cropBitmapFromFace(fullImageBitmap: Bitmap, face: Face): Bitmap</strong>: Crops the <strong>fullImageBitmap</strong> based on the bounding box of the provided <strong>face</strong> object. It returns a new bitmap that contains the cropped face image.</li>
<li><strong>mergeBitmaps(fullImageBitmap: Bitmap, croppedFaceImageBitmap: Bitmap, face: Face)</strong>: <strong>Bitmap</strong>: Merges the <strong>croppedFaceImageBitmap</strong> into the <strong>fullImageBitmap</strong> at the bounding box of the provided <strong>face</strong></li>
</ul>
</li>
<li>
<p><strong>FaceLandmarkAIInterpreter</strong>: This class provides the functionality to process face images and detect facial landmarks using an AI model. It encapsulates the logic for interpreting the AI model's output and extracting the facial landmark positions.
Load the FaceLandmark AI model by specifying the model file (<strong>face_landmark_model.tflite</strong>):</p>
<pre><code class="hljs language-kotlin">faceLandmarkInterpreter.loadModel(<span class="hljs-string">"face_landmark_model.tflite"</span>)
</code></pre>
<p>An Interpreter object is created to handle model inference. The input tensor's shape is retrieved, and a Bitmap with dimensions 192x192 pixels is created to match the input size required by the model. The output tensor's shape is retrieved, and a TensorBuffer is created to store the model's output predictions.
Make sure the model file is placed in the assets directory of your Android project.</p>
<ul>
<li><strong>processFaceImage(faceImage: Bitmap): List<pointf></pointf></strong>: Processes the face image and returns a list of facial landmark positions. It takes the face image as a parameter and returns a list of PointF objects representing the positions of the facial landmarks. Process an input image using the loaded model:</li>
</ul>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">val</span> inputBitmap: Bitmap = <span class="hljs-comment">// Load or create a 192x192 size bitmap</span>
<span class="hljs-keyword">val</span> landmarks: List&#x3C;PointF> = faceLandmarkInterpreter.processImage(inputBitmap)
</code></pre>
</li>
<li>
<p><strong>FaceLandmarkARProcessor</strong>: This package contains functions related to processing face landmarks and applying AR makeup effects to the lips in the input bitmap image.</p>
<ul>
<li><strong>processImageByAR(bitmap: Bitmap, facePos: List<pointf>, color: Int = Color.RED, radius: Float = 10f, isDrawLandmarks: Boolean = false): Bitmap</pointf></strong>: Processes the input bitmap image by detecting facial landmarks, applying AR makeup effects to the lips, and optionally drawing facial landmarks. It takes the bitmap image, a list of face landmark positions (<strong>facePos</strong>), the desired color for the AR makeup effects (<strong>color</strong>), the radius for drawing facial landmarks (<strong>radius</strong>), and a flag indicating whether to draw facial landmarks (<strong>isDrawLandmarks</strong>). Returns the processed bitmap image.</li>
<li><strong>drawLandmarks(bitmap: Bitmap, landmarks: List<pointf>, radius: Float, isDrawLandmarks: Boolean): Bitmap</pointf></strong>: Draws facial landmarks on the input bitmap image. It takes the bitmap image, a list of facial landmark positions (<strong>landmarks</strong>), the radius for drawing facial landmarks (<strong>radius</strong>), and a flag indicating whether to draw facial landmarks (<strong>isDrawLandmarks</strong>). Returns the bitmap image with facial landmarks drawn.</li>
<li><strong>drawRedColorfulMarks(bitmap: Bitmap, facePos: List<pointf>, color: Int)</pointf></strong>: Applies a red colorful mark effect to the lips in the input bitmap image. It takes the bitmap image, a list of face landmark positions (<strong>facePos</strong>), and the desired color for the effect (<strong>color</strong>). This function draws polygons on the lip region with the specified color.</li>
</ul>
</li>
</ul>
<h3>Libraries</h3>
<ul>
<li>
<p><a href="https://mvnrepository.com/artifact/org.tensorflow/tensorflow-lite/2.12.0">TensorFlow Lite</a>: A library for running machine learning models on mobile devices. It enables the app to perform real-time inference using the AI model for face landmark detection.</p>
</li>
<li>
<p><a href="https://mvnrepository.com/artifact/org.tensorflow/tensorflow-lite-task-vision/0.4.0">TensorFlow Lite Task Vision</a>: A library for performing vision-related tasks with TensorFlow Lite. It provides the necessary tools and utilities for working with vision models.</p>
</li>
<li>
<p><a href="https://mvnrepository.com/artifact/com.google.mlkit/face-detection/16.1.5">ML Kit Face Detection</a>: A library provided by Google for face detection. It offers pre-trained models and APIs for detecting faces in images and videos.</p>
</li>
<li>
<p>CameraX: A Jetpack library that simplifies the camera development process on Android. It provides a consistent API across different Android devices and versions. CameraX consists of multiple modules, including:</p>
<ul>
<li><a href="https://mvnrepository.com/artifact/androidx.camera/camera-camera2/1.0.1">Camera2 API</a>: A lower-level camera API introduced in Android 5.0 (API level 21) that provides more advanced features and greater control over camera hardware.
<ul>
<li><a href="https://mvnrepository.com/artifact/androidx.camera/camera-view/1.0.0-alpha27">CameraX CameraView</a>: A pre-built UI component for displaying a camera preview and capturing images or videos. It is built on top of the Camera2 API and provides a higher-level and consistent API.</li>
<li><a href="https://mvnrepository.com/artifact/androidx.camera/camera-lifecycle/1.0.1">CameraX Lifecycle</a>: A module that provides integration with the Android lifecycle components, allowing for easy management of camera operations during different lifecycle states.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>Technological background: iOS</h1>
<p>This is the development explanation of our iOS app.</p>
<figure>
<img src="/assets/img/articles/2023-08-31-Strengthening-our-AI-knowledge-on-mobile-apps/image11.webp">
<figcaption>Debug Mode (left)shows additional face landmark points to check detection accuracy. Color Picker (center) allows color selection with RGB sliders. Normal Mode (right) shows your face with a lipstick overlay in your chosen color.</figcaption>
</figure>
<h3>Implementation</h3>
<ol>
<li>
<p><strong>Set Up the Camera for Video Capture</strong>:</p>
<ol>
<li>Create a capture session to manage video input from the front camera using <strong>AVCaptureSession</strong>.</li>
<li>Set up the input device by creating an instance of <strong>AVCaptureDeviceInput</strong> using the camera device and add the input device to the capture session.</li>
<li>Using <strong>AVCaptureVideoDataOutput</strong>, configure the capture session to receive video frames from the capture session.</li>
</ol>
</li>
<li>
<p><strong>Set Up Preview Layer</strong>:</p>
<ol>
<li>Create an instance of <strong>AVCaptureVideoPreviewLayer</strong> to display the camera feed on the screen.</li>
<li>Start the capture session to begin receiving video frames.</li>
</ol>
</li>
<li>
<p><strong>Set Up Face Detection with Vision Framework</strong>:</p>
<ol>
<li>Adopt the <strong>AVCaptureVideoDataOutputSampleBufferDelegate</strong> protocol in your view controller.</li>
<li>Implement the <strong>captureOutput(_:didOutput:from:)</strong> function to receive and process video frames.</li>
<li>Inside <strong>captureOutput</strong> create a face detection request with an instance of <strong>VNDetectFaceRectanglesRequest</strong> to detect the face in the captured video frames.</li>
<li>Create a <strong>VNImageRequestHandler</strong> to handle the captured video frames and perform the face detection request.</li>
<li>Within the completion handler of the face detection request, process the detected face rectangle results to obtain a bounding box (<strong>CGRect</strong>) that matches the face area.</li>
<li>Implement a <strong>cropFace(from:boundingBox:)</strong> function to extract the face region from the captured video frame.</li>
</ol>
</li>
<li>
<p><strong>Set Up TensorFlow Lite To Obtain Face Landmarks</strong>:</p>
<ol>
<li>Prepare a machine learning model that is specifically designed for face landmark detection. This model should be compatible with TensorFlow Lite and trained to detect landmarks on cropped face images.</li>
<li>Load the TensorFlow Lite model (with a <strong>.tflite</strong> extension) into your Xcode project and ensure it is included in the app target.</li>
<li>Create an instance of <strong>Interpreter</strong> from TensorFlow Lite using the loaded model file.</li>
<li>Before feeding the cropped face image into the TensorFlow Lite model, you need to preprocess it. This typically involves resizing, normalizing, and converting the image to a suitable format expected by the model. The specific implementation details will depend on the image settings (size, orientation etc) and the input tensor specifications.</li>
<li>Pass the preprocessed cropped face image to the TensorFlow Lite model for inference using the interpreter.</li>
<li>Extract the face landmarks from the output tensor obtained from the TensorFlow Lite model. The specific implementation will depend on the structure of your model and the format of the output tensor.</li>
</ol>
</li>
<li>
<p><strong>Identify Facial Features and Add Images to Their Positions</strong>:</p>
<ol>
<li>Identify the specific facial features that you want to draw on by using the face landmarks. In this case, the lips.</li>
<li>The face landmarks obtained from the TensorFlow Lite interpreter are typically in normalized coordinates [0, 1] and relative to the cropped face image. To draw the facial features on the video preview layer, convert these landmarks to screen coordinates by using the dimensions of the video preview layer and the position of the cropped face within the video frame to perform the coordinate transformation.</li>
<li>Determine the appropriate visual representation for the lips image (e.g., border lines and filled shapes) by using <strong>path.addLine</strong> etc and create a corresponding overlay view. Configure the appearance of this image, such as color, line thickness, and transparency, as required.</li>
<li>Add the overlay view as a subview of the video preview layer and adjust the position and size of the view to align with the specific facial features accordingly.</li>
</ol>
</li>
</ol>
<h3>Libraries</h3>
<ul>
<li>
<p><a href="https://developer.apple.com/documentation/vision/">Vision Library Framework</a>: Apply computer vision algorithms to perform a variety of tasks on input images and video.</p>
<ul>
<li><strong>iOS 11.0+, iPadOS 11.0+,macOS 10.13+, Mac Catalyst 13.0+, tvOS 11.0+, visionOS 1.0+ Beta</strong>.</li>
<li>The Vision framework performs face detection, face landmark detection, text detection, barcode recognition, image registration, and general feature tracking. Vision also allows the use of custom Core ML models for tasks like classification or object detection.</li>
</ul>
</li>
<li>
<p><a href="https://developer.apple.com/documentation/avfoundation/avcapturevideodataoutput">AVCaptureVideoDataOutput (AVFoundation Framework)</a>: A capture output that records video and provides access to video frames for processing.</p>
<ul>
<li><strong>iOS 4.0+, iPadOS 4.0+, macOS 10.7+, Mac Catalyst 14.0+, tvOS 17.0+ Beta</strong>.</li>
<li><strong>class AVCaptureVideoDataOutput : AVCaptureOutput</strong></li>
</ul>
</li>
<li>
<p><a href="https://www.tensorflow.org/lite/guide/build_ios">TensorFlow Lite Framework</a>: TensorFlow Lite is a mobile library for deploying machine learning models on mobile, microcontrollers and other edge devices.</p>
<ul>
<li><strong>pod 'TensorFlowLiteSwift', '~> 0.0.1-nightly', :subspecs => ['Metal']</strong></li>
</ul>
</li>
</ul>
<h1>Wrapping up</h1>
<p>The integration of AI undoubtedly brings a novel dimension to the capabilities of mobile apps. However, our experiences with this current project, particularly in the domain of image processing, underscore the complexity that can be involved. The power of AI is apparent, yet the challenges it poses require meticulous handling, involving extensive pre-processing and post-processing to attain the desired outcomes.</p>
<p>Truth be told, we embarked on this venture with the choice of a Flutter app development. However, we encountered several impediments with the TensorFlow library's compatibility with Flutter, therefore it led us to pivot towards more established platforms: iOS and Android. While this transition may have led us away from Flutter at this moment, we hold firm in the belief that the rapid advancement of cross-platform capabilities will soon catch up in this field as well.</p>
<p>Looking ahead, our project's trajectory points towards embracing augmented reality (AR) libraries to introduce more complicated elements into the implementation. Specifically, we are currently working on overlaying sunglasses onto the detected faces. As we move forward, the challenges encountered and lessons learned have undoubtedly fueled our determination to strengthen our AI knowledge in mobile app development, propelling us to craft even more captivating and innovative experiences for our clients.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to create an Interactive Experience using the Spatial Creator Toolkit]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/09/06/How-to-create-an-Interactive-Experience-using-the-Spatial-Creator-Toolkit</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/09/06/How-to-create-an-Interactive-Experience-using-the-Spatial-Creator-Toolkit</guid>
            <pubDate>Wed, 06 Sep 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><a href="https://spatial.io/">Spatial</a> is a metaverse platform that allows anyone to create immersive 3D spaces, which can be instantly shared to the Web, iOS, Android, and VR, and explored by others. The Spatial Creator Toolkit, powered by Unity, allows you to add even more interactivity to the experience, without needing to code. This tutorial will guide you through the process of creating and uploading your first Spatial experience using the Creator Toolkit on Unity.</p>
<h3>Before we get started:</h3>
<ul>
<li>Create an account with <a href="https://spatial.io/signup">Spatial</a></li>
<li>Install <a href="https://unity.com/releases/editor/whats-new/2021.3.21">Unity version 2021.3.21</a> and install the “WebGL Build Support” module with it. If you don’t yet have Unity Hub installed, you will need to <a href="https://unity.com/download">install it first</a></li>
<li>Download the <a href="https://drive.google.com/file/d/1AAdOCsfh-E1OlIqAwHOgBjVBxG4CYCyN/view?usp=sharing">Spatial Unity Starter Template</a></li>
<li>Download this <a href="https://sketchfab.com/3d-models/golf-ball-5f158949c1084575abf02437f6b43028">3D golf ball</a> in .obj file format</li>
<li>Download this <a href="https://drive.google.com/file/d/1qQeiCBRPuWM8B2sfJdJYPeNmhBUHVU-K/view?usp=sharing">Monstarlab Badge</a></li>
</ul>
<p>Start by opening the Spatial Starter template project in Unity. You will be prompted to update to the latest Spatial SDK. Click 'yes' and allow the project to load. We will need to configure our Spatial credentials first. Click on the 'Spatial SDK' tab in the header > Account. This will open the Spatial Portal window (which we will keep coming back to).</p>
<p>Click on "Get Login Token", which will redirect you to your browser where you can copy your token directly to your clipboard. Once you copy it, come back to Unity and click on "Paste Login Token". Now you should be ready and set up to start working!</p>
<p>![Account Config](/assets/img/articles/2023-09-06-How-to-create-an-Interactive-Experience-using-the-Spatial-Creator-Toolkit/AccountConfig.webp</p>
<p>In the Project window, search for "GolfCourseDriving" and open this scene.</p>
<p><img src="/assets/img/articles/2023-09-06-How-to-create-an-Interactive-Experience-using-the-Spatial-Creator-Toolkit/Golf_Blue.webp" alt="Golf Course Driving"></p>
<p>In the Spatial Portal window, under the 'Config' tab, set 'Active Package' to 'Space - Golf Course'. Now that we've set this golf course environment as our active scene, we can go ahead and test this scene in Spatial by clicking on 'Test Active Scene' at the top of the window.</p>
<p>![Active Package](/assets/img/articles/2023-09-06-How-to-create-an-Interactive-Experience-using-the-Spatial-Creator-Toolkit/Config.webp</p>
<p>If this is your first time testing a scene on Spatial, it will take some time to load, but subsequent tests will be much quicker. Once it's done, your sandbox environment will open on your browser.</p>
<p><img src="/assets/img/articles/2023-09-06-How-to-create-an-Interactive-Experience-using-the-Spatial-Creator-Toolkit/Spatial_Course.webp" alt="Sandbox Environment"></p>
<p>You can walk around using your arrow keys. Walk up to the golf cart and click [F] to drive it. Now your arrow keys will drive the golf cart around the scene! This is already pretty fun, but let's go back to Unity and see how we can add more interactivity to this scene.</p>
<p>Back in Unity, let's change out the default blue sky. Open the lighting window from 'Window' > 'Rendering' > 'Lighting'. Under the 'Environment' tab, click on the small circle next to 'Skybox Material'. This will let you search through your project to replace the material. Search for 'Epic_GloriousPink' and select it.</p>
<p>![Lighting](/assets/img/articles/2023-09-06-How-to-create-an-Interactive-Experience-using-the-Spatial-Creator-Toolkit/Lighting.webp</p>
<p>You will notice instantly that this also changes the ambient light of the scene to a more pink-ish hue. That's because the source of the environment lighting is currently being pulled from the sky, which is what happens in real life. However, if you'd like to control the mood of the scene yourself, you can choose a color from 'Lighting' > 'Environment lighting' > 'Source' > 'Color'. Personally, I like the pink sunset!</p>
<p><img src="/assets/img/articles/2023-09-06-How-to-create-an-Interactive-Experience-using-the-Spatial-Creator-Toolkit/Golf_Pink.webp" alt="Pink Sunset"></p>
<p>Now, let's import the 3D model of the golf ball into the project. You can do this by opening the root of your Unity project in your File explorer/Finder and pasting the 3D model folder inside the 'Assets' folder. For more information on importing 3D objects and how to deal with materials and textures, you can read more <a href="https://docs.unity3d.com/Manual/ImportingModelFiles.html">here.</a></p>
<p>From the Project window, search for "Trigger (Collectable)" and drag this prefab directly into your scene. Set its position in the Transform component to (X: 522, Y: 4.48, Z: 654). If this is your first time using Unity, you can read <a href="https://docs.unity3d.com/Manual/SceneViewNavigation.html">here</a> to learn how to navigate the scene view.</p>
<p>If you look at the 'Spatial Trigger Event' component, you can see a few things that will happen when the player walks into the radius of this object.</p>
<p>![Trigger Collectable](/assets/img/articles/2023-09-06-How-to-create-an-Interactive-Experience-using-the-Spatial-Creator-Toolkit/Trigger1.webp</p>
<p>It's listening for 'Local Avatar', which is any player in Spatial. Once a player enters this trigger, 'On Enter Event' is called and the following happens:</p>
<ul>
<li>A coin sound effect is played</li>
<li>The coin game object is disabled</li>
<li>A particle effect plays</li>
<li>The 'Spatial Trigger Component' is disabled</li>
</ul>
<p>All of these happening in unison gives the impression of the player "collecting" the coin when they walk over it.</p>
<p>Let's switch out the coin model to the golf ball we imported earlier. Do this by disabling the child object "Coin" and dragging in the golf ball model as a child to the 'Trigger (Collectable)' game object. Scale and position it so it's roughly at the same size and position as the coin.</p>
<p><img src="/assets/img/articles/2023-09-06-How-to-create-an-Interactive-Experience-using-the-Spatial-Creator-Toolkit/Ball_Course.webp" alt="Golf ball on course"></p>
<p>Make sure to switch out the coin for the golf ball in the 'On Trigger Event', so the new 3D model is disabled when collected.</p>
<p>![On Trigger Event](/assets/img/articles/2023-09-06-How-to-create-an-Interactive-Experience-using-the-Spatial-Creator-Toolkit/Trigger2.webp</p>
<p>Now, let's set up a Quest. A Quest is a set of tasks the player needs to complete in order to fulfil a certain goal or objective. We can also reward players when they finish a quest, but more on that later.</p>
<p>Create an empty game object in the scene hierarchy and name it 'Golf Quest'. Click 'Add Component' > 'Spatial Quest'. Give your Quest a name and description. Enable the options as shown below:</p>
<p>![Spatial Quest](/assets/img/articles/2023-09-06-How-to-create-an-Interactive-Experience-using-the-Spatial-Creator-Toolkit/Quest1.webp</p>
<p>Let's add a task under the Tasks list.</p>
<p>![Task List](/assets/img/articles/2023-09-06-How-to-create-an-Interactive-Experience-using-the-Spatial-Creator-Toolkit/Task1.webp</p>
<p>This task shows us that we would need to collect 5 golf balls to complete this task, but we need  some way of tracking that a golf ball has been collected. Back in the 'Golf Ball' game object, in the 'Spatial Trigger Event Component' > 'On Enter Event', add a 'Quest Event'. Select the Quest you created, and choose 'Add Task Progress' and 'Golf balls collected'.</p>
<p>![Quest Event](/assets/img/articles/2023-09-06-How-to-create-an-Interactive-Experience-using-the-Spatial-Creator-Toolkit/QuestEvent.webp</p>
<p>Now, when a player walks over this golf ball, it will update the task progress on the Quest. On the Quest game object, we said they would need to collect 5 golf balls, so go ahead and duplicate the golf ball 4 more times and place them around the environment as you like. Once you're done, go ahead and test the active scene to try it out yourself on Spatial!</p>
<p>If you run around and collect the balls, the quest gets updated on the bottom left of the screen. However, if you try driving a golf cart to collect it, you'll notice it's unable to pick it up. Let's fix this.</p>
<p>Back in Unity, select a GolfCart game object. You'll see it's on the 'Vehicle' layer. If you check any of the golf ball triggers, it's listening for a 'LocalAvatar', which would be the player. On the golf cart, switch out its layer to 'AvatarLocal'. The editor will ask if you want to change the layer of all its children objects too. Select 'No, this object only'.</p>
<p>![Game object layers](/assets/img/articles/2023-09-06-How-to-create-an-Interactive-Experience-using-the-Spatial-Creator-Toolkit/Layer.webp</p>
<p>Now, we need to add a box collider around the cart to define the boundary of what needs to collide with the golf ball to trigger its event. On the cart object, click 'Add Component' > 'Box Collider'. Set 'IsTrigger' to true. Edit the box collider to be bigger than the cart.</p>
<p><img src="/assets/img/articles/2023-09-06-How-to-create-an-Interactive-Experience-using-the-Spatial-Creator-Toolkit/Cart_Collider.webp" alt="Box Collider"></p>
<p>Go ahead and repeat these steps for all the carts. Test the scene again in your sandbox environment and you'll find you can drive the carts around to collect the golf balls now!</p>
<p><img src="/assets/img/articles/2023-09-06-How-to-create-an-Interactive-Experience-using-the-Spatial-Creator-Toolkit/Cart_Collect.webp" alt="Collect golf balls"></p>
<p>Finally, to reward the players for finishing the quest, we need to publish our space first (it can be set to private). In the Spatial Portal > Config tab, select 'Create New World'and then select 'Publish'. You'll receive an email once your space is successfully published. After that, we can set up a badge in <a href="https://www.spatial.io/studio/worlds">Spatial Studio</a>.</p>
<p>Select your world, and then select Badges > + New Badge. Here, you can upload the Monstarlab badge that you downloaded at the start of this blog (or feel free to create your own!). Give your badge a name and description.</p>
<p>![Badge Description](/assets/img/articles/2023-09-06-How-to-create-an-Interactive-Experience-using-the-Spatial-Creator-Toolkit/Badge.webp</p>
<p>Now you can return to Unity, and go back to your Quest object. Add a 'Quest Reward' and select 'Badge'. Copy and paste the 'Id' from your badge in Spatial Studio.</p>
<p>![Badge ID](/assets/img/articles/2023-09-06-How-to-create-an-Interactive-Experience-using-the-Spatial-Creator-Toolkit/BadgeID.webp</p>
<p>And there you go! Run the scene again, collect all the golf balls, and you should be rewarded with a badge when you're done! 🙂</p>
<p>![Reward](/assets/img/articles/2023-09-06-How-to-create-an-Interactive-Experience-using-the-Spatial-Creator-Toolkit/End.webp</p>
<h3>Useful resources</h3>
<ul>
<li><a href="https://docs.spatial.io/">https://docs.spatial.io/</a></li>
<li><a href="https://docs.spatial.io/quests">https://docs.spatial.io/quests</a></li>
</ul>
<p><em>Article Photo by <a href="https://www.spatial.io/">Spatial</a></em></p>
<p><em>Spatial Unity Starter Template by <a href="https://docs.spatial.io/getting-started">Spatial</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Surefire and Failsafe: Benefits of parallel execution]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/09/01/Test-Build-Profile-for-Maven-with-Surfire-and-Failsafe</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/09/01/Test-Build-Profile-for-Maven-with-Surfire-and-Failsafe</guid>
            <pubDate>Fri, 01 Sep 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Why split the test execution in the build process for different types of tests?</p>
<p>By default, all unit tests written with JUnit and Mockito will be categorised as Unit tests whereas all other types of tests
integrate one or more pieces or units of the system.</p>
<p>Here are for example some common context type annotations provided by SpringBoot for integration tests, which can be used
to test various components in a backend application:</p>
<pre><code class="hljs language-less"><span class="hljs-variable">@DataJpaTest</span>

<span class="hljs-variable">@DataMongoTest</span>

<span class="hljs-variable">@WebMvcTest</span>

<span class="hljs-variable">@SpringBootTest</span>
</code></pre>
<p>If you are interested in how to set up an isolated test with SpringBoot and TestContainers,
then please refer to our <a href="https://engineering.monstar-lab.com/en/post/2023/08/01/Test-Isolation-with-TestContainers-and-SpringBoot/">previous blog post provided with Gradle and Kotlin</a>.</p>
<p>The purpose of this article is to share our own experience with a few arguments about
how to structure the common type of unit and integration tests (also shortly named as *IT)
in Spring Boot with Maven and Java. This type of setup can be very individual
and might be considerable only by some engineers. This knowledge share will just demonstrate how to achieve
it and how to organise them best, as well as which arguments support this idea.</p>
<h1></h1>
<hr>
<h1></h1>
<p>Below are some reasons which might serve as an argument for setting up your build profiles in this way:</p>
<figure>
<img src="/assets/img/articles/2023-09-01-Test-Build-Profile-for-Maven-with-Surfire-and-Failsafe/mix.webp">
<figcaption>Conflict in Intention</figcaption>
</figure>
<p>The complexity of unit and integration tests can vary from each other by setup and the amount of components involved.
Unit tests are normally short, they use very little dependencies or no dependencies at all and are fast in their execution.
The Integration tests on the other hand could have the size from small to huge,
depending on the part of the system they will be executed against and
often instability might come into question. If the setup requires multiple dependencies which are not just Beans,
but an entire system or containers to be provided, they will also run longer.
If a problem occurs in a unit test, it will cause the pipeline to fail.
On the other hand, some other integration test may have already been executed before this failure, which could have been avoided
and postponed for a later phase. Parallel execution of these 2 different types of tests could alert about a failing unit test
way earlier, which means putting the shorter and simpler execution beforehand will cost less time and effort by providing early
feedback about failures.</p>
<figure>
<img src="/assets/img/articles/2023-09-01-Test-Build-Profile-for-Maven-with-Surfire-and-Failsafe/report.webp">
<figcaption>Code Coverage</figcaption>
</figure>
<p>Success is measured, and in this scenario, the measurement scale is the code coverage report,
which is normally generated and provided with specific tools such as Jacoco with SonarQube visualisation and code quality checks.
After unit test execution, Jacoco could help collect reports from different parts of the execution in order to visualise it.
Unit tests could for example provide line coverage for the methods that they are bound to.
This is not true for the integration tests, because integration tests might aim to test or connect a few units of an application,
but in reality, the execution can start or execute many other parts of it: as a result the report will include all parts
executed during the test, even though some units weren't intended to be part of the testing at all.</p>
<figure>
<img src="/assets/img/articles/2023-09-01-Test-Build-Profile-for-Maven-with-Surfire-and-Failsafe/time.webp">
<figcaption>Time is precious</figcaption>
</figure>
<p>As unit tests are small, they also run fast. On the other hand the integration tests are not only different in size,
but also in execution time. This can of course depend on several aspects such as how many beans need to be created,
or which context is necessary for the application to prepare, or how many containers or systems should be started
in order to get ready for the test completion. This leads the test execution to take much longer and even
with some intentional delays put in the code, it might prolong to some minutes.</p>
<figure>
<img src="/assets/img/articles/2023-09-01-Test-Build-Profile-for-Maven-with-Surfire-and-Failsafe/balance.webp">
<figcaption>Stability</figcaption>
</figure>
<p>The more "parties" are involved in the "business" the more problems and blockers are possible.
As unit tests normally do not have many services or components as dependencies,
they always run stable and are important to fix, because this would be the first quality control.
Of course, it is always a good practice to achieve maximum stability for the integration tests,
but this is not always possible or should be postponed for internal specific reasons. As many different typesIf the arguments above were convincing, then the example below<br>
of technologies might be used to prepare the test system, it could also be preferred to fix them at a later time.</p>
<h3>Technical Setup</h3>
<p>If the arguments above were convincing, then the example below should provide a very simple technical setup
to create different build profiles which can later be used in CI/CD pipeline configuration.</p>
<p>In the current example a standard SpringBoot application is used with the following components</p>
<ul>
<li>Controllers for endpoint calls</li>
<li>Service layer for repository access</li>
<li>Repository layer to access PostgreSQL Database</li>
</ul>
<p>The unit tests mock all dependencies with Mockito and JUnit5 as following:</p>
<pre><code class="hljs language-scss"><span class="hljs-keyword">@ExtendWith</span>(SpringExtension.class)
class UserServiceTest {

    <span class="hljs-keyword">@Mock</span>
    UserRepository userRepository;

    <span class="hljs-keyword">@InjectMocks</span>
    UserService userService;

    <span class="hljs-keyword">@Test</span>
    void findAll() {
        <span class="hljs-comment">//GIVEN</span>
        User user = new <span class="hljs-built_in">User</span>();
        user<span class="hljs-selector-class">.setFirstname</span>("TestName");
        user<span class="hljs-selector-class">.setLastname</span>("TestLastname");
        <span class="hljs-built_in">doReturn</span>(new ArrayList&#x3C;>(){{
            <span class="hljs-built_in">add</span>(user);
        }})<span class="hljs-selector-class">.when</span>(userRepository)<span class="hljs-selector-class">.findAll</span>();

        <span class="hljs-comment">//WHEN</span>
        List&#x3C;User> users = userService<span class="hljs-selector-class">.findAll</span>();

        <span class="hljs-comment">//THEN</span>
        <span class="hljs-built_in">assertThat</span>(users.get(<span class="hljs-number">0</span>)<span class="hljs-selector-class">.getFirstname</span>())<span class="hljs-selector-class">.isEqualTo</span>("TestName");
        <span class="hljs-built_in">verify</span>(userRepository)<span class="hljs-selector-class">.findAll</span>();
    }
}
</code></pre>
<p>In order to demonstrate the separation we also provided a full isolation test with TestContainers,
which tests the following integration in an isolated context:
Endpoint Call >> Service Layer >> Repository Layer >> PSQL Instance.</p>
<pre><code class="hljs language-less"><span class="hljs-variable">@Testcontainers</span>
<span class="hljs-variable">@SpringBootTest</span>(classes = TestProfileApplication.class)
<span class="hljs-variable">@ActiveProfiles</span>(<span class="hljs-string">"test"</span>)
<span class="hljs-variable">@AutoConfigureMockMvc</span>(addFilters = false)
class UserServiceIT {

  <span class="hljs-variable">@Autowired</span>
  UserRepository userRepository;

  <span class="hljs-variable">@Autowired</span>
  MockMvc mockMvc;

  <span class="hljs-variable">@Test</span>
  void retrieveSavedUser() throws Exception {
    <span class="hljs-comment">//GIVEN</span>
    <span class="hljs-selector-tag">var</span> <span class="hljs-selector-tag">user</span> = <span class="hljs-selector-tag">new</span> <span class="hljs-selector-tag">User</span>();
    <span class="hljs-selector-tag">user</span><span class="hljs-selector-class">.setFirstname</span>(<span class="hljs-string">"Name"</span>);
    <span class="hljs-selector-tag">user</span><span class="hljs-selector-class">.setLastname</span>(<span class="hljs-string">"Lastname"</span>);
    <span class="hljs-selector-tag">userRepository</span><span class="hljs-selector-class">.save</span>(user);

    <span class="hljs-comment">//WHEN</span>
    <span class="hljs-selector-tag">mockMvc</span><span class="hljs-selector-class">.perform</span>(MockMvcRequestBuilders.get(<span class="hljs-string">"/user/{id}"</span>, <span class="hljs-number">1</span>))

    <span class="hljs-comment">//THEN</span>
            <span class="hljs-selector-class">.andExpect</span>((MockMvcResultMatchers.status().isOk()))
            <span class="hljs-selector-class">.andExpect</span>(result -> {
              <span class="hljs-selector-tag">var</span> <span class="hljs-selector-tag">content</span> = <span class="hljs-selector-tag">result</span><span class="hljs-selector-class">.getResponse</span>()<span class="hljs-selector-class">.getContentAsString</span>();
              <span class="hljs-selector-tag">Assertions</span><span class="hljs-selector-class">.assertThat</span>(content)<span class="hljs-selector-class">.contains</span>(<span class="hljs-string">"Lastname"</span>);
            });
  }
}
</code></pre>
<p>For simplicity in the structure, both tests follow the famous Given-When-Then pattern for writing the tests.</p>
<p>And finally, to configure it, the following 2 plugin configurations for Surefire and Failsafe are added in <code>pom.xml</code>.</p>
<pre><code class="hljs language-php-template"><span class="xml">    <span class="hljs-tag">&#x3C;<span class="hljs-name">build</span>></span>
        <span class="hljs-tag">&#x3C;<span class="hljs-name">plugins</span>></span>
            <span class="hljs-tag">&#x3C;<span class="hljs-name">plugin</span>></span>
                <span class="hljs-tag">&#x3C;<span class="hljs-name">groupId</span>></span>org.apache.maven.plugins<span class="hljs-tag">&#x3C;/<span class="hljs-name">groupId</span>></span>
                <span class="hljs-tag">&#x3C;<span class="hljs-name">artifactId</span>></span>maven-surefire-plugin<span class="hljs-tag">&#x3C;/<span class="hljs-name">artifactId</span>></span>
                <span class="hljs-tag">&#x3C;<span class="hljs-name">version</span>></span>2.22.0<span class="hljs-tag">&#x3C;/<span class="hljs-name">version</span>></span>
                <span class="hljs-tag">&#x3C;<span class="hljs-name">configuration</span>></span>
                    <span class="hljs-tag">&#x3C;<span class="hljs-name">skipTests</span>></span>${skip.unit.tests}<span class="hljs-tag">&#x3C;/<span class="hljs-name">skipTests</span>></span>
                <span class="hljs-tag">&#x3C;/<span class="hljs-name">configuration</span>></span>
            <span class="hljs-tag">&#x3C;/<span class="hljs-name">plugin</span>></span>

            <span class="hljs-tag">&#x3C;<span class="hljs-name">plugin</span>></span>
                <span class="hljs-tag">&#x3C;<span class="hljs-name">groupId</span>></span>org.apache.maven.plugins<span class="hljs-tag">&#x3C;/<span class="hljs-name">groupId</span>></span>
                <span class="hljs-tag">&#x3C;<span class="hljs-name">artifactId</span>></span>maven-failsafe-plugin<span class="hljs-tag">&#x3C;/<span class="hljs-name">artifactId</span>></span>
                <span class="hljs-tag">&#x3C;<span class="hljs-name">executions</span>></span>
                    <span class="hljs-tag">&#x3C;<span class="hljs-name">execution</span>></span>
                        <span class="hljs-tag">&#x3C;<span class="hljs-name">goals</span>></span>
                            <span class="hljs-tag">&#x3C;<span class="hljs-name">goal</span>></span>integration-test<span class="hljs-tag">&#x3C;/<span class="hljs-name">goal</span>></span>
                            <span class="hljs-tag">&#x3C;<span class="hljs-name">goal</span>></span>verify<span class="hljs-tag">&#x3C;/<span class="hljs-name">goal</span>></span>
                        <span class="hljs-tag">&#x3C;/<span class="hljs-name">goals</span>></span>
                    <span class="hljs-tag">&#x3C;/<span class="hljs-name">execution</span>></span>
                <span class="hljs-tag">&#x3C;/<span class="hljs-name">executions</span>></span>
            <span class="hljs-tag">&#x3C;/<span class="hljs-name">plugin</span>></span>
        <span class="hljs-tag">&#x3C;/<span class="hljs-name">plugins</span>></span>
    <span class="hljs-tag">&#x3C;/<span class="hljs-name">build</span>></span>
</span></code></pre>
<p>If we execute the usual <code>mvn clean isntall</code> in CLI, then both tests would execute after each other without an order.</p>
<p>In order to split the execution we would need to be more specific
about which plugin we would like to make part of the maven lifecycle.
By default, in SpringBoot and Maven using the following command would skip the Integration Test
by scanning the files which end with *IT.java,
which of course can also be configured with a different naming convention pattern if needed.</p>
<pre><code class="hljs language-ini">mvn clean <span class="hljs-attr">-DskipITs</span>=<span class="hljs-literal">true</span> verify
</code></pre>
<p>This will then let the Surefire plugin execute all tests by ignoring *IT tests.</p>
<p>In order to do the opposite, a flag configuration will be held for Surefire to let it ignore the unit test execution phase.
In the end, running the following command for Failsafe, will run the IT tests accordingly.</p>
<pre><code class="hljs language-ini">mvn clean <span class="hljs-attr">-Dskip.unit.tests</span>=<span class="hljs-literal">true</span> verify
</code></pre>
<p>As a result of this setup the pipeline can be configured to run separately
to block the follow-up checks based on the results off both execution phases.</p>
<p>Below is a sample from GitHub Actions.</p>
<figure>
<img src="/assets/img/articles/2023-09-01-Test-Build-Profile-for-Maven-with-Surfire-and-Failsafe/pipeline-unlocked.webp">
<figcaption>Locked only by Unit test phase</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2023-09-01-Test-Build-Profile-for-Maven-with-Surfire-and-Failsafe/pipeline-locked.webp">
<figcaption>Locked also by Integration test phase</figcaption>
</figure>
<h4>Tools &#x26; References used here...</h4>
<ul>
<li><a href="https://github.com/monstar-lab-oss/TestProfile">Monstarlab:: template with Kotlin and Gradle</a></li>
<li><a href="https://maven.apache.org/">Maven as build tool</a></li>
<li><a href="https://openjdk.org/">Java and OpenJDK as a programming and runtime environment</a></li>
<li><a href="https://hibernate.org/">Hibernate as an ORM layer for JPA</a></li>
<li><a href="https://www.postgresql.org/">PostgreSQL as a Datasource</a></li>
<li><a href="https://flywaydb.org/">FlyWay for DB script migrations</a></li>
<li><a href="https://spring.io/projects/spring-boot">Spring Boot for Backend Application development</a></li>
<li><a href="https://testcontainers.com/">Test Containers for running PSQL Docker based Container</a></li>
<li><a href="https://unsplash.com/">Unsplash Images for writing the blog post</a></li>
<li><a href="https://monstar-lab.com/global/">Brain and Mind from Monstarlab::</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building a conversational phone callbot powered by Twilio, ChatGPT & AmiVoice]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/08/30/Building-a-conversational-phone-callbot-Twilio-ChatGPT-AmiVoice</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/08/30/Building-a-conversational-phone-callbot-Twilio-ChatGPT-AmiVoice</guid>
            <pubDate>Wed, 30 Aug 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Hello, it’s Tony from the Backend Team of the Japan Office. With the recent rise in popularity of ChatGPT, I decided to explore and implement an interesting use case: a phone conversation with an AI bot.</p>
<p>As AI is taking over the world, call centers will probably be one of the sectors that may be impacted and where humans could be replaced by AI bots (at least, for simple inquiries).</p>
<p>In this article, we will be implementing a prototype of such an application. The technology stack is as follows:</p>
<ul>
<li>NodeJS API &#x26; Websocket server</li>
<li>Twilio API (via the official SDK)</li>
<li>OpenAI API (via the official SDK)</li>
<li>AmiVoice websocket API</li>
</ul>
<p>Twilio will be used as our entrypoint for receiving phone calls. We will provision a phone number for Twilio platform and connect it to our NodeJS server on incoming calls.</p>
<p>Then, a voice recognition service (for Japanese language only) called AmiVoice will be used for transcribing the caller voice stream into text by the means of websocket API.</p>
<p>OpenAI API will be used for sending requests with the transcribed texts and getting back the responses. The responses will be relayed back verbally to the caller via Twilio’s internal text-to-speech features.</p>
<h1>Architecture</h1>
<p>The architecture is quite straight-forward and easy to understand.</p>
<p><img src="/assets/img/articles/2023-08-30-Building-a-conversational-phone-callbot-Twilio-ChatGPT-AmiVoice/architecture.webp" alt="architecture"></p>
<ol>
<li>The caller calls the phone number.</li>
<li>Upon receiving the call, Twilio will start a stream and continously provide voice data.</li>
<li>That data will be relayed to AmiVoice for speech-to-text analysis.</li>
<li>Transcribed texts are received from AmiVoice when a transcription is complete. AmiVoice also takes care of detecting pauses in speech so it will return transcribed texts of complete sentences.</li>
<li>The text data is provided to ChatGPT.</li>
<li>ChatGPT returns a response.</li>
<li>That response is relayed to Twilio.</li>
<li>The response (originally in text format) will be spoken via Twilio’s text-to-speech feature to the caller.</li>
</ol>
<h1>Implementation</h1>
<p>⚠️ Please note that I didn't include all the code as I want to focus on the main parts of the implementation. The full code can be found here: <a href="https://github.com/tonystrawberry/callbot.twilio.amivoice/tree/main">https://github.com/tonystrawberry/callbot.twilio.amivoice</a>.</p>
<h2>Setting ngrok for local testing</h2>
<p>Our NodeJS HTTP/websocket server will need to be accessed from the internet. In order to achieve that, let’s setup <code>ngrok</code> and get our <code>ngrok</code> domain.</p>
<ul>
<li>
<p>Install ngrok on your machine. Installation instructions can be found on the <a href="https://ngrok.com/download">official website</a>.</p>
</li>
<li>
<p>We will be using port 3000. Run the command below to provide a tunnel for the internet to access our local server.</p>
<pre><code class="hljs language-protobuf">ngrok http 3000
</code></pre>
</li>
<li>
<p>It will display your ngrok domain. In my case below, it is <a href="https://df2b-39-110-216-51.ngrok-free.app/"><code>https://df2b-39-110-216-51.ngrok-free.app</code></a>. Please note that everytime your restart ngrok (on the free version), your domain will change and you will need to reconfigure the Twilio settings accordingly.</p>
</li>
</ul>
<p>![ngrok](/assets/img/articles/2023-08-30-Building-a-conversational-phone-callbot-Twilio-ChatGPT-AmiVoice/ngrok.webp</p>
<h2>Setting up Twilio</h2>
<p>First, let’s setup Twilio and get our phone number.</p>
<ul>
<li>Sign up and log in into your account.</li>
<li>Buy a phone number. More information can be found here: <a href="https://support.twilio.com/hc/en-us/articles/223135247-How-to-Search-for-and-Buy-a-Twilio-Phone-Number-from-Console">https://support.twilio.com/hc/en-us/articles/223135247-How-to-Search-for-and-Buy-a-Twilio-Phone-Number-from-Console</a></li>
<li>Go to the <code>Voice Configuration</code> for your purchased phone number and set the following configuration:
<ul>
<li>Configure with → <code>Webhook</code></li>
<li>A call comes in → <code>Webhook</code></li>
<li>URL → The <code>ngrok</code> domain appended with <code>/twiml</code></li>
<li>HTTP → <code>HTTP POST</code></li>
</ul>
</li>
</ul>
<p>With this, we are done with our Twilio configuration.</p>
<h2>Building our HTTP server</h2>
<p>Now, let’s dive into the implementation of the application. First, we need to setup a HTTP server for receiving the HTTP POST request from Twilio on incoming calls.</p>
<pre><code class="hljs language-jsx"><span class="hljs-comment">// server.js</span>

<span class="hljs-keyword">const</span> <span class="hljs-title class_">Http</span> = <span class="hljs-built_in">require</span>(<span class="hljs-string">"http"</span>);

<span class="hljs-keyword">const</span> <span class="hljs-title class_">HttpDispatcher</span> = <span class="hljs-built_in">require</span>(<span class="hljs-string">"httpdispatcher"</span>);
<span class="hljs-keyword">const</span> dispatcher = <span class="hljs-keyword">new</span> <span class="hljs-title class_">HttpDispatcher</span>();

<span class="hljs-keyword">const</span> httpServer = <span class="hljs-title class_">Http</span>.<span class="hljs-title function_">createServer</span>(handleRequest);
<span class="hljs-keyword">const</span> <span class="hljs-variable constant_">HTTP_SERVER_PORT</span> = <span class="hljs-number">3000</span>;

<span class="hljs-comment">/* POST /twiml */</span>
<span class="hljs-comment">/* This endpoint is called when the call is first connected */</span>
<span class="hljs-comment">/* This endpoint needs to be set on Twilio as the Voice Request URL */</span>
<span class="hljs-comment">/* Reference: https://www.twilio.com/docs/voice/tutorials/how-to-respond-to-incoming-phone-calls/node */</span>
dispatcher.<span class="hljs-title function_">onPost</span>(<span class="hljs-string">"/twiml"</span>, <span class="hljs-function">(<span class="hljs-params">_request, response</span>) =></span> {
  <span class="hljs-keyword">const</span> twilioVoiceResponse = <span class="hljs-keyword">new</span> <span class="hljs-title class_">VoiceResponse</span>();

  <span class="hljs-comment">/* Setup the media stream from Twilio to our own websocket */</span>
  twilioVoiceResponse.<span class="hljs-title function_">start</span>().<span class="hljs-title function_">stream</span>({
    <span class="hljs-attr">url</span>: <span class="hljs-string">`wss://<span class="hljs-subst">${env.NGROK_DOMAIN}</span>`</span>,
  });

  <span class="hljs-comment">/* Say the first message to the caller */</span>
  twilioVoiceResponse.<span class="hljs-title function_">say</span>(
    {
      <span class="hljs-attr">voice</span>: <span class="hljs-string">"Polly.Takumi-Neural"</span>,
      <span class="hljs-attr">language</span>: <span class="hljs-string">"ja-JP"</span>,
    },
    <span class="hljs-string">"こんにちは。匠です。何でも聞いてください。"</span>
  );

  <span class="hljs-comment">/* Do not hangup the call for 40 seconds */</span>
  twilioVoiceResponse.<span class="hljs-title function_">pause</span>({ <span class="hljs-attr">length</span>: <span class="hljs-number">40</span> });

  response.<span class="hljs-title function_">writeHead</span>(<span class="hljs-number">200</span>, { <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"text/xml"</span> });

  <span class="hljs-comment">/* Send the TwiML twilioVoiceResponse back to Twilio */</span>
  <span class="hljs-comment">/* Example:
  &#x3C;?xml version="1.0" encoding="UTF-8"?>
  &#x3C;Response>
    &#x3C;Start>
      &#x3C;Stream name="VoiceStream from Twilio" url="wss://df2b-39-110-216-51.ngrok-free.app"/>
    &#x3C;/Start>
    &#x3C;Say language="ja-JP" voice="Polly.Takumi-Neural">こんにちは。かずはです。何でも聞いてください。&#x3C;/Say>
    &#x3C;Pause length="40"/>
  &#x3C;/Response>
  */</span>
  <span class="hljs-title function_">log</span>(<span class="hljs-string">`Twilio WS: TwiML response sent back: <span class="hljs-subst">${twilioVoiceResponse.toString()}</span>`</span>);
  response.<span class="hljs-title function_">end</span>(twilioVoiceResponse.<span class="hljs-title function_">toString</span>());
});

<span class="hljs-comment">/* Start the HTTP server on localhost with port HTTP_SERVER_PORT */</span>
httpServer.<span class="hljs-title function_">listen</span>(<span class="hljs-variable constant_">HTTP_SERVER_PORT</span>, <span class="hljs-function">() =></span> {
  <span class="hljs-title function_">log</span>(<span class="hljs-string">`Server listening on: http://localhost:<span class="hljs-subst">${HTTP_SERVER_PORT}</span>`</span>);
});
</code></pre>
<p>The code above sets the <code>POST /twiml</code> endpoint. A response has to be sent back to Twilio to provide it with instructions on what to do. In our case, we want to Twilio to:</p>
<ul>
<li>start a stream to our websocket (<code>wss://54de-126-36-198-149.ngrok-free.app</code> in my case). Documentation about Twilio Voice Stream can be found <a href="https://www.twilio.com/docs/voice/twiml/stream">here</a>.</li>
<li>make Twilio say an introductory message to the caller. We specified the language <code>ja-JP</code> and the used voice <code>Polly.Kazuha-Neural</code>. A list of usable voices for text-to-speech can be found <a href="https://www.twilio.com/docs/voice/twiml/say/text-speech">here</a>.</li>
<li>pause the call for 40 seconds. Otherwise, the call would end immediately.</li>
</ul>
<p>That is it for our HTTP server.</p>
<h2>Using Twilio VoiceStream</h2>
<p>In the previous step, we provided Twilio with a websocket URL. We will now implement that websocket server.</p>
<pre><code class="hljs language-jsx"><span class="hljs-comment">// server.js</span>

<span class="hljs-keyword">const</span> <span class="hljs-title class_">WebSocketServer</span> = <span class="hljs-built_in">require</span>(<span class="hljs-string">"websocket"</span>).<span class="hljs-property">server</span>;
<span class="hljs-keyword">const</span> httpServer = <span class="hljs-title class_">Http</span>.<span class="hljs-title function_">createServer</span>(handleRequest);

<span class="hljs-comment">/* Setup the websocket server */</span>
<span class="hljs-keyword">const</span> websocketServer = <span class="hljs-keyword">new</span> <span class="hljs-title class_">WebSocketServer</span>({
  <span class="hljs-attr">httpServer</span>: httpServer,
  <span class="hljs-attr">autoAcceptConnections</span>: <span class="hljs-literal">true</span>,
});

websocketServer.<span class="hljs-title function_">on</span>(<span class="hljs-string">"connect"</span>, <span class="hljs-function"><span class="hljs-params">connection</span> =></span> {
  <span class="hljs-title function_">log</span>(<span class="hljs-string">"Twilio WS: Connection accepted"</span>);
  <span class="hljs-keyword">new</span> <span class="hljs-title class_">VoiceStream</span>(connection);
});
</code></pre>
<p>When Twilio connects to websocket server, we will instantiate a VoiceStream object that will manage that connection. Implementation is as follows.</p>
<pre><code class="hljs language-jsx"><span class="hljs-comment">// server.js</span>

<span class="hljs-comment">/* VoiceStream class that will store the Websocket connection [Twilio] &#x3C;-> [Websocket server] */</span>
<span class="hljs-comment">/* This class will handle the media stream from Twilio */</span>
<span class="hljs-comment">/* It will also handle the transcription from AmiVoice */</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">VoiceStream</span> {
  <span class="hljs-title function_">constructor</span>(<span class="hljs-params">connection</span>) {
    <span class="hljs-comment">// processMessage is called when a message is received from Twilio</span>
    connection.<span class="hljs-title function_">on</span>(<span class="hljs-string">"message"</span>, <span class="hljs-function"><span class="hljs-params">message</span> =></span> {
      <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">processMessage</span>(message);
    });

    <span class="hljs-comment">// close is called when the connection is closed by Twilio</span>
    connection.<span class="hljs-title function_">on</span>(<span class="hljs-string">"close"</span>, <span class="hljs-function">() =></span> {
      <span class="hljs-title function_">log</span>(<span class="hljs-string">"Twilio WS: Connection closed by Twillio"</span>);
      <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">close</span>();
    });

    <span class="hljs-variable language_">this</span>.<span class="hljs-property">messageCount</span> = <span class="hljs-number">0</span>;
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">amiVoiceConnection</span> = <span class="hljs-literal">null</span>;
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">callSid</span> = <span class="hljs-string">""</span>; <span class="hljs-comment">// unique call identifier from Twilio</span>
  }

  <span class="hljs-comment">/* This function is called when a message is received from Twilio */</span>
  <span class="hljs-comment">/* The message will be a JSON object */</span>
  <span class="hljs-comment">/* Reference: https://www.twilio.com/docs/voice/tutorials/consume-real-time-media-stream-using-websockets-python-and-flask */</span>
  <span class="hljs-title function_">processMessage</span>(<span class="hljs-params">message</span>) {
    <span class="hljs-keyword">if</span> (message.<span class="hljs-property">type</span> === <span class="hljs-string">"utf8"</span>) {
      <span class="hljs-keyword">const</span> data = <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">parse</span>(message.<span class="hljs-property">utf8Data</span>);
      <span class="hljs-keyword">switch</span> (data.<span class="hljs-property">event</span>) {
        <span class="hljs-keyword">case</span> <span class="hljs-string">"connected"</span>:
          <span class="hljs-comment">// This event is received when the connection is first established</span>
          <span class="hljs-comment">// Example: { event: "connected", protocol: "Call", version: "0.2.0" }</span>
          <span class="hljs-title function_">log</span>(<span class="hljs-string">"Twilio WS: Connected event received: "</span>, data);
          <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">"start"</span>:
          <span class="hljs-comment">// This event is received when the stream is started</span>
          <span class="hljs-comment">// We will store the callSid and create an AmiVoice connection</span>
          <span class="hljs-comment">// Example: {</span>
          <span class="hljs-comment">//   event: "start",</span>
          <span class="hljs-comment">//   sequenceNumber: "1",</span>
          <span class="hljs-comment">//   start: {</span>
          <span class="hljs-comment">//     accountSid: "AC0d7016bb1842a6b3e80d1d6d56036784",</span>
          <span class="hljs-comment">//     streamSid: "MZ639f5aaf9b0c0fe84f01b5e8478c7d52",</span>
          <span class="hljs-comment">//     callSid: "CAea9962c5d642db0576ffd21fa1b9d6ad",</span>
          <span class="hljs-comment">//     tracks: [ "inbound" ],</span>
          <span class="hljs-comment">//     mediaFormat: { encoding: "audio/x-mulaw", sampleRate: 8000, channels: 1 }</span>
          <span class="hljs-comment">//   },</span>
          <span class="hljs-comment">//   streamSid: "MZ639f5aaf9b0c0fe84f01b5e8478c7d52"</span>
          <span class="hljs-comment">// }}</span>
          <span class="hljs-title function_">log</span>(<span class="hljs-string">"Twilio WS: Start event received: "</span>, data);

          <span class="hljs-variable language_">this</span>.<span class="hljs-property">callSid</span> = data.<span class="hljs-property">start</span>.<span class="hljs-property">callSid</span>;
          <span class="hljs-variable language_">this</span>.<span class="hljs-property">amiVoiceConnection</span> = <span class="hljs-keyword">new</span> <span class="hljs-title class_">AmiVoice</span>(env.<span class="hljs-property">AMIVOICE_API_KEY</span>);

          <span class="hljs-comment">// When the AmiVoice connection receives a transcription, we will process it with ChatGPT and send it back to Twilio</span>
          <span class="hljs-variable language_">this</span>.<span class="hljs-property">amiVoiceConnection</span>.<span class="hljs-title function_">on</span>(<span class="hljs-string">"transcription"</span>, <span class="hljs-function"><span class="hljs-params">transcriptionData</span> =></span> {
            <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">receivedTranscription</span>(transcriptionData, <span class="hljs-variable language_">this</span>.<span class="hljs-property">callSid</span>);
          });
          <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">"media"</span>:
          <span class="hljs-comment">// This event is received continuously while the stream is active</span>
          <span class="hljs-comment">// It contains the audio data from Twilio that we will send to AmiVoice</span>
          <span class="hljs-comment">// Example: {</span>
          <span class="hljs-comment">//   event: 'media',</span>
          <span class="hljs-comment">//   sequenceNumber: '256',</span>
          <span class="hljs-comment">//   media: {</span>
          <span class="hljs-comment">//     track: 'inbound',</span>
          <span class="hljs-comment">//     chunk: '255',</span>
          <span class="hljs-comment">//     timestamp: '5180',</span>
          <span class="hljs-comment">//     payload: 'fv//fv///37///////////////////////////9+//9+////fv///37/////fv////////////////9+//////////9+////fn7/////////////////////fv////9+//9+/////////////////37//////////37//////////37//////////////////37/////////////fv9+/w=='</span>
          <span class="hljs-comment">//   },</span>
          <span class="hljs-comment">//   streamSid: 'MZ1476190b98d7e720c314ecc9cde50b73'</span>
          <span class="hljs-comment">// }</span>
          <span class="hljs-variable language_">this</span>.<span class="hljs-property">amiVoiceConnection</span>.<span class="hljs-title function_">send</span>(data.<span class="hljs-property">media</span>.<span class="hljs-property">payload</span>);
          <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">"closed"</span>:
          <span class="hljs-comment">// This event is received when the stream is closed</span>
          <span class="hljs-comment">// Example: { event: "close", streamSid: "MZ639f5aaf9b0c0fe84f01b5e8478c7d52" }</span>
          <span class="hljs-title function_">log</span>(<span class="hljs-string">"Twilio WS: Close event received: "</span>, data);
          <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">close</span>();
          <span class="hljs-keyword">break</span>;
      }

      <span class="hljs-variable language_">this</span>.<span class="hljs-property">messageCount</span>++;
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (message.<span class="hljs-property">type</span> === <span class="hljs-string">"binary"</span>) {
      <span class="hljs-title function_">log</span>(<span class="hljs-string">"Twilio WS: binary message received (not supported)"</span>);
    }
  }

  <span class="hljs-comment">// This function is called when the connection is closed</span>
  <span class="hljs-comment">// We will close the AmiVoice connection as well</span>
  <span class="hljs-title function_">close</span>(<span class="hljs-params"></span>) {
    <span class="hljs-title function_">log</span>(
      <span class="hljs-string">"Twilio WS: Closed. Received a total of ["</span> +
        <span class="hljs-variable language_">this</span>.<span class="hljs-property">messageCount</span> +
        <span class="hljs-string">"] messages"</span>
    );
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">amiVoiceConnection</span>.<span class="hljs-title function_">close</span>();
  }

  <span class="hljs-comment">// This function is called when a transcription is received from AmiVoice</span>
  <span class="hljs-comment">// We will send the transcription to ChatGPT and send the response back to Twilio via API</span>
  <span class="hljs-comment">// We can identify the call using the callSid that we stored earlier and pass the ChatGPT response to Twilio</span>
  <span class="hljs-comment">// Reference: https://www.twilio.com/docs/voice/tutorials/how-to-modify-calls-in-progress/node</span>
  <span class="hljs-keyword">async</span> <span class="hljs-title function_">receivedTranscription</span>(<span class="hljs-params">data, callSid</span>) {
    <span class="hljs-title function_">log</span>(<span class="hljs-string">`Twilio WS: Received message from AmiVoice: <span class="hljs-subst">${data.body.text}</span>`</span>);

    <span class="hljs-comment">// Send a &#x3C;Say> message to Twilio that will be read back to the caller</span>
    <span class="hljs-keyword">const</span> completion = <span class="hljs-keyword">await</span> openai.<span class="hljs-title function_">createChatCompletion</span>({
      <span class="hljs-attr">model</span>: <span class="hljs-string">"gpt-3.5-turbo"</span>,
      <span class="hljs-attr">messages</span>: [{ <span class="hljs-attr">role</span>: <span class="hljs-string">"user"</span>, <span class="hljs-attr">content</span>: data.<span class="hljs-property">body</span>.<span class="hljs-property">text</span> }],
    });

    <span class="hljs-comment">// Update the call with the &#x3C;Say> message</span>
    <span class="hljs-keyword">const</span> chatGptMessage = completion.<span class="hljs-property">data</span>.<span class="hljs-property">choices</span>[<span class="hljs-number">0</span>].<span class="hljs-property">message</span>.<span class="hljs-property">content</span>;
    <span class="hljs-keyword">const</span> twiml = <span class="hljs-keyword">new</span> <span class="hljs-title class_">VoiceResponse</span>();
    twiml.<span class="hljs-title function_">say</span>(
      {
        <span class="hljs-attr">voice</span>: <span class="hljs-variable constant_">TWILIO_VOICE</span>,
        <span class="hljs-attr">language</span>: <span class="hljs-variable constant_">TWILIO_LANGUAGE</span>,
      },
      chatGptMessage
    );
    twiml.<span class="hljs-title function_">pause</span>({ <span class="hljs-attr">length</span>: <span class="hljs-number">40</span> });

    twilioClient
      .<span class="hljs-title function_">calls</span>(callSid)
      .<span class="hljs-title function_">update</span>({ <span class="hljs-attr">twiml</span>: twiml.<span class="hljs-title function_">toString</span>() })
      .<span class="hljs-title function_">then</span>(<span class="hljs-function"><span class="hljs-params">_call</span> =></span>
        <span class="hljs-title function_">log</span>(
          <span class="hljs-string">`Twilio WS: ChatGPT response sent back with TwiML: <span class="hljs-subst">${chatGptMessage}</span>`</span>
        )
      );
  }
}
</code></pre>
<p>Throughout the call, Twilio will send to the websocket server four types of message:</p>
<ul>
<li><code>connected</code> : event notifying that Twilio connects to our websocket for the first time.</li>
<li><code>start</code> : event notifying that the stream has started. Useful informations such as <code>callSid</code> (unique identifier for the call) are provided in the body. We will use that <code>callSid</code> later to send back verbal messages from ChatGPT to the call. Besides, we will setup the connection with AmiVoice websocket API and a callback method for processing transcribed messages received.</li>
<li><code>media</code> : event containing the voice data of the caller. That data will be sent to AmiVoice websocket API for transcription.</li>
<li><code>closed</code> : event notifying the end of the connection. Received when the user hangs up or if an error occured.</li>
</ul>
<h2>Transcribing with AmiVoice WebSockets API</h2>
<pre><code class="hljs language-jsx"><span class="hljs-comment">// amivoice.js</span>

<span class="hljs-comment">/*
  This class will store the connection to the AmiVoice API.
  It will also handle the connection and disconnection of the service.
  It will also handle the sending of audio to the service and the sending of the transcription to the server via EventEmitter.
*/</span>

<span class="hljs-keyword">const</span> <span class="hljs-title class_">EventEmitter</span> = <span class="hljs-built_in">require</span>(<span class="hljs-string">"events"</span>);
<span class="hljs-keyword">const</span> <span class="hljs-title class_">WebSocketClient</span> = <span class="hljs-built_in">require</span>(<span class="hljs-string">"websocket"</span>).<span class="hljs-property">client</span>;

<span class="hljs-keyword">const</span> <span class="hljs-variable constant_">AMIVOICE_WEBSOCKET_URL</span> = <span class="hljs-string">"wss://acp-api.amivoice.com/v1/"</span>;

<span class="hljs-keyword">class</span> <span class="hljs-title class_">AmiVoice</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_ inherited__">EventEmitter</span> {
  <span class="hljs-title function_">constructor</span>(<span class="hljs-params">apiKey</span>) {
    <span class="hljs-variable language_">super</span>();
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">apiKey</span> = apiKey;
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">isReady</span> = <span class="hljs-literal">false</span>;
    <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">connect</span>();
  }

  <span class="hljs-comment">/* connect() will create a new connection to the AmiVoice API. */</span>
  <span class="hljs-comment">/* All the listeners are set up here. */</span>
  <span class="hljs-comment">/* Reference: https://docs.amivoice.com/amivoice-api/manual/user-guide/request/websocket-interface */</span>
  <span class="hljs-title function_">connect</span>(<span class="hljs-params"></span>) {
    <span class="hljs-keyword">if</span> (!<span class="hljs-variable language_">this</span>.<span class="hljs-property">isReady</span>) {
      <span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> <span class="hljs-title class_">WebSocketClient</span>();

      client.<span class="hljs-title function_">on</span>(<span class="hljs-string">"connectFailed"</span>, <span class="hljs-function"><span class="hljs-params">error</span> =></span> {
        <span class="hljs-title function_">log</span>(<span class="hljs-string">"AmiVoice: "</span> + error.<span class="hljs-title function_">toString</span>());
        <span class="hljs-variable language_">this</span>.<span class="hljs-property">isReady</span> = <span class="hljs-literal">false</span>;
      });

      client.<span class="hljs-title function_">on</span>(<span class="hljs-string">"connect"</span>, <span class="hljs-function"><span class="hljs-params">connection</span> =></span> {
        <span class="hljs-variable language_">this</span>.<span class="hljs-property">connection</span> = connection;
        <span class="hljs-title function_">log</span>(<span class="hljs-string">"AmiVoice: websocket client connected"</span>);

        connection.<span class="hljs-title function_">on</span>(<span class="hljs-string">"message"</span>, <span class="hljs-function"><span class="hljs-params">message</span> =></span> {
          <span class="hljs-keyword">if</span> (message.<span class="hljs-property">type</span> === <span class="hljs-string">"utf8"</span>) {
            <span class="hljs-comment">// code is the first character of the message and is used to determine what the message is</span>
            <span class="hljs-comment">// Reference: https://docs.amivoice.com/amivoice-api/manual/reference/websocket/packet/packet-state-transition</span>
            <span class="hljs-comment">// A: recognition processing completed and recognition result accepted</span>
            <span class="hljs-keyword">const</span> code = message.<span class="hljs-property">utf8Data</span>.<span class="hljs-title function_">charAt</span>(<span class="hljs-number">0</span>);

            <span class="hljs-keyword">switch</span> (code) {
              <span class="hljs-keyword">case</span> <span class="hljs-string">"A"</span>:
                <span class="hljs-keyword">const</span> data = <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">parse</span>(message.<span class="hljs-property">utf8Data</span>.<span class="hljs-title function_">substring</span>(<span class="hljs-number">2</span>));
                <span class="hljs-title function_">log</span>(<span class="hljs-string">`AmiVoice: [A] <span class="hljs-subst">${data.text}</span>`</span>);
                <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">emit</span>(<span class="hljs-string">"transcription"</span>, { <span class="hljs-attr">body</span>: data });
                <span class="hljs-keyword">break</span>;
            }
          }
        });

        connection.<span class="hljs-title function_">on</span>(<span class="hljs-string">"close"</span>, <span class="hljs-function">() =></span> {
          <span class="hljs-variable language_">this</span>.<span class="hljs-property">isStarted</span> = <span class="hljs-literal">false</span>;
          <span class="hljs-title function_">log</span>(<span class="hljs-string">"AmiVoice: connection closed"</span>);
        });

        <span class="hljs-variable language_">this</span>.<span class="hljs-property">isReady</span> = <span class="hljs-literal">true</span>;
        <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">start</span>();
      });

      client.<span class="hljs-title function_">connect</span>(<span class="hljs-variable constant_">AMIVOICE_WEBSOCKET_URL</span>);
    }
  }

  <span class="hljs-comment">// send() will send the audio to the AmiVoice API</span>
  <span class="hljs-comment">// The audio is sent as a base64 encoded string in the payload so it must be decoded first</span>
  <span class="hljs-comment">// Then the payload is converted to a Uint8Array and the first byte is set to 0x70</span>
  <span class="hljs-comment">// Then the Uint8Array is converted to a Buffer and sent to the AmiVoice API</span>
  <span class="hljs-comment">// Reference: https://docs.amivoice.com/amivoice-api/manual/reference/websocket/command/p-command-packet</span>
  <span class="hljs-comment">// Format: p&#x3C;audio_data></span>
  <span class="hljs-comment">// According to the documentation:</span>
  <span class="hljs-comment">//   `&#x3C;audio_data>は、セッション開始時の s コマンドで指定した音声フォーマットの音声データです。</span>
  <span class="hljs-comment">//   この音声データの先頭に、0x70（ 'p' のアスキーコード）を付け、バイナリフレームで送信します。`</span>
  <span class="hljs-title function_">send</span>(<span class="hljs-params">payload</span>) {
    <span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">isStarted</span>) {
      <span class="hljs-keyword">const</span> buff = <span class="hljs-title class_">Buffer</span>.<span class="hljs-title function_">from</span>(payload, <span class="hljs-string">"base64"</span>);

      <span class="hljs-keyword">const</span> outData = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Uint8Array</span>(buff.<span class="hljs-property">length</span> + <span class="hljs-number">1</span>);
      outData[<span class="hljs-number">0</span>] = <span class="hljs-number">0x70</span>; <span class="hljs-comment">// "p"</span>
      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &#x3C; buff.<span class="hljs-property">length</span>; i++) {
        outData[<span class="hljs-number">1</span> + i] = buff[i];
      }

      <span class="hljs-variable language_">this</span>.<span class="hljs-property">connection</span>.<span class="hljs-title function_">send</span>(<span class="hljs-title class_">Buffer</span>.<span class="hljs-title function_">from</span>(outData));
    }
  }

  <span class="hljs-comment">// close() will send the close command to the AmiVoice API</span>
  <span class="hljs-comment">// Reference: https://docs.amivoice.com/amivoice-api/manual/reference/websocket/command/e-command-packet</span>
  <span class="hljs-comment">// Format: e</span>
  <span class="hljs-title function_">close</span>(<span class="hljs-params"></span>) {
    <span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">isStarted</span> &#x26;&#x26; <span class="hljs-variable language_">this</span>.<span class="hljs-property">isReady</span>) {
      <span class="hljs-title function_">log</span>(<span class="hljs-string">"AmiVoice: send close command"</span>);
      <span class="hljs-keyword">const</span> endCommand = <span class="hljs-string">"e"</span>;
      <span class="hljs-variable language_">this</span>.<span class="hljs-property">connection</span>.<span class="hljs-title function_">send</span>(endCommand);
      <span class="hljs-variable language_">this</span>.<span class="hljs-property">isStarted</span> = <span class="hljs-literal">false</span>;
    }
  }
}

<span class="hljs-variable language_">module</span>.<span class="hljs-property">exports</span> = {
  <span class="hljs-title class_">AmiVoice</span>,
};
</code></pre>
<p>The AmiVoice class will manage the AmiVoice websocket connection and provide methods for sending data (<code>send()</code>) and receiving the transcribed data (<code>A</code> message inside the <code>connection.on("message")</code> block).</p>
<ul>
<li><code>send(payload)</code> function is used for relaying the data from Twilio to AmiVoice and is called in <code>server.js</code>.</li>
<li>When AmiVoice is done with a transcription task, it will send back to us a message with a code <code>A</code> accompanied with the transcribed text. Upon receiving it, we will <code>emit</code> (we used <code>EventEmitter</code> library for this) that transcription back to our <code>VoiceStream</code> so that we can process it.</li>
</ul>
<p>I will skip the detailed explanations of the other parts of the code as they are very specific to AmiVoice. More information about AmiVoice (in Japanese) can be found <a href="https://docs.amivoice.com/amivoice-api/manual/">here</a>.</p>
<h2>Getting &#x26; relaying back ChatGPT answers</h2>
<p>Finally, let’s talk to Mr. ChatGPT. We got the transcribed text of the caller voice and will provide ChatGPT with it.</p>
<pre><code class="hljs language-jsx"><span class="hljs-comment">// amivoice.js</span>

<span class="hljs-keyword">case</span> <span class="hljs-string">"A"</span>:
  <span class="hljs-keyword">const</span> data = <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">parse</span>(message.<span class="hljs-property">utf8Data</span>.<span class="hljs-title function_">substring</span>(<span class="hljs-number">2</span>));　<span class="hljs-comment">// to exclude the A code from the body</span>
  <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">emit</span>(<span class="hljs-string">"transcription"</span>, { <span class="hljs-attr">body</span>: data });
  <span class="hljs-keyword">break</span>;
</code></pre>
<pre><code class="hljs language-jsx"><span class="hljs-comment">// server.js</span>

<span class="hljs-comment">// When the AmiVoice connection receives a transcription, we will process it with ChatGPT and send it back to Twilio</span>
<span class="hljs-variable language_">this</span>.<span class="hljs-property">amiVoiceConnection</span>.<span class="hljs-title function_">on</span>(<span class="hljs-string">"transcription"</span>, <span class="hljs-function">(<span class="hljs-params">transcriptionData</span>) =></span> {
  <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">receivedTranscription</span>(transcriptionData, <span class="hljs-variable language_">this</span>.<span class="hljs-property">callSid</span>);
});

...

<span class="hljs-comment">// This function is called when a transcription is received from AmiVoice</span>
<span class="hljs-comment">// We will send the transcription to ChatGPT and send the response back to Twilio via API</span>
<span class="hljs-comment">// We can identify the call using the callSid that we stored earlier and pass the ChatGPT response to Twilio</span>
<span class="hljs-comment">// Reference: https://www.twilio.com/docs/voice/tutorials/how-to-modify-calls-in-progress/node</span>
<span class="hljs-keyword">async</span> <span class="hljs-title function_">receivedTranscription</span>(<span class="hljs-params">data, callSid</span>) {
  <span class="hljs-title function_">log</span>(<span class="hljs-string">`Twilio WS: Received message from AmiVoice: <span class="hljs-subst">${data.body.text}</span>`</span>);

  <span class="hljs-comment">// Send a &#x3C;Say> message to Twilio that will be read back to the caller</span>
  <span class="hljs-keyword">const</span> completion = <span class="hljs-keyword">await</span> openai.<span class="hljs-title function_">createChatCompletion</span>({
    <span class="hljs-attr">model</span>: <span class="hljs-string">"gpt-3.5-turbo"</span>,
    <span class="hljs-attr">messages</span>: [{<span class="hljs-attr">role</span>: <span class="hljs-string">"user"</span>, <span class="hljs-attr">content</span>: data.<span class="hljs-property">body</span>.<span class="hljs-property">text</span>}],
  });

  <span class="hljs-comment">// Update the call with the &#x3C;Say> message</span>
  <span class="hljs-keyword">const</span> chatGptMessage = completion.<span class="hljs-property">data</span>.<span class="hljs-property">choices</span>[<span class="hljs-number">0</span>].<span class="hljs-property">message</span>.<span class="hljs-property">content</span>;
  <span class="hljs-keyword">const</span> twiml = <span class="hljs-keyword">new</span> <span class="hljs-title class_">VoiceResponse</span>();
  twiml.<span class="hljs-title function_">say</span>({
    <span class="hljs-attr">voice</span>: <span class="hljs-variable constant_">TWILIO_VOICE</span>,
    <span class="hljs-attr">language</span>: <span class="hljs-variable constant_">TWILIO_LANGUAGE</span>,
  }, chatGptMessage);
  twiml.<span class="hljs-title function_">pause</span>({ <span class="hljs-attr">length</span>: <span class="hljs-number">40</span> });

  twilioClient.<span class="hljs-title function_">calls</span>(callSid)
    .<span class="hljs-title function_">update</span>({<span class="hljs-attr">twiml</span>: twiml.<span class="hljs-title function_">toString</span>()})
    .<span class="hljs-title function_">then</span>(<span class="hljs-function"><span class="hljs-params">_call</span> =></span> <span class="hljs-title function_">log</span>(<span class="hljs-string">`Twilio WS: ChatGPT response sent back with TwiML: <span class="hljs-subst">${chatGptMessage}</span>`</span>));
}
</code></pre>
<p>The <code>receivedTranscription</code> called upon receiving the transcription is quite compact and straight-forward:</p>
<ul>
<li>It will send the transcription to text to ChatGPT using <code>openai</code> library and get back the the answer.</li>
<li>A <code>VoiceResponse</code> will be built with the ChatGPT message and other settings such as <code>voice</code>, <code>language</code> , <code>pause</code> and update the current call identified by the <code>callSid</code>. More information <a href="https://www.twilio.com/docs/voice/tutorials/how-to-modify-calls-in-progress/node">here</a>.</li>
</ul>
<p>Now, the application’s implementation is done. We are now able to call a phone number and talk to ChatGPT 🎉 Here is a demonstration video about the prototype we just built.</p>
<h1>Demo</h1>
<p><div class="html5-video-container">
        <video width="400" controls>
          <source src="/assets/img/articles/2023-08-30-Building-a-conversational-phone-callbot-Twilio-ChatGPT-AmiVoice/demo.mp4" type="video/mp4">
        Your browser does not support HTML5 video.
        </video>
      </div></p>
<h1>Conclusion</h1>
<p>Thank you for reading! With simple libraries and services, we managed to make a pretty awesome AI callbot for talking about anything. Full code with installation instructions can be found here: <a href="https://github.com/tonystrawberry/callbot.twilio.amivoice/tree/main">https://github.com/tonystrawberry/callbot.twilio.amivoice</a>.</p>
<p>Of course, we could have considered other services such as Amazon Connect coupled with Lambda instead of Twilio or Amazon Transcribe instead of AmiVoice API. I chose Twilio over Amazon Connect for this prototype for the simplicity of installation and usage and AmiVoice seems to be better at recognizing and transcribing Japanese speech than Amazon Transcribe.</p>
<p>Feel free to contact me for any questions: <a href="https://github.com/tonystrawberry/callbot.twilio.amivoice/tree/main">https://github.com/tonystrawberry/</a></p>
<p><strong>Reference</strong>: <a href="https://cloudapi.kddi-web.com/magazine/twilio-media-stream/twilio-amivoice-speech-recognition-method">https://cloudapi.kddi-web.com/magazine/twilio-media-stream/twilio-amivoice-speech-recognition-method</a></p>
<p><em>Article Photo by <a href="https://unsplash.com/ja/%E5%86%99%E7%9C%9F/0E_vhMVqL9g">Andy Kelly</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Setting Up the Optimal gRPC Development Environment Using Connect-go and VSCode Dev Container]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/08/25/Optimal-development-environment-for-Connect-go</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/08/25/Optimal-development-environment-for-Connect-go</guid>
            <pubDate>Fri, 25 Aug 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Hello there! This is Ueda from the Backend Team at MonstarLab. Recently, in a project I was involved in, the topic of using gRPC came up. So, I went through various trials and errors to quickly set up the optimal development environment. In this article, I'll introduce the method I used.</p>
<ul>
<li>The code is based on the Connect-go tutorial.</li>
<li>We'll build a container development environment using Dev Container,</li>
<li>Introduce Hot Reload using tools like Air</li>
<li>Demonstrate the use of protoc-gen-validate for validation.</li>
</ul>
<p>To focus on setting up the development environment, I'll exclude technical explanations or architecture design related to gRPC and Connect, as well as topics like CI/CD, from this article.
You can find the repository for the sample code <a href="https://github.com/Yukio0315/connect-sample">here</a>.</p>
<h1>Operating Environment</h1>
<p>I have conducted operational tests in the following environment:</p>
<ul>
<li>M1 MacBook Pro (Ventura 13.4.1)</li>
<li>Docker Desktop for Mac Version 4.20.1</li>
<li>VSCode Version 1.79.2</li>
<li>Dev Containers v0.295.0</li>
</ul>
<h1>Technology Stack</h1>
<p>My technology stack includes:</p>
<ul>
<li><a href="https://code.visualstudio.com/docs/devcontainers/containers">VSCode Dev Containers</a></li>
<li><a href="https://go.dev/">Golang</a></li>
<li><a href="https://github.com/cosmtrek/air">Air</a></li>
<li><a href="https://connect.build/docs/go/getting-started">Connect-go</a></li>
<li><a href="https://github.com/bufbuild/protoc-gen-validate">protoc-gen-validate(PGV)</a></li>
</ul>
<h1>Setting Up the Environment</h1>
<p>Personally, I believe there are three key criteria for creating the optimal development environment:</p>
<ol>
<li>Maintaining a certain level of code quality without relying solely on human attention.</li>
<li>Compiling written code quickly to receive immediate feedback.</li>
<li>Allowing anyone to start development promptly.</li>
</ol>
<p>Taking these criteria into account for gRPC development environment, I have selected the following tools and libraries: VSCode Dev Container as the IDE, Golang as the programming language, Connect as the gRPC library, and libraries like Air for achieving Hot Reload. The tools chosen for this setup are considered industry standards and were also selected based on personal preferences after testing and evaluation.</p>
<h1>Setting Up VSCode Dev Container</h1>
<p>I added configurations based on the <a href="https://github.com/devcontainers/templates/tree/main/src/go">Go template</a> created with the "Add Dev Container Configuration Files" command from the Dev Containers extension. To install Go configurations in the container, I set up the container build using a Dockerfile. The other main configuration items are primarily these three:</p>
<ul>
<li>Introducing Features.</li>
<li>Configuring VSCode settings and introducing extensions.</li>
<li>Installing necessary tools for Go development in the Dockerfile.</li>
</ul>
<p>While there are other things I might want to do, such as setting up git pre-commit configurations, I prioritized starting gRPC development and kept the setup minimal.</p>
<h2>Introducing Features</h2>
<h3>Configuring Oh My Zsh</h3>
<p>To establish my shell environment, I have adopted Oh My Zsh. Since people often have their own preferences when it comes to shells, it's best to choose something you like. Here are the steps to introduce Oh My Zsh to the Dev Container:</p>
<h4>Installing Zsh and Oh My Zsh</h4>
<p>By installing the <a href="https://github.com/devcontainers/features/pkgs/container/features%2Fcommon-utils"><strong><code>common-utils</code></strong></a> feature, you can incorporate both Zsh and Oh My Zsh. Configure it within the "features" section in the <strong><code>devcontainer.json</code></strong> file like this:</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
  <span class="hljs-comment">// // Features to add to the dev container. More info: https://containers.dev/features.</span>
  <span class="hljs-attr">"features"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
    <span class="hljs-attr">"ghcr.io/devcontainers/features/common-utils:2"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">"configureZshAsDefaultShell"</span><span class="hljs-punctuation">:</span> <span class="hljs-keyword">true</span>
    <span class="hljs-punctuation">}</span>
  <span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<h4>Setting Zsh as the Default Shell</h4>
<p>You can change the default shell to Zsh by adding the following script to the Dev Container's <strong><code>dockerfile</code>.</strong> Reference: <a href="https://stackoverflow.com/questions/55987337/visual-studio-code-remote-containers-change-shell">stackoverflow</a></p>
<pre><code class="hljs language-Dockerfile"><span class="hljs-keyword">RUN</span><span class="bash"> <span class="hljs-built_in">echo</span> <span class="hljs-string">"if [ -t 1 ]; then"</span> >> /root/.bashrc</span>
<span class="hljs-keyword">RUN</span><span class="bash"> <span class="hljs-built_in">echo</span> <span class="hljs-string">"exec zsh"</span> >> /root/.bashrc</span>
<span class="hljs-keyword">RUN</span><span class="bash"> <span class="hljs-built_in">echo</span> <span class="hljs-string">"fi"</span> >> /root/.bashrc</span>
</code></pre>
<h4>Configuring Oh My Zsh in .zshrc</h4>
<p>Include the configuration for Oh My Zsh in the <strong><code>.zshrc</code></strong> file within the <strong><code>.devcontainer</code></strong> directory. You can use the <a href="https://github.com/ohmyzsh/ohmyzsh/blob/master/templates/zshrc.zsh-template"><strong><code>zsh-template</code></strong></a> from Oh My Zsh for this purpose.</p>
<h4>Copying .zshrc to the Container</h4>
<p>Finally, add the following line to your Dockerfile to copy the <strong><code>.zshrc</code></strong> file to the home directory within the container:</p>
<pre><code class="hljs language-Dockerfile"><span class="hljs-keyword">ADD</span><span class="bash"> .zshrc <span class="hljs-variable">$HOME</span></span>
</code></pre>
<h2>Configuring VSCode Settings and Introducing Extensions</h2>
<p>Now let's dive into configuring VSCode. We'll start with the <strong><code>settings.json</code></strong> configuration, where I will use the official <a href="https://code.visualstudio.com/docs/getstarted/settings#_default-settings">default settings</a>. You can customize these settings to match your personal preferences. When it comes to introducing extensions, this is the part where personal preferences shine through the most. In my case, I went with the following setup. Since these are all well-known extensions, I'll skip detailed descriptions for each. Just remember to add the necessary configurations to your settings files as needed.</p>
<p>In your <strong><code>.devcontainer.json</code></strong>:</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
  <span class="hljs-attr">"customizations"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
    <span class="hljs-attr">"vscode"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">"extensions"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span>
        <span class="hljs-string">"EditorConfig.EditorConfig"</span><span class="hljs-punctuation">,</span>
        <span class="hljs-string">"streetsidesoftware.code-spell-checker"</span><span class="hljs-punctuation">,</span>
        <span class="hljs-string">"wayou.vscode-todo-highlight"</span><span class="hljs-punctuation">,</span>
        <span class="hljs-string">"ms-azuretools.vscode-docker"</span><span class="hljs-punctuation">,</span>
        <span class="hljs-string">"shardulm94.trailing-spaces"</span><span class="hljs-punctuation">,</span>
        <span class="hljs-string">"bungcip.better-toml"</span>
      <span class="hljs-punctuation">]</span>
    <span class="hljs-punctuation">}</span>
  <span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<h2>Configuring Dockerfile for Golang Development</h2>
<p>An essential part of Golang development is the <a href="https://github.com/golang/vscode-go">Go extension</a> from Extensions. Installing this extension is enough to get started with development.</p>
<p>In your <strong><code>.devcontainer.json</code></strong>:</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
  <span class="hljs-comment">// Configure tool-specific properties.</span>
  <span class="hljs-attr">"customizations"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
    <span class="hljs-attr">"vscode"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">"extensions"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-string">"golang.go"</span><span class="hljs-punctuation">]</span>
    <span class="hljs-punctuation">}</span>
  <span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<p>In the Dockerfile, you'll want to install dependencies required by the Go extension, such as gopls and dlv, along with any other necessary tools to complete your Golang development environment.</p>
<p><code>Dockerfile</code></p>
<pre><code class="hljs language-Dockerfile"><span class="hljs-keyword">RUN</span><span class="bash"> go install github.com/cweill/gotests/gotests@latest</span>
<span class="hljs-keyword">RUN</span><span class="bash"> go install github.com/josharian/impl@latest</span>
<span class="hljs-keyword">RUN</span><span class="bash"> go install github.com/go-delve/delve/cmd/dlv@latest</span>
<span class="hljs-keyword">RUN</span><span class="bash"> go install honnef.co/go/tools/cmd/staticcheck@latest</span>
<span class="hljs-keyword">RUN</span><span class="bash"> go install golang.org/x/tools/gopls@latest</span>
</code></pre>
<h1>Setting Up Connect-go Environment</h1>
<p>With the Golang environment set up as described in the previous sections, we can now proceed to set up Connect-go. Essentially, by following the <a href="https://connect.build/docs/go/getting-started/">tutorial</a>, you can easily get Connect-go up and running.</p>
<h2>Introducing Extensions</h2>
<p>For the <strong>Dockerfile</strong>, add the following commands based on the tutorial's instructions:</p>
<p><code>Dockerfile</code></p>
<pre><code class="hljs language-go">RUN <span class="hljs-keyword">go</span> install github.com/bufbuild/buf/cmd/buf@latest
RUN <span class="hljs-keyword">go</span> install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
RUN <span class="hljs-keyword">go</span> install google.golang.org/protobuf/cmd/protoc-gen-<span class="hljs-keyword">go</span>@latest
RUN <span class="hljs-keyword">go</span> install github.com/bufbuild/connect-<span class="hljs-keyword">go</span>/cmd/protoc-gen-connect-<span class="hljs-keyword">go</span>@latest
</code></pre>
<h2>Writing Sample Code</h2>
<p>Following the tutorial, create <strong><code>go.mod</code></strong>, <strong><code>buf.yaml</code></strong>, and <strong><code>buf.gen.yaml</code></strong> files, write your main code, and execute <strong><code>main.go</code></strong> to complete the Connect-go setup.</p>
<h2>Introducing Hot Reload</h2>
<p>To enhance the development environment, we will introduce Hot Reload. Hot Reload requires two steps:</p>
<ol>
<li>Automatic code generation.</li>
<li>Hot Reload for Golang code.</li>
</ol>
<h3>Automatic Code Generation</h3>
<p>For automatic code generation, utilize the <strong><code>buf generate</code></strong> command upon saving proto files. You can achieve this using the "Run on Save" <a href="https://github.com/emeraldwalk/vscode-runonsave">extension</a>. Add <strong><code>"emeraldwalk.RunOnSave"</code></strong> to your <strong><code>devcontainer.json</code></strong> and configure it in <strong><code>.vscode/settings.json</code></strong>:</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
  <span class="hljs-comment">// Run on Save</span>
  <span class="hljs-comment">// proto-gen</span>
  <span class="hljs-attr">"emeraldwalk.runonsave"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
    <span class="hljs-attr">"commands"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span>
      <span class="hljs-punctuation">{</span>
        <span class="hljs-attr">"match"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"\\.proto$"</span><span class="hljs-punctuation">,</span>
        <span class="hljs-attr">"cmd"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"rm -rf gen | buf generate"</span>
      <span class="hljs-punctuation">}</span>
    <span class="hljs-punctuation">]</span>
  <span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<p>This <strong><code>cmd</code></strong> is used to remove all files within the <strong><code>gen</code></strong> directory. This precaution is taken because the <strong><code>buf generate</code></strong> command might not remove inadvertently generated files.</p>
<p>As the scale of your project increases, I think I will need to reconsider it and potentially implement a more refined approach.</p>
<h3>Introducing Golang's Hot Reload with Air</h3>
<p>For Golang's Hot Reload, we've introduced the use of <a href="https://github.com/cosmtrek/air">Air</a>. There are various ways to install it, but by adding <strong><code>RUN go install github.com/cosmtrek/air@latest</code></strong> to your Dockerfile, the setup is complete. The configuration file needs to be set up as well. Running the <strong><code>air init</code></strong> command within the container will generate the <strong><code>.air.toml</code></strong> file, which you can modify based on the <a href="https://github.com/cosmtrek/air/blob/master/air_example.toml"><strong><code>.air_sample.toml</code></strong></a>, adapting it for your project's structure.</p>
<p>In this setup, we've set <strong><code>delay=500</code></strong>. <em>This delay is important</em> because without it, when <strong><code>RunOnSave</code></strong> operates, it might not be able to read files under the <strong><code>gen</code></strong> directory.</p>
<p>Once the Hot Reload configuration is in place, make sure Air automatically starts after the container is up. Use the <strong><code>postStartCommand</code></strong> in your <strong><code>devcontainer.json</code></strong>:</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
  <span class="hljs-attr">"postStartCommand"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"air -c .air.toml"</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<p>With these steps, your Hot Reload configuration is now fully set up.</p>
<h2>Adding Request Validation Feature</h2>
<p>Now, let's move on to adding the request validation feature, something that would be useful in practical usage. We'll use <a href="https://github.com/bufbuild/protoc-gen-validate">protoc-gen-validate</a> (PGV) for Connect's validation.</p>
<p>Add <strong><code>RUN go install github.com/envoyproxy/protoc-gen-validate@latest</code></strong> to your Dockerfile. Then, as documented, add the necessary configurations to <strong><code>buf.gen.yaml</code></strong> and <strong><code>buf.yaml</code></strong>. Be careful not to forget adding <strong><code>opt</code></strong> according to your environment's needs.</p>
<p><code>buf.gen.yaml</code></p>
<pre><code class="hljs language-yaml"><span class="hljs-attr">plugins:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">plugin:</span> <span class="hljs-string">buf.build/bufbuild/validate-go</span>
    <span class="hljs-attr">out:</span> <span class="hljs-string">gen</span>
    <span class="hljs-attr">opt:</span> <span class="hljs-string">paths=source_relative</span>
</code></pre>
<p><code>buf.yaml</code></p>
<pre><code class="hljs language-yaml"><span class="hljs-attr">deps:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">buf.build/envoyproxy/protoc-gen-validate</span>
</code></pre>
<p>After this, run the <strong><code>buf mod update</code></strong> command to generate the <strong><code>buf.gen.yaml</code></strong> file. Be aware that automatically running <strong><code>bef generate</code></strong> might prevent you from receiving error messages. If things don't work smoothly, manually running the commands is recommended.</p>
<p>Now, let's implement validation. Import <strong><code>validate</code></strong> in your <strong><code>greet.proto</code></strong> file and modify the validation rules for <strong><code>GreetRequest</code></strong>, like so:</p>
<p><code>greet/v1/greet.proto</code></p>
<pre><code class="hljs language-proto">syntax = "proto3";

package greet.v1;

import "validate/validate.proto";

option go_package= "connect-sample/gen/greet/v1;greetv1";

message GreetRequest {
  string name = 1 [(validate.rules).string = {min_len: 5, max_len: 10}];
}

message GreetResponse {
  string greeting = 1;
}

service GreetService {
  rpc Greet(GreetRequest) returns (GreetResponse) {}
}

</code></pre>
<p>Running the <strong><code>buf generate</code></strong> command will generate the <strong><code>gen.pb.validate.go</code></strong> file. Upon inspecting its contents, you'll notice that the <strong><code>GreetRequest</code></strong> type struct includes methods like <strong><code>Validate</code></strong> and <strong><code>ValidateAll</code></strong>. You can use these methods. For instance, you can add validation like this:</p>
<pre><code class="hljs language-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *GreetServer)</span></span> Greet(
 ctx context.Context,
 req *connect.Request[greetv1.GreetRequest],
) (*connect.Response[greetv1.GreetResponse], <span class="hljs-type">error</span>) {
	<span class="hljs-keyword">if</span> err := req.Msg.Validate(); err != <span class="hljs-literal">nil</span> {
	  <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
	}
	log.Println(<span class="hljs-string">"Request headers: "</span>, req.Header())
	res := connect.NewResponse(&#x26;greetv1.GreetResponse{
		Greeting: fmt.Sprintf(<span class="hljs-string">"Hello, %s!"</span>, req.Msg.Name),
	})
	res.Header().Set(<span class="hljs-string">"Greet-Version"</span>, <span class="hljs-string">"v1"</span>)
	<span class="hljs-keyword">return</span> res, <span class="hljs-literal">nil</span>
}

</code></pre>
<p>With this, the validation setup is complete.</p>
<h1>Testing the Setup</h1>
<p>You can test the setup using the following three commands:</p>
<pre><code class="hljs language-shell">git clone https://github.com/Yukio0315/connect-sample

devcontainer open # if devcontainer CLI installed

root ➜ /workspaces/connect-sample (main) $ go run ./cmd/client/main.go
YYYY/MM/DD hh:mm:ss unknown: invalid GreetRequest.Name: value length must be between 5 and 10 runes, inclusive
</code></pre>
<p>With these steps, you should see that the server runs without issues and the validation is working as expected.</p>
<h1>Conclusion</h1>
<p>This article has explained how to set up the optimal gRPC development environment using Connect-go within a Dev Container, all in a speedy manner. By incorporating features like linting and hot reload, I have been able to create an environment that's fairly ideal for development. While there might still be areas that need improvement, I have managed to establish the essential environment needed to kickstart development. I hope this article proves helpful to those involved in gRPC development and beyond.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/aZjw7xI3QAA">Simon Berger</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to use SwiftUI in a UIKit app]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/08/03/SwiftUI-in-UIKit</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/08/03/SwiftUI-in-UIKit</guid>
            <pubDate>Thu, 03 Aug 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>SwiftUI was initially announced 4 years ago, and last year Apple said clearly that the best way to build an app is with Swift and SwiftUI. However, some of us are still working on codebases that were started in UIKit more than 4-5 years ago and that is possibly holding us back from adopting SwiftUI. But as long as you target iOS 13 or above, there are ways to use SwiftUI even if you have a UIKit project. The way to do it depends on what exactly you want to build in SwiftUI: is it a full screen? A view to be shown inside a UIKit screen? Or do you have a table or collection view cell that you want to build in SwiftUI?</p>
<p><img src="/assets/img/articles/2023-07-24-SwiftUI-in-UIKit/best.webp" alt="">
<em>Capture from WWDC 2022 Session 102 Platforms State of the Union, 29:20</em></p>
<h2>Whole SwiftUI screen</h2>
<p>Let's say you have an existing app, where you need to build a new feature that is its own separate screen. That's a perfect candidate to do it using SwiftUI. For that, we use a <code>UIHostingController</code>. So first, we build the new screen in SwiftUI. Here's a very simple example of a static screen:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> SwiftUI

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">SwiftUIScreen</span>: <span class="hljs-title class_">View</span> {
    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
        <span class="hljs-type">VStack</span>(spacing: <span class="hljs-number">24</span>) {
            <span class="hljs-type">Text</span>(<span class="hljs-string">"Hello, from SwiftUI!"</span>)
            <span class="hljs-type">Image</span>(systemName: <span class="hljs-string">"globe"</span>)
        }
    }
}
</code></pre>
<p>This gives the following view when shown on the whole screen:
<img src="/assets/img/articles/2023-07-24-SwiftUI-in-UIKit/1.webp" alt=""></p>
<p>Then we need to push or present or show in whatever way the SwiftUI screen that we just built, from UIKit, using a <code>UIHostingController</code>. In our case, I have a very simple <code>UIViewController</code>, in which I have added a <code>UIButton</code> which presents this screen modally.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> UIKit
<span class="hljs-keyword">import</span> SwiftUI

<span class="hljs-keyword">class</span> <span class="hljs-title class_">ViewController</span>: <span class="hljs-title class_">UIViewController</span> {

    <span class="hljs-keyword">override</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">viewDidLoad</span>() {
        <span class="hljs-keyword">super</span>.viewDidLoad()
        <span class="hljs-comment">// Do any additional setup after loading the view.</span>
    }

    <span class="hljs-keyword">@IBAction</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">openSwiftUIScreen</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">sender</span>: <span class="hljs-keyword">Any</span>) {
        <span class="hljs-keyword">let</span> vc <span class="hljs-operator">=</span> <span class="hljs-type">UIHostingController</span>(rootView: <span class="hljs-type">SwiftUIScreen</span>())
        present(vc, animated: <span class="hljs-literal">true</span>)
    }
}
</code></pre>
<p>That was one simple way to integrate a SwiftUI screen in a UIKit app.</p>
<p>Of course, in a real-life project, things are not as simple as here, we normally have more dependencies that we need to inject, possibly a ViewModel depending on the choice of architecture. However, the basics are the same: in your UIKit code, instantiate your SwiftUI screen, embed it into a <code>UIHostsingController</code> (which inherits from <code>UIViewController</code>) and then present or push or do what needs to be done with that hosting controller.</p>
<h2>Smaller SwiftUI view</h2>
<p>But what if we want to only build a small view in SwiftUI and integrate it in a <code>UIViewController</code>, as opposed to a whole SwiftUI screen? We can use the same technique, with <code>UIHostingController</code>, because a <code>UIViewController</code> doesn't have to cover the whole screen. So what we're doing basically is adding a child view controller to host the SwiftUI view. Here's how we can do that.</p>
<p>Here I have <code>MixedViewController</code> which is a <code>UIViewController</code>, in which I add a <code>UIStackView</code> that covers the whole screen. In the stack view, I add 2 arranged subviews: a <code>UILabel</code>, and the same SwiftUI View that we used above (<code>SwiftUIScreen</code>).</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> UIKit
<span class="hljs-keyword">import</span> SwiftUI

<span class="hljs-keyword">class</span> <span class="hljs-title class_">MixedViewController</span>: <span class="hljs-title class_">UIViewController</span> {
    
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> stackView <span class="hljs-operator">=</span> <span class="hljs-type">UIStackView</span>()
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> label <span class="hljs-operator">=</span> <span class="hljs-type">UILabel</span>()
    
    <span class="hljs-keyword">override</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">viewDidLoad</span>() {
        <span class="hljs-keyword">super</span>.viewDidLoad()

        view.backgroundColor <span class="hljs-operator">=</span> .systemBackground
        setupStackView()

        label.text <span class="hljs-operator">=</span> <span class="hljs-string">"This is a UILabel"</span>
        label.textAlignment <span class="hljs-operator">=</span> .center
        stackView.addArrangedSubview(label)
        
        <span class="hljs-keyword">let</span> swiftUIController <span class="hljs-operator">=</span> <span class="hljs-type">UIHostingController</span>(rootView: <span class="hljs-type">SwiftUIScreen</span>())
        addChild(swiftUIController)
        stackView.addArrangedSubview(swiftUIController.view)
        swiftUIController.didMove(toParent: <span class="hljs-keyword">self</span>)
    }
    
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">setupStackView</span>() {
        stackView.axis <span class="hljs-operator">=</span> .vertical
        stackView.distribution <span class="hljs-operator">=</span> .fillEqually
        
        stackView.translatesAutoresizingMaskIntoConstraints <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>
        view.addSubview(stackView)
        <span class="hljs-type">NSLayoutConstraint</span>.activate([
            stackView.topAnchor.constraint(equalTo: view.topAnchor),
            stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            stackView.rightAnchor.constraint(equalTo: view.rightAnchor),
        ])
    }
}
</code></pre>
<p>As you can see in the code above, the way to embed a SwiftUI view in a <code>UIViewController</code> is very similar to embedding a child view controller in a <code>UIViewController</code>. In fact, it's exactly the same, because we embed the SwiftUI view in a <code>UIHostingController</code>, which inherits from <code>UIViewController</code>.
Here's what our <code>MixedViewController</code> looks like when running the app.
<img src="/assets/img/articles/2023-07-24-SwiftUI-in-UIKit/2.webp" alt=""></p>
<h2>SwiftUI in table or collection cells</h2>
<p>If you target iOS 16 or above, there's also another way of adding SwiftUI in a UITableViewCell or UICollectionViewCell.
Apple introduced <code>UIHostingConfiguration</code>, which allows developers to declare the contents of a UITableViewCell or UICollectionViewCell inline, using SwiftUI. Here's a simple example to show this:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> UIKit
<span class="hljs-keyword">import</span> SwiftUI

<span class="hljs-keyword">class</span> <span class="hljs-title class_">TableViewController</span>: <span class="hljs-title class_">UITableViewController</span> {

    <span class="hljs-keyword">override</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">viewDidLoad</span>() {
        <span class="hljs-keyword">super</span>.viewDidLoad()

        tableView.register(<span class="hljs-type">UITableViewCell</span>.<span class="hljs-keyword">self</span>, forCellReuseIdentifier: <span class="hljs-string">"reuseIdentifier"</span>)
    }

    <span class="hljs-comment">// MARK: - Table view data source</span>

    <span class="hljs-keyword">override</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">numberOfSections</span>(<span class="hljs-params">in</span> <span class="hljs-params">tableView</span>: <span class="hljs-type">UITableView</span>) -> <span class="hljs-type">Int</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>
    }

    <span class="hljs-keyword">override</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">tableView</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">tableView</span>: <span class="hljs-type">UITableView</span>, <span class="hljs-params">numberOfRowsInSection</span> <span class="hljs-params">section</span>: <span class="hljs-type">Int</span>) -> <span class="hljs-type">Int</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-number">4</span>
    }
    
    <span class="hljs-keyword">override</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">tableView</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">tableView</span>: <span class="hljs-type">UITableView</span>, <span class="hljs-params">cellForRowAt</span> <span class="hljs-params">indexPath</span>: <span class="hljs-type">IndexPath</span>) -> <span class="hljs-type">UITableViewCell</span> {
        <span class="hljs-keyword">let</span> cell <span class="hljs-operator">=</span> tableView.dequeueReusableCell(withIdentifier: <span class="hljs-string">"reuseIdentifier"</span>, for: indexPath)

        cell.contentConfiguration <span class="hljs-operator">=</span> <span class="hljs-type">UIHostingConfiguration</span> {
            <span class="hljs-type">HStack</span> {
                <span class="hljs-type">Text</span>(<span class="hljs-string">"SwiftUI cell - Row <span class="hljs-subst">\(indexPath.row)</span>"</span>)
                <span class="hljs-type">Spacer</span>()
                <span class="hljs-type">Image</span>(systemName: <span class="hljs-string">"globe"</span>)
            }
        }

        <span class="hljs-keyword">return</span> cell
    }
    
}
</code></pre>
<p>And here's the screen with a <code>UITableView</code> with SwiftUI content in the <code>UITableViewCell</code>s if we run it:</p>
<p><img src="/assets/img/articles/2023-07-24-SwiftUI-in-UIKit/3.webp" alt=""></p>
<p>And those are three ways to use SwiftUI in UIKit: either use a <code>UIHostingController</code> initialised with a SwiftUI root view and show that full screen or as a child view controller, or if you can target iOS 16 or above, use a <code>UIHostingConfiguration</code> to add collection view or table view cells content inline using SwiftUI.</p>
<p>Please note that all the code examples in this blog post are meant to be as concise and to the point as possible, illustrating how to embed SwiftUI content in UIKit. Don't use it as best practice guidelines for anything else because the main and only point they have was to show how to use SwiftUI in UIKit.</p>
<p>And just because I'm showing how to use SwiftUI in a UIKit project doesn't mean UIKit is obsolete. For example, it's <a href="https://developer.apple.com/videos/play/wwdc2023/111215/">still a thing in visionOS</a>.</p>
<p><em>Article photo by <a href="https://unsplash.com/@varpap">Vardan Papikyan</a> on <a href="https://unsplash.com/photos/JzE1dHEaAew">Unsplash</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Empowering Mobile Applications through Blockchain: Our Journey, Part 2]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/08/01/Empowering-Mobile-Applications-through-Blockchain-Our-Journey-Part-2</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/08/01/Empowering-Mobile-Applications-through-Blockchain-Our-Journey-Part-2</guid>
            <pubDate>Tue, 01 Aug 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Welcome back to our blog series on blockchain technology! In our previous post, we explored the fundamentals of blockchain and its potential impact on various industries. If you haven't read it yet, you can find it <a href="https://engineering.monstar-lab.com/en/post/2023/07/12/Empowering-Mobile-Applications-through-Blockchain-Our-Journey-Part-1/">here</a>. Today, we're excited to delve deeper into the practical implementation of blockchain by discussing the development process of our prototype mobile application. The focus of this app is to provide users with a secure and transparent way to verify their pension and tax records using blockchain technology.</p>
<p>The main features of the app are:</p>
<ul>
<li>User authentication using a QR code or user id</li>
<li>Logout process</li>
<li>Show tax and pension details such as the paid amount, timestamp and a note with relevant information about the records made in the Blockchain.</li>
</ul>
<h1>App + Blockchain development</h1>
<p>By integrating blockchain into mobile apps, businesses and organizations are paving the way for a new method of handling their data. Using the core features of blockchain, such as decentralization and immutability, enables a more transparent and secure environment for recording digital transactions and managing digital assets.</p>
<p>In the current landscape, there are various methods and technologies available to connect mobile applications to blockchains. Here are some common approaches:</p>
<ul>
<li>SDKs provide developers with pre-built libraries and tools to integrate blockchain functionality into their mobile apps.</li>
<li>APIs allow mobile apps to communicate with blockchain networks and access their functionalities. For example, the Coinbase API facilitates interaction with blockchain networks.</li>
<li>Integration with Web3 wallets, such as MetaMask, enables seamless interaction with blockchain networks.</li>
</ul>
<p>Currently, there are functional open-source libraries available for integration into mobile apps, regardless of whether they are natively developed or built using cross-platform technologies like Flutter. Here are some popular options:</p>
<ul>
<li>For iOS, <a href="https://github.com/web3swift-team/web3swift">Web3swift</a> provides the necessary tools to connect to smart contracts and execute transactions directly from the app.</li>
<li>For Android, <a href="https://github.com/web3j/web3j">Web3j</a> is a Java library that enables Android developers to interact with Ethereum blockchains. It offers features such as wallet creation, transaction handling, and smart contract invocation.</li>
<li>In the case of Flutter, <a href="https://pub.dev/packages/web3dart">web3dart</a> is a library that allows developers to interact with Ethereum blockchains. It offers functionalities such as wallet creation, transaction handling, and smart contract interaction within Flutter apps.</li>
</ul>
<h1>What can be done in mobile apps that connect to blockchain?</h1>
<p>Here are some of the most common operations carried out in a mobile application that connects to a blockchain:</p>
<ul>
<li>
<p><strong>Wallet Management</strong>: Apps connected to blockchains often include functionality for managing digital wallets. Users can create wallets, store their cryptographic keys securely, and manage their digital assets, such as cryptocurrencies or tokens.</p>
</li>
<li>
<p><strong>Transaction Processing</strong>: Apps can facilitate the initiation and processing of blockchain transactions. Users can send or receive digital assets and initiate payments from their mobile devices.</p>
</li>
<li>
<p><strong>Asset Tracking</strong>: Apps connected to blockchains can provide real-time tracking and monitoring of digital assets. Users can view the transaction history, balance, and ownership information of their assets on the blockchain.</p>
</li>
<li>
<p><strong>Smart Contract Interaction</strong>: Apps can enable interaction with smart contracts deployed on the blockchain. Users can invoke functions, participate in decentralized applications (DApps), and interact with automated contract logic.</p>
</li>
</ul>
<p>In this article, we talk about a mobile application developed mainly to interact with Smart contracts.</p>
<h1>Blockchain development concepts</h1>
<p>Before going through the details of the development, let's become familiar with some concepts that we should understand for better comprehension.</p>
<h3>Polygon network</h3>
<p>The Polygon network acts as a scaling solution or a Layer 2 solution for Ethereum (check the picture below). It provides additional infrastructure and technology that helps alleviate the congestion and high fees on the Ethereum network. Transactions and activities can be offloaded from the Ethereum mainnet to the Polygon network, where they can be processed faster and with lower fees.</p>
<figure>
<img src="/assets/img/articles/2023-08-01-Empowering-Mobile-Applications-through-Blockchain-Our-Journey-Part-2/image1.webp">
<figcaption>Layer 2 solution representation, where orange part is a Layer 2 solution and the green part is the mainnet. Source: https://www.globalxetfs.com.au/scaling-blockchains-what-are-layer-2-solutions-and-interoperable-chains/</figcaption>
</figure>
<p>The web-based platform called <strong>Polygon Scan</strong> is used to explore and monitor activities on the Polygon network.</p>
<p>As an interesting fact, Polygon is an <a href="https://polygon.technology/blog/polygon-the-eco-friendly-blockchain-scaling-ethereum">“Eco-friendly blockchain”</a>, especially compared to the heavily energy-consuming Bitcoin blockchain network.</p>
<p>For the project described in this article, we used Polygon Network to store records. Please refer to the smart contract development section in our last <a href="https://engineering.monstar-lab.com/en/post/2023/07/12/Empowering-Mobile-Applications-through-Blockchain-Our-Journey-Part-1/">article</a> for a deeper understanding.</p>
<h3>Testnet</h3>
<p>Testnets are separate blockchain networks that developers and users can use to test and experiment with applications, smart contracts, and other blockchain functionalities without using real cryptocurrencies or interacting with the mainnet (The live, production version of the blockchain).</p>
<p>In this project, we used Mumbai Testnet which is accessible through the <a href="https://mumbai.polygonscan.com/">PolygonScan platform</a></p>
<h3>Faucet</h3>
<p>In Web3, faucet is a service or mechanism that provides users with free tokens or cryptocurrencies for testing and development purposes. It is commonly used on testnet networks to distribute tokens that hold no real-world value but can be used for experimenting with blockchain applications.</p>
<p>In the context of a faucet, a transaction refers to the transfer of cryptocurrency from the faucet to a user's wallet.</p>
<p>For this project, we acquired free tokens from the following Faucets:</p>
<ul>
<li><a href="https://faucet.polygon.technology/">Polygon Faucet</a></li>
<li><a href="https://chainlist.org/?search=Polygon&#x26;testnets=true">Chainlist</a></li>
</ul>
<p>The following is an example of a transaction from a faucet to an account created in Metamask. The only required parameter was the Wallet address which is taken from the Metamask extension.</p>
<figure>
<img src="/assets/img/articles/2023-08-01-Empowering-Mobile-Applications-through-Blockchain-Our-Journey-Part-2/image2.webp">
<figcaption>Faucet transaction</figcaption>
</figure>
<p>The web-based platform called <strong>Polygon Scan</strong> is used to explore and monitor activities on the Polygon network.</p>
<p>For the project described in this article, we used Polygon Network to store records. Please refer to section about the smart contract development.</p>
<h3>Metamask</h3>
<p>For the project described in this article, we created a Wallet in <a href="https://metamask.io/">Metamask</a>. Metamask functions as a bridge between the user and the blockchain network by a browser extension.</p>
<figure>
<img src="/assets/img/articles/2023-08-01-Empowering-Mobile-Applications-through-Blockchain-Our-Journey-Part-2/image3.webp">
<figcaption>Transaction details in Metamask</figcaption>
</figure>
<p>The steps we carried out to set up the wallet in Metamask were the following:</p>
<ul>
<li>Install the Metamask browser extension.</li>
<li>Follow the steps to generate the Password and the Secret Recovery Phrase.</li>
<li>Create an account in the Metamask menu.</li>
</ul>
<h1>Smart Contract Development</h1>
<p>The purpose of the smart contract developed for this project is to securely store records of pension and tax transactions on the blockchain. The final deployed Smart Contract can be found <a href="https://mumbai.polygonscan.com/address/0x22112abffaa55876d6ef203d1f3a5f2465c69be6#code">here</a>.</p>
<h3>Key Features</h3>
<ul>
<li>
<p>This smart contract acts as a decentralized ledger, ensuring the immutability and transparency of pension and tax records.</p>
</li>
<li>
<p>Ability to add records to the blockchain by government officials who possess access to user identifiers.</p>
</li>
<li>
<p>The smart contract provides a method that allows the mobile application to invoke and retrieve only the records that belong to a specific user, ensuring privacy and personalized access.</p>
</li>
</ul>
<h3>Development and Deployment</h3>
<p>These were the steps followed in the development and deployment process of the smart contract.</p>
<ol>
<li>
<p><strong>Requirement Analysis</strong>: We gathered and analyzed the requirements for the smart contract, such as the data to be stored, the record structure, user identification, and access control taking into account the app specifications as well.</p>
</li>
<li>
<p><strong>Solidity Contract Development</strong>: We wrote the smart contract using the Solidity programming language. Defined the necessary variables and functions to implement the desired functionality.</p>
<ul>
<li>Variables
<ul>
<li>contributionType (uint8)</li>
<li>recordNumber (uint)</li>
<li>amount (uint)</li>
<li>timestamp (uint)</li>
<li>note (string)</li>
</ul>
</li>
<li>Functions
<ul>
<li>Function to add a single record</li>
<li>Function to retrieve all the records for a specific User</li>
<li>Function to retrieve the number the records for a specific User</li>
</ul>
</li>
</ul>
</li>
<li>
<p><strong>Remix Integration</strong>: We used Remix, an open-source tool, to develop and test the smart contract directly from the browser. Remix provides a convenient environment for writing, compiling, and debugging Solidity contracts.</p>
</li>
<li>
<p><strong>Testing</strong>: Before deploying, we use Remix to test the smart contract locally.</p>
</li>
<li>
<p><strong>Create a wallet in Metamask</strong>: In order to start interacting with a blockchain network, we created a wallet in Metamask by following the required steps to set up the password and seed phrase for authentication access. Additionally, we created a couple of accounts to verify transactions and manage the token funds.</p>
</li>
<li>
<p><strong>Token Acquisition</strong>: Then, we acquired free tokens from the Faucet at <a href="https://faucet.polygon.technology/">Polygon Faucet</a> for deploying and interacting with a smart contract on a Testnet.</p>
</li>
<li>
<p><strong>Deployment on Mumbai Testnet</strong>: We deployed the smart contract on the Mumbai Testnet provided by the PolygonScan platform. The deployed smart contract's address is <a href="https://mumbai.polygonscan.com/address/0x22112abffaa55876d6ef203d1f3a5f2465c69be6#code">here</a>.</p>
</li>
</ol>
<h3>Smart Contract Response Structure</h3>
<p>The deployed smart contract includes a method called getRecordsForUser, which is invoked from the mobile application to retrieve Pension and Tax records. The method returns a dictionary-type object with two keys: "0" and "_success". The key "0" contains an array of tuples, as shown in the example below:</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">[</span>
   <span class="hljs-attr">"0"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">[</span>
      <span class="hljs-punctuation">[</span>
         <span class="hljs-number">2</span><span class="hljs-punctuation">,</span>
         <span class="hljs-number">0</span><span class="hljs-punctuation">,</span>
         <span class="hljs-number">13200</span><span class="hljs-punctuation">,</span>
         <span class="hljs-number">1682317471</span><span class="hljs-punctuation">,</span>
         <span class="hljs-string">"税金2022年11月。納付"</span>
      <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span>
      <span class="hljs-punctuation">[</span>
         <span class="hljs-number">2</span><span class="hljs-punctuation">,</span>
         <span class="hljs-number">1</span><span class="hljs-punctuation">,</span>
         <span class="hljs-number">13200</span><span class="hljs-punctuation">,</span>
         <span class="hljs-number">1682416499</span><span class="hljs-punctuation">,</span>
         <span class="hljs-string">"税金2022年5月。納付"</span>
      <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span>
      <span class="hljs-punctuation">[</span>
         <span class="hljs-number">1</span><span class="hljs-punctuation">,</span>
         <span class="hljs-number">17</span><span class="hljs-punctuation">,</span>
         <span class="hljs-number">6000</span><span class="hljs-punctuation">,</span>
         <span class="hljs-number">1682416853</span><span class="hljs-punctuation">,</span>
         <span class="hljs-string">"国民年金2023年4月。納付"</span>
      <span class="hljs-punctuation">]</span>
   <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span>
   <span class="hljs-attr">"_success"</span><span class="hljs-punctuation">:</span><span class="hljs-keyword">true</span>
<span class="hljs-punctuation">]</span>
</code></pre>
<p>Each tuple corresponds to a record. The items included are as follows:</p>
<ol>
<li>Contribution type: 1 for Pension, 2 for Taxes</li>
<li>Record Number: The record number for this specific user</li>
<li>Amount: The amount of this record</li>
<li>Timestamp: The timestamp when the record was added</li>
<li>Note: A relevant note regarding the record added to the blockchain.</li>
</ol>
<h1>Android App Development</h1>
<h3>Application Overview</h3>
<p>This Native Android app allows the user to “log in” which entails storing a valid GUID which exists on the Manekin Contract in the application. The application then queries the contract for the transactions associated with that GUID, and displays them to the user in categories. Querying the contract is a read-only operation and as such does not require cryptocurrency.</p>
<figure>
<img src="/assets/img/articles/2023-08-01-Empowering-Mobile-Applications-through-Blockchain-Our-Journey-Part-2/image4.webp">
<figcaption>Android Mobile Application screens</figcaption>
</figure>
<p>To interact with the Smart Contract, we utilized the <a href="https://github.com/web3j/web3j">Web3j Library</a> for Android. It is by far the most popular and most maintained project allowing access to multiple blockchains.</p>
<h3>Other Libraries used</h3>
<p><strong>Firebase Remote Config</strong>
In case the testnet changes in the future, the RPC URL and Contract address could be modified remotely, without the need to re-release the app if that information was hard coded.</p>
<p><strong>AndroidX Camera</strong>
In order to feed information to the barcode reader, and display a camera preview.</p>
<p><strong>Google MLKit Barcode Scanner</strong>
To recognize the QR code used to log in, and read the data inside.</p>
<p><strong>Google Accompanist</strong>
To handle the camera permissions with ease.</p>
<h3>Generating the Java Smart Contract Wrapper</h3>
<p>First, it was necessary to download the contract’s ABI from the Manekin Smart Contract page.</p>
<p>Then, we installed the <a href="https://docs.web3j.io/4.10.0/command_line_tools/">Web3j Command Line Tools</a> (4.10.0 at time of writing).</p>
<p>After that, the Wrapper was generated with the following command, and placed in our <code>data/model</code> folder:</p>
<pre><code class="hljs language-shell">web3j generate solidity -a &#x3C;abi_file> -o &#x3C;output_folder> -p &#x3C;java_package_name>
</code></pre>
<h3>Accessing the Polygon Mumbai Testnet</h3>
<ol>
<li>Define the RPC URL and Contract Address. As mentioned previously, we stored and subsequently then retrieved them from Firebase Remote Config:</li>
</ol>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">val</span> remoteConfigRepository: RemoteConfigRepository =
            (application <span class="hljs-keyword">as</span> App).remoteConfigRepository

<span class="hljs-keyword">val</span> rpcUrl = remoteConfigRepository.getRPCUrl().<span class="hljs-keyword">data</span> ?: <span class="hljs-string">""</span>
<span class="hljs-keyword">val</span> contractAddress = remoteConfigRepository.getContractAddress().<span class="hljs-keyword">data</span> ?: <span class="hljs-string">""</span>
</code></pre>
<ol start="2">
<li>Initialize the web3j Library using the RPC URL:</li>
</ol>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">val</span> web3j: Web3j = Web3j.build(HttpService(rpcUrl))
</code></pre>
<ol start="3">
<li>Initialize a read-only Transaction Manager to retrieve the transactions, using the previous webj object:</li>
</ol>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">val</span> readonlyTransactionManager = ReadonlyTransactionManager(web3j, <span class="hljs-string">"0x0000000000000000000000000000000000000000"</span>)
</code></pre>
<p>Note that Web3j needs a wallet address, even for read only operations, so we have used a Dummy Address.</p>
<ol start="4">
<li>Load the Manekin Contract Wrapper, which was previously generated with the Web4j Command Line tools:</li>
</ol>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">val</span> contract: ManekinContract =
            ManekinContract.load(
                contractAddress,
                web3j,
                readonlyTransactionManager,
                DefaultGasProvider()
            )
</code></pre>
<ol start="5">
<li>Retrieve the records from our contract, using the function which Web4j generated:</li>
</ol>
<pre><code class="hljs language-kotlin">Retrieve the records from our contract, using the function which Web4j generated :
</code></pre>
<p>It is assumed that <code>userIdentifier</code> has been defined or passed in, and is the GUID representing a user’s data.</p>
<h1>iOS App Development</h1>
<h3>Application Overview</h3>
<p>This is a native mobile application designed to allow users to verify their pension and tax records stored on the blockchain. The process of adding records to the blockchain is not executed in the application, but rather manually, instead this can be done in the <a href="https://mumbai.polygonscan.com/address/0x22112abffaa55876d6ef203d1f3a5f2465c69be6#writeContract">PolygonScan</a> console by calling the method <em>writeContract</em>.</p>
<figure>
<img src="/assets/img/articles/2023-08-01-Empowering-Mobile-Applications-through-Blockchain-Our-Journey-Part-2/image5.webp">
<figcaption>iOS Mobile Application screens</figcaption>
</figure>
<p>To interact with the smart contract, we utilized the <strong>Web3Swift</strong> library, which is a swift library for iOS designed for seamless integration with Ethereum's smart contracts on the blockchain. You can find the library <a href="https://github.com/web3swift-team/web3swift">here</a>.</p>
<p>We opted for this library due to its popularity, ease of integration into Swift projects, and its active maintenance. Furthermore, Web3swift offers extensive support for various smart contract operations, making it suitable for the development of complex Web3-related projects.</p>
<h3>Parameters required</h3>
<p>To instantiate a smart contract deployed on the Mumbai testnet of Polygon, we needed three pieces of information:</p>
<ul>
<li>
<p>RPC Server Address: Mumbai Tested has multiple addresses, allowing us to choose the most convenient one. For further details, refer to <a href="https://chainlist.org/">Chainlist</a>.</p>
</li>
<li>
<p>Chain ID: Mumbai Tesnet's chain ID is 80001. For more information, visit <a href="https://chainlist.org/">Chainlist</a>.</p>
</li>
<li>
<p>Contract Address: This refers to the hash that is embedded in the smart <a href="https://mumbai.polygonscan.com/address/0x22112abffaa55876d6ef203d1f3a5f2465c69be6">contract URL</a>.</p>
</li>
<li>
<p>Contract ABI: This JSON file contains the smart contract's details and can be downloaded from the polygonscan platform.</p>
</li>
</ul>
<h3>Steps involved in retrieving records from the blockchain</h3>
<ol>
<li>Create a Web3 object by using RPC Server Address and Mumbai Tesnet's chain ID</li>
</ol>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> mumbaiEndpoint <span class="hljs-operator">=</span> <span class="hljs-type">URL</span>(string: rpcUrl)
<span class="hljs-keyword">let</span> mumbaiNetworkId <span class="hljs-operator">=</span> <span class="hljs-number">0x13881</span>

<span class="hljs-keyword">var</span> provider: <span class="hljs-type">Web3Provider</span>

<span class="hljs-keyword">do</span> {
    provider <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-keyword">await</span> <span class="hljs-type">Web3HttpProvider</span>(
        url: mumbaiEndpoint,
        network: <span class="hljs-type">Networks</span>.fromInt(<span class="hljs-type">UInt</span>(mumbaiNetworkId)))
} <span class="hljs-keyword">catch</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-type">TaskResult</span>.failure(<span class="hljs-type">AppError</span>.providerGenerateError)
}

<span class="hljs-keyword">let</span> web3 <span class="hljs-operator">=</span> <span class="hljs-type">Web3</span>(provider: provider)
</code></pre>
<ol start="2">
<li>Retrieve the locally stored JSON abi file as String.</li>
</ol>
<pre><code class="hljs language-swift"><span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> contractABIURL <span class="hljs-operator">=</span> <span class="hljs-type">Bundle</span>.main.url(forResource: <span class="hljs-string">"contract_abi"</span>, withExtension: <span class="hljs-string">"json"</span>) <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-type">TaskResult</span>.failure(<span class="hljs-type">AppError</span>.abiBundleError)
}

<span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> contractABIString <span class="hljs-operator">=</span> <span class="hljs-keyword">try?</span> <span class="hljs-type">String</span>(contentsOf: contractABIURL, encoding: .utf8) <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-type">TaskResult</span>.failure(<span class="hljs-type">AppError</span>.abiStringParseError)
}
</code></pre>
<ol start="3">
<li>Create a ReadOperation object by using web3 instance, contract address , the abi string and the name of the method to invoke in the smart contract.</li>
</ol>
<pre><code class="hljs language-swift"><span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> contract <span class="hljs-operator">=</span> web3.contract(contractABIString, at: <span class="hljs-type">EthereumAddress</span>(contractAddress)) <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-type">TaskResult</span>.failure(<span class="hljs-type">AppError</span>.contractGenerateError)
}

<span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> readOperation <span class="hljs-operator">=</span> contract.createReadOperation(
    <span class="hljs-string">"getRecordsForUser"</span>,
    parameters: [userIdentifier]) <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-type">TaskResult</span>.failure(<span class="hljs-type">AppError</span>.contractWrongMethodOrParameters)
}
</code></pre>
<ol start="4">
<li>Execute callContractMethod and parse the data as required to display in the User Interface</li>
</ol>
<pre><code class="hljs language-swift"><span class="hljs-keyword">do</span> {
  <span class="hljs-comment">// 1. Call callContractMethod</span>
  <span class="hljs-keyword">let</span> result <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-keyword">await</span> readOperation.callContractMethod()

  <span class="hljs-comment">// 2. Check whether there are records added for this User</span>
  <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> array <span class="hljs-operator">=</span> result[<span class="hljs-string">"0"</span>] <span class="hljs-keyword">as?</span> [[<span class="hljs-keyword">Any</span>]] <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-type">TaskResult</span>.failure(<span class="hljs-type">AppError</span>.userDoesNotExistOrNoRecords)
  }
  <span class="hljs-keyword">if</span> <span class="hljs-operator">!</span>array.isEmpty {
      <span class="hljs-keyword">var</span> records: [<span class="hljs-type">ContributionModel</span>] <span class="hljs-operator">=</span> []

      <span class="hljs-comment">// 3. Parse the records in the response one by one</span>
      <span class="hljs-keyword">for</span> subArray <span class="hljs-keyword">in</span> array {
          <span class="hljs-keyword">if</span> <span class="hljs-operator">!</span>subArray.isEmpty {

              <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> recordNumber <span class="hljs-operator">=</span> subArray[<span class="hljs-number">1</span>] <span class="hljs-keyword">as?</span> <span class="hljs-type">BigUInt</span>,
                    <span class="hljs-keyword">let</span> amount <span class="hljs-operator">=</span> subArray[<span class="hljs-number">2</span>] <span class="hljs-keyword">as?</span> <span class="hljs-type">BigUInt</span>,
                    <span class="hljs-keyword">let</span> timeStamp <span class="hljs-operator">=</span> subArray[<span class="hljs-number">3</span>] <span class="hljs-keyword">as?</span> <span class="hljs-type">BigUInt</span>,
                    <span class="hljs-keyword">let</span> contributionType <span class="hljs-operator">=</span> subArray[<span class="hljs-number">0</span>] <span class="hljs-keyword">as?</span> <span class="hljs-type">BigUInt</span> <span class="hljs-keyword">else</span> {
                  <span class="hljs-keyword">continue</span>
              }

              <span class="hljs-keyword">var</span> type: <span class="hljs-type">ContributionModelType</span>
              <span class="hljs-keyword">if</span> contributionType <span class="hljs-operator">==</span> <span class="hljs-number">1</span>  {
                  type <span class="hljs-operator">=</span> .pensions
              } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> contributionType <span class="hljs-operator">==</span> <span class="hljs-number">2</span>  {
                  type <span class="hljs-operator">=</span> .taxes
              } <span class="hljs-keyword">else</span> {
                  <span class="hljs-keyword">continue</span>
              }

              <span class="hljs-keyword">let</span> note <span class="hljs-operator">=</span> subArray[<span class="hljs-number">4</span>] <span class="hljs-keyword">as?</span> <span class="hljs-type">String</span> <span class="hljs-operator">??</span> <span class="hljs-string">""</span>

              <span class="hljs-keyword">let</span> newRecord <span class="hljs-operator">=</span> <span class="hljs-type">ContributionModel</span>(
                  recordNumber: recordNumber,
                  timeStamp: timeStamp,
                  amount: amount,
                  note: note,
                  type: type)

              records.append(newRecord)
          }
      }
      <span class="hljs-keyword">return</span> <span class="hljs-type">TaskResult</span>.success(records)
  }
  <span class="hljs-keyword">return</span> <span class="hljs-type">TaskResult</span>.failure(<span class="hljs-type">AppError</span>.userDoesNotExistOrNoRecords)
} <span class="hljs-keyword">catch</span> {
  <span class="hljs-keyword">return</span> <span class="hljs-type">TaskResult</span>.failure(<span class="hljs-type">AppError</span>.userDoesNotExistOrNoRecords)
}
</code></pre>
<h1>Conclusions</h1>
<p>Blockchain is a very new technology, therefore it is rapidly evolving and decisions we made two months ago during the development of this project may have been different today.</p>
<p>In conclusion, the integration of Blockchain technology into mobile apps, such as the city hall app developed, offers enhanced security, transparency, streamlined processes, and improved data integrity. Furthermore, we are sure that the future of Blockchain technology in mobile apps holds immense potential.</p>
<p>In this first stage, we created a simple app just retrieving some info from the Blockchain to take a first contact and understand how it works. The next step will be adding records into the Blockchain from the app itself. For this purpose, we want to create an app for our City Hall; using it, employees would be able to add records into the smart contract directly from the app. To do this, we are thinking of using a different wallet than Metamask in order to gain a more friendly user experience. But these are contents for another article, and we will surely be talking here about them when we investigate a little bit more.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Isolated Testing with Test Containers and Spring Boot in Kotlin]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/08/01/Test-Isolation-with-TestContainers-and-SpringBoot</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/08/01/Test-Isolation-with-TestContainers-and-SpringBoot</guid>
            <pubDate>Tue, 01 Aug 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Are you using SpringBoot for backend services and looking to integrate TestContainers into your testing setup?</p>
<p>If so, then this example should give you an idea of how to achieve it in the shortest amount of time using the least possible configuration to set the implementation up.
To do this we will be considering Isolated Testing as a use case and use the TestContainers
to achieve this in the most simple way possible in order to run an integration test.
This setup would be more suitable for the projects where integration tests have higher priority and are part of regular execution in the CI/CD pipeline.</p>
<figure>
<img src="/assets/img/articles/2023-08-01-Test-Isolation-with-TestContainers-and-SpringBoot/test.webp">
<figcaption>Testing Lab</figcaption>
</figure>
<p>The purpose of isolated test, with or without TestContainers, is very important in Backend applications,
because at some point the integration tests should be stable enough to make part of regular execution in CI/CD pipeline chain.</p>
<h1></h1>
<blockquote>
<p><em><strong>Besides, we want to make sure tests never interfere with each other during the execution and especially the technical
setup does not enforce more and more refactoring over the time because of dependencies among the tests.</strong></em></p>
</blockquote>
<h1></h1>
<h1></h1>
<p>To ensure the correctness of the test results and the stability of each test method execution,
we need to make sure the internal or external services used in the integration test context
do not produce a data collision, and each test method execution can set up the data state for the service it requires.
This could be done for example on an infrastructure premises by running separate instances of services on which our tests would depend.
By doing so, we could achieve the goal, but not in an efficient way.
This for example would require more effort to set it up and also to make sure
that the test results are not impacting each other before, after and during the execution.</p>
<figure>
<img src="/assets/img/articles/2023-08-01-Test-Isolation-with-TestContainers-and-SpringBoot/result.webp">
<figcaption>What could possibly go wrong?</figcaption>
</figure>
<p>What problems would we encounter with this setup? The earlier will be too expensive and
the later one will is not flexible enough to prevent parallel test execution.</p>
<p>Of course, we could make sure the data is clean in the datasource before starting the tests to support parallel execution,
but the cost of knowing the whole test system in order to modify or extend it would be
a heavy task and never safe because of the risk of producing bugs within the test code.</p>
<p>Luckily, TestContainers would make this easy for us so that we do not need to worry
about the data collision or parallel execution.
Why? Depending on our setup, the technology would simply take care of it
by creating and destroying the type of instance we require for our integration test either for each test case or test class.
The execution in this manner might be a little more expensive,
but the cost pays us with some values of easy setup, stability, flexibility and independence in tests.
Besides, making sure that both old and new tests written at
any point in time are still independent of each other could save lots of refactoring time.</p>
<figure>
<img src="/assets/img/articles/2023-08-01-Test-Isolation-with-TestContainers-and-SpringBoot/design.webp">
<figcaption>How to design an integration test?</figcaption>
</figure>
<p>With new releases of SpringBoot and TestContainers it is way easier to accomplish the integration.
Depending on the use case, as in unit testing, as well as in the integration test,
the recommended structure to use in the test code is the classical testing strategy based on GIVEN/WHEN/THEN concept as following:</p>
<blockquote>
<ol>
<li>
<p>Given: Data Preparation: Prepares or mocks the test data.
.</p>
</li>
<li>
<p>When: Target Code: Executes the testable production code.</p>
</li>
<li>
<p>Then: Assertion: Checks the expected data.</p>
</li>
</ol>
</blockquote>
<h1></h1>
<p>What is different here compared to unit testing is that we not only need to prepare
the test data but also, depending on the requirements, provide external and/or internal
services for the test setup, allowing each test case to manage them individually according to their needs.
For example, if our integration test depends on a DB instance, then we would need to provide
a Docker based DB instance before the test execution and insert the necessary data.</p>
<h1></h1>
<hr>
<h1></h1>
<pre><code class="hljs language-kotlin"><span class="hljs-meta">@RestController</span>
<span class="hljs-meta">@RequiredArgsConstructor</span>
<span class="hljs-keyword">internal</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserController</span></span>(<span class="hljs-keyword">val</span> service: UserService) {

    <span class="hljs-meta">@GetMapping(<span class="hljs-string">"/users"</span>)</span>
    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">all</span><span class="hljs-params">()</span></span>: Iterable&#x3C;User> {
        <span class="hljs-keyword">return</span> service.findAll()
    }

    <span class="hljs-meta">@PostMapping(<span class="hljs-string">"/users"</span>)</span>
    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">newUser</span><span class="hljs-params">(<span class="hljs-meta">@RequestBody</span> newUser: <span class="hljs-type">User</span>)</span></span>: User {
        <span class="hljs-keyword">return</span> service.createNew(newUser)
    }

    <span class="hljs-meta">@GetMapping(<span class="hljs-string">"/users/{id}"</span>)</span>
    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">one</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable</span> id: <span class="hljs-type">Long</span>)</span></span>: User? {
        <span class="hljs-keyword">return</span> service.getUser(id) ?: <span class="hljs-keyword">throw</span> UserNotFoundException(id)
    }
}
</code></pre>
<p>This specific example has been developed as a basic SpringBoot application
which exposes endpoints to insert and fetch basic data from PostgreSQL datasource.
If we were to write multiple integration tests for this application we could either
run multiple PSQL instances by creating and destroying them per test case or class or
use 1 instance and prepare the data state before and after each execution.</p>
<pre><code class="hljs language-less"><span class="hljs-variable">@Testcontainers</span>
<span class="hljs-variable">@SpringBootTest</span>(classes = [<span class="hljs-attribute">IsolatedTestingApplication</span>::class])
<span class="hljs-variable">@ActiveProfiles</span>(<span class="hljs-string">"test"</span>)
<span class="hljs-variable">@AutoConfigureMockMvc</span>(addFilters = false)
class UserApiIT {

    <span class="hljs-variable">@Autowired</span>
    var <span class="hljs-attribute">userRepository</span>: UserRepository? = null

    <span class="hljs-variable">@Autowired</span>
    var <span class="hljs-attribute">mockMvc</span>: MockMvc? = null

    <span class="hljs-variable">@Test</span>
    fun <span class="hljs-built_in">`Retrieve User from DB via the Endpoint`</span>() {
        <span class="hljs-comment">//GIVEN        </span>
        <span class="hljs-selector-tag">val</span> <span class="hljs-selector-tag">user</span> = <span class="hljs-selector-tag">User</span>(<span class="hljs-string">"Name"</span>, <span class="hljs-string">"Lastname"</span>)
        <span class="hljs-selector-tag">userRepository</span>?<span class="hljs-selector-class">.save</span>(user)

        <span class="hljs-comment">//WHEN</span>
        <span class="hljs-selector-tag">mockMvc</span>!!<span class="hljs-selector-class">.perform</span>(
            MockMvcRequestBuilders.get(<span class="hljs-string">"/users/{id}"</span>, <span class="hljs-number">1</span>))
            
        <span class="hljs-comment">//THEN</span>
                <span class="hljs-selector-class">.andExpect</span>(MockMvcResultMatchers.status().isOk)
                <span class="hljs-selector-class">.andExpect</span> { result: MvcResult ->
                    val <span class="hljs-attribute">content = result.response.contentAsString
                    assertThat(content).contains("Lastname")}
                .andReturn())
    }
}
</span></code></pre>
<p>In the test case, the requests will be made by using Spring Mock MVC to reach the PSQL Datasource, which we can then set up by using
Spring Boot configuration file and TestContainers provided PostgreSQL Docker based instance.</p>
<p>Next, the configuration would make sure that the SpringBoot and TestContainer integration could set up and run a test application before the test case execution:</p>
<p>appication.yml</p>
<pre><code class="hljs language-css">spring:
  datasource:
    url: jdbc:postgresql://${DATABASE_HOST:localhost}: ${DATABASE_PORT:<span class="hljs-number">5435</span>}/${DATABASE_NAME:template}
    username: ${DATABASE_USER:templateUser}
    password: ${DATABASE_PASSWORD:templatePassword}
</code></pre>
<p>appication-test.yml</p>
<pre><code class="hljs language-ruby"><span class="hljs-symbol">spring:</span>
  <span class="hljs-symbol">datasource:</span>
    <span class="hljs-symbol">url:</span> <span class="hljs-symbol">jdbc:</span><span class="hljs-symbol">tc:</span><span class="hljs-symbol">postgresql:</span><span class="hljs-number">14</span><span class="hljs-symbol">:///template</span>
</code></pre>
<p>This will automatically initiate an PSQL Docker based instance by creating the mentioned user as well as the database in it.
As soon as the Docker instance is ready SpringBoot application startup will be triggered along with the FlyWay migration scripts to be executed.
Based on the given configuration the Docker PSQL instance will have the following setup ready for the application to connect and use...</p>
<ul>
<li>Host: <i>localhost</i></li>
<li>Username: <i>templateUser</i></li>
<li>Password: <i>templatePassword</i></li>
<li>Database: <i>template</i></li>
</ul>
<figure>
<img src="/assets/img/articles/2023-08-01-Test-Isolation-with-TestContainers-and-SpringBoot/docker.webp">
<figcaption>Docker and Containers in action</figcaption>
</figure>
<p>While running the provided test sample you can simply follow in the Docker Desktop that 2 Docker Containers are appearing and disappearing on the fly to support our test execution on.</p>
<h1></h1>
<hr>
<h1></h1>
<figure>
<img src="/assets/img/articles/2023-08-01-Test-Isolation-with-TestContainers-and-SpringBoot/kts.webp">
<figcaption>
Technical Setup
</figcaption>
</figure>
<p>The source code for the described use case is available in <a href="https://github.com/monstar-lab-oss/Isolated-Testing">this GitHub repository</a> provided by <a href="https://monstar-lab.com/global/">Monstarlab::</a>
Having <a href="https://www.docker.com/">Docker Desktop</a> installed is a pre-requisite to run it.</p>
<h1></h1>
<hr>
<h1></h1>
<p>Executing the following command in CLI should be sufficient to see and check the test results.</p>
<pre><code class="hljs language-agsl">./gradlew clean build
</code></pre>
<h1></h1>
<hr>
<h1></h1>
<p>The following steps are necessary to run the backend application in a CLI:</p>
<ol>
<li>Initiate PSQL Docker Container with a Database.</li>
</ol>
<pre><code class="hljs language-arduino">docker run -d --name template_postgres -e POSTGRES_USER=templateUser -e POSTGRES_PASSWORD=templatePassword -e POSTGRES_DB=<span class="hljs-keyword">template</span> -p <span class="hljs-number">5435</span>:<span class="hljs-number">5432</span>  --restart=always postgres
</code></pre>
<ol start="2">
<li>Start the backend application</li>
</ol>
<pre><code class="hljs">./gradlew bootRun
</code></pre>
<p>The choice of Kotlin as a language and Gradle as a build tool is merely one step forward towards the modern programming world and has no impact on the setup:
this could have been easily also achieved with Maven and Java.</p>
<h4>Tools &#x26; References used here...</h4>
<ul>
<li><a href="https://github.com/monstar-lab-oss/spring-boot-template">Monstarlab:: template with Kotlin and Gradle</a></li>
<li><a href="https://gradle.org/">Gradle as build tool with KTS</a></li>
<li><a href="https://kotlinlang.org/">Kotlin as a programming language</a></li>
<li><a href="https://hibernate.org/">Hibernate as an ORM layer for JPA</a></li>
<li><a href="https://www.postgresql.org/">PostgreSQL as a Datasource</a></li>
<li><a href="https://flywaydb.org/">FlyWay for DB script migrations</a></li>
<li><a href="https://spring.io/projects/spring-boot">Spring Boot, Note that since 3.1 TestContainers are supported by SrpingBoot</a></li>
<li><a href="https://testcontainers.com/">Test Containers for running PSQL Docker based Container</a></li>
<li><a href="https://unsplash.com/">Unsplash Images for writing the blog post</a></li>
<li><a href="https://monstar-lab.com/global/">Brain and Mind from Monstarlab::</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Accelerate Your Android App Testing with Appium and Java (Part 2)- Hybrid App Automation]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/07/14/Accelerate-Your-Android-App-Testing-with-Appium-and-Java-(Part 2)-Hybrid-App-Automation</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/07/14/Accelerate-Your-Android-App-Testing-with-Appium-and-Java-(Part 2)-Hybrid-App-Automation</guid>
            <pubDate>Fri, 14 Jul 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Welcome back! In this blog post, we will explore Appium, a powerful tool for automating Hybrid Android apps. Firstly, we will discuss locating Hybrid app elements in Appium, which involves identifying and interacting with specific elements within the app's user interface. Next, we will cover Hybrid Android app automation, which focuses on automating the testing process of Android apps with hybrid features. Additionally, we will explore the Page Object Model (POM) pattern for Appium test automation, a structured approach that aids in maintaining maintainable and reusable code. Finally, we will highlight best practices for Appium test automation, providing valuable guidelines for achieving reliable and efficient automation.</p>
<p>By the end of this blog, you will have the knowledge and tools to enhance your Hybrid Android app automation process using Appium. If you haven't read the <a href="https://engineering.monstar-lab.com/en/post/2023/04/26/Accelerate-Your-Android-App-Testing-with-Appium-and-Java-A-Beginner%E2%80%99s-Guide-(Part-1)/">first part</a> yet, I highly recommend checking it out before proceeding. In the first part of this blog post series, we explored:</p>
<ul>
<li>How to set up Appium for Android test automation</li>
<li>Getting the appPackage and appActivity</li>
<li>Writing your first Appium test</li>
</ul>
<p>Now let’s deep dive. Hybrid Android app refers to an application that combines elements of both native and web-based technologies for development. It typically uses web technologies such as HTML, CSS, and JavaScript to build the user interface (UI) and runs inside a WebView component, which acts as a browser embedded within the app.</p>
<h2>1. Locating Hybrid app elements in Appium</h2>
<p>Hybrid apps are built using a combination of web technologies (HTML, CSS, JavaScript) and native components. In that case, you need to locate both web and native elements.</p>
<h3>Locating Web Elements:</h3>
<p>Hybrid apps typically use a WebView component to display web content within the app. You'll need to locate the WebView element to interact with the web elements inside it. You can locate Web elements following these steps:</p>
<ol>
<li>Install the hybrid app on an Android device or emulator. If you're running on it on a real device, connect the Android device to your development machine using a USB cable. Make sure USB debugging is enabled in the device's developer options. Ensure that the app is running in a debuggable mode.</li>
</ol>
<p><img src="/assets/img/articles/2023-07-14-Accelerate-Your-Android-App-Testing-with-Appium-and-Java-(Part-2)-Hybrid-App-Automation/USBDebuuging.webp" alt=""></p>
<ol start="2">
<li>Open Google Chrome on your development machine and navigate to "chrome://inspect". You should see a list of connected devices and running WebView instances.</li>
</ol>
<p><img src="/assets/img/articles/2023-07-14-Accelerate-Your-Android-App-Testing-with-Appium-and-Java-(Part-2)-Hybrid-App-Automation/ChromeInspector.webp" alt=""></p>
<ol start="3">
<li>Find the WebView instance corresponding to your app and click the "Inspect" button. This opens the Chrome DevTools for that WebView.</li>
</ol>
<p><img src="/assets/img/articles/2023-07-14-Accelerate-Your-Android-App-Testing-with-Appium-and-Java-(Part-2)-Hybrid-App-Automation/ChromeInspecter2.webp" alt=""></p>
<ol start="4">
<li>
<p>Within Chrome DevTools, you can use various inspection tools like the Elements panel, Console, and Network panel to locate and interact with web elements. You can inspect the HTML structure, apply CSS selectors, execute JavaScript, and simulate user interactions.</p>
</li>
<li>
<p>Once you have identified the desired web element, you can use it in your code to interact with the element.</p>
</li>
</ol>
<h3>Locating Native Elements:</h3>
<p>When it comes to locating native elements in Appium, there are several ways to do so. One popular method is to use Appium Inspector, which is a visual tool that allows you to inspect the user interface (UI) elements of your application and generate the corresponding locators.
To use Appium Inspector, you'll need to have your app running on a device or emulator and have the Appium server running. Once you have both of these setups, you can locate elements following these steps:</p>
<ol>
<li>
<p>Download and Install the Appium Inspector from <a href="https://github.com/appium/appium-inspector/releases">here</a>.</p>
</li>
<li>
<p>Open Appium Inspector and Enter the desired capabilities for your test, such as the device name and platform name. Find the details documentation of desired capabilities from <a href="https://appium.readthedocs.io/en/stable/en/writing-running-appium/caps/">here</a>.</p>
</li>
</ol>
<p><img src="/assets/img/articles/2023-07-14-Accelerate-Your-Android-App-Testing-with-Appium-and-Java-(Part-2)-Hybrid-App-Automation/AppiumInspecter.webp" alt=""></p>
<ol start="3">
<li>
<p>Click the "Start Session" button to launch your app on the device or emulator.</p>
</li>
<li>
<p>Once your app is running, you should see the UI elements displayed in Appium Inspector. You can click on the UI elements and see their attributes, such as the ID, class name, and text.</p>
</li>
</ol>
<p><img src="/assets/img/articles/2023-07-14-Accelerate-Your-Android-App-Testing-with-Appium-and-Java-(Part-2)-Hybrid-App-Automation/AppiumInspecter2.webp" alt=""></p>
<ol start="5">
<li>
<p>To generate a locator for an element, simply click on the element in Appium Inspector and Copy "XPath" / "Accessibility ID" or “Resource ID” (depending on your preferred locator type) from the right side of the Selected Element.</p>
</li>
<li>
<p>Paste the generated locator into your Appium test code and use it to interact with the element.</p>
</li>
</ol>
<p>Using Appium Inspector you can save you time and make it easier to locate elements in your app. However, it's important to note that it's not always the most reliable method, especially if the UI of your app changes frequently. In these cases, you may need to use alternative methods such as <a href="https://www.lambdatest.com/blog/locators-in-appium/">regular expressions</a> to locate your elements.</p>
<h2>2. Hybrid Android app Automation with Appium</h2>
<p>Automating Hybrid apps can be more complex compared to testing native apps or web apps alone, as you need to handle both web and native contexts in your test script. Fortunately, Appium provides methods to switch between these contexts and perform actions accordingly.</p>
<p>Here's an example code snippet to give you an idea of how to automate a hybrid Android app using Appium with Java:</p>
<p>Switching into Webview:</p>
<pre><code class="hljs language-java">    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">switchToWebView</span><span class="hljs-params">()</span> {
        Set&#x3C;String> contextHandles = driver.getContextHandles();
        <span class="hljs-keyword">for</span> (String context : contextHandles) {
            <span class="hljs-keyword">if</span> (context.startsWith(<span class="hljs-string">"WEBVIEW"</span>)) {
                driver.context(context);
                <span class="hljs-keyword">break</span>;
            }
        }
    }
</code></pre>
<p>Switching into Nativeview:</p>
<pre><code class="hljs language-java">
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">switchToNativeView</span><span class="hljs-params">()</span> {
        Set&#x3C;String> contextHandles = driver.getContextHandles();
        <span class="hljs-keyword">for</span> (String context : contextHandles) {
            <span class="hljs-keyword">if</span> (context.startsWith(<span class="hljs-string">"NATIVE"</span>)) {
                driver.context(context);
                <span class="hljs-keyword">break</span>;
            }
        }
    }

</code></pre>
<p>The given code provides two methods, <code>switchToWebView()</code> and <code>switchToNativeView()</code>, for switching between the web view and native view in a mobile application using Appium.</p>
<p>In the <code>switchToWebView()</code> method:</p>
<ul>
<li>The code starts by obtaining a set of all available context handles using the <code>driver.getContextHandles()</code> method. Context handles represent different execution contexts or views within the application.</li>
<li>It then iterates through each context handle using a for-each loop.</li>
<li>Inside the loop, it checks if the context handle starts with the prefix "WEBVIEW" using the startsWith() method.</li>
<li>If a context handle with the "WEBVIEW" prefix is found, it indicates the web view context.</li>
<li>The code then switches the driver's context to the identified web view context using the driver.context(context) method.</li>
<li>Finally, the loop is broken using the break statement to prevent unnecessary iterations once the web view context is found.</li>
</ul>
<p>Similarly, in the <code>switchToNativeView()</code> method, the code performs the following steps:</p>
<ul>
<li>Obtains the set of all available context handles using driver.getContextHandles().</li>
<li>Iterates through each context handle.</li>
<li>Checks if the context handle starts with the prefix "NATIVE".</li>
<li>If a context handle with the "NATIVE" prefix is found, it indicates the native view context.</li>
<li>Switches the driver's context to the identified native view context using <code>driver.context(context)</code>.</li>
<li>Breaks the loop to stop further iterations once the native view context is found.</li>
</ul>
<p>These methods provide a way to switch between the web view and native view contexts in a mobile application when using Appium. There are some other methods such as <code>driver.getContextHandles()</code>, <code>driver.getContext()</code>, <code>driver.switchTo().context()</code>
are used to get context and switching the context between Web and Native view.</p>
<h2>3. POM for Appium test Automation</h2>
<p>POM stands for Page Object Model, which is a design pattern used in test automation to represent each page of an application as a separate class, containing its own set of methods and properties. By separating the application pages into separate classes, the code becomes more readable and easier to understand.</p>
<p><img src="/assets/img/articles/2023-07-14-Accelerate-Your-Android-App-Testing-with-Appium-and-Java-(Part-2)-Hybrid-App-Automation/POM.webp" alt=""></p>
<h3>Step 1: Create Page Objects</h3>
<p>The first step is to create a class for each page or screen of the application that you want to test. The class should contain the elements and methods specific to that page. For example, if you have a login page, you can create a LoginPage class as follows:</p>
<pre><code class="hljs language-java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LoginPage</span> {
   <span class="hljs-keyword">private</span> AndroidDriver&#x3C;AndroidElement> driver;
   <span class="hljs-keyword">public</span> <span class="hljs-title function_">LoginPage</span><span class="hljs-params">(AndroidDriver&#x3C;AndroidElement> driver)</span> {
      <span class="hljs-built_in">this</span>.driver = driver;
   }
   <span class="hljs-keyword">private</span> <span class="hljs-type">By</span> <span class="hljs-variable">emailId</span> <span class="hljs-operator">=</span> By.id(“email”);
   <span class="hljs-keyword">private</span> <span class="hljs-type">By</span> <span class="hljs-variable">password</span> <span class="hljs-operator">=</span> By.id(“password”);
   <span class="hljs-keyword">private</span> <span class="hljs-type">By</span> <span class="hljs-variable">loginButton</span> <span class="hljs-operator">=</span> By.id(“login”);

   <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">enterEmail</span><span class="hljs-params">(String email)</span> {
      driver.findElement(emailId).sendKeys(email);
   }
   <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">enterPassword</span><span class="hljs-params">(String pass)</span> {
      driver.findElement(password).sendKeys(pass);
   }
   <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">clickLoginButton</span><span class="hljs-params">()</span> {
      driver.findElement(loginButton).click();
       <span class="hljs-comment">// Check if the login is successful </span>
      <span class="hljs-keyword">if</span> (<span class="hljs-comment">/* condition to check if login is successful */</span>) { 
        <span class="hljs-comment">// Success assertion Assert.assertTrue</span>
       (<span class="hljs-comment">/* condition indicating login success */</span>, <span class="hljs-string">"Login was successful."</span>); 
       returnToHome();
       } 
     <span class="hljs-keyword">else</span> { 
       <span class="hljs-comment">// Fail assertion Assert.fail</span>
      (<span class="hljs-string">"Login failed."</span>); 
      }
  }
 <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">returnToHome</span><span class="hljs-params">()</span> { 
    <span class="hljs-comment">// Code to navigate back to the home screen or perform other actions </span>
}
}
</code></pre>
<h3>Step 2: Create a TestCases Class</h3>
<p>Creates a separate class for each test case that you want to execute. In each test class, initialize the driver and create an instance of the page class. For example, if you want to test the login functionality, you can create a test class as follows:</p>
<pre><code class="hljs language-java">
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LoginTest</span> {
   <span class="hljs-keyword">private</span> AndroidDriver&#x3C;AndroidElement> driver;
   LoginPage loginPage;
   <span class="hljs-meta">@BeforeTest</span>
   <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setUp</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> MalformedURLException {
      <span class="hljs-type">DesiredCapabilities</span> <span class="hljs-variable">caps</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">DesiredCapabilities</span>();
      caps.setCapability(“deviceName”, “emulator-<span class="hljs-number">5554</span><span class="hljs-string">");
      caps.setCapability(“platformVersion”, “10.0"</span>);
      caps.setCapability(“deviceName”, “emulator-<span class="hljs-number">5554</span><span class="hljs-string">");
      caps.setCapability(“appPackage”, “com.example.android”);
      caps.setCapability(“appActivity”, “.MainActivity”);
      caps.setCapability(“automationName”, “UiAutomator2"</span>);
      <span class="hljs-type">URL</span> <span class="hljs-variable">url</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">URL</span>(“http:<span class="hljs-comment">//127.0.0.1:4723/wd/hub”);</span>
      driver = <span class="hljs-keyword">new</span> <span class="hljs-title class_">AndroidDriver</span>&#x3C;AndroidElement>(url, caps);
      loginPage = <span class="hljs-keyword">new</span> <span class="hljs-title class_">LoginPage</span>(driver);
   }
   <span class="hljs-meta">@Test</span>
   <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testLogin</span><span class="hljs-params">()</span> {
      loginPage.enterEmail(“testuser<span class="hljs-meta">@test</span>.com”);
      loginPage.enterPassword(“testpassword”);
      loginPage.clickLoginButton();
   }
   <span class="hljs-meta">@AfterTest</span>
   <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">tearDown</span><span class="hljs-params">()</span> {
      driver.quit();
   }
}
</code></pre>
<h3>Step 3: Run the Test</h3>
<p>Run all the test classes and verify the results. You can use a test runner such as TestNG or JUnit to run the tests. Find the demo project for POM <a href="https://github.com/Eftiar/DemoPageObjectModel">here</a>.</p>
<h3>4: Best practices for Appium test automation</h3>
<ul>
<li>
<p><strong>Use stable locators</strong>: Choose reliable and unique locators to identify elements within the app's UI. IDs or names are better to use for web elements, while resource IDs or accessibility IDs are useful for native elements. Avoid relying solely on element positions, as they can change dynamically.</p>
</li>
<li>
<p><strong>Set up the desired capabilities correctly:</strong> Provide accurate and complete desired capabilities to establish the correct session for your hybrid app. Specify the app type and include other necessary capabilities like platform name, device name, app package, app activity, and automation name.</p>
</li>
<li>
<p><strong>Use waits and synchronization:</strong> Hybrid apps often involve asynchronous operations, such as loading web content or making API requests. Use implicit and explicit waits to handle synchronization. Implicit waits set a global timeout for element searches, while explicit waits wait for specific conditions to be met before proceeding with the test. Learn more about waits from here. Here’s an example of how to use waits:</p>
<pre><code class="hljs language-java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">HybridAppTest</span> { 

<span class="hljs-keyword">private</span> WebDriver driver; 
<span class="hljs-keyword">private</span> WebDriverWait wait; 
<span class="hljs-keyword">public</span> <span class="hljs-title function_">HybridAppTest</span><span class="hljs-params">(WebDriver driver)</span> { 
<span class="hljs-built_in">this</span>.driver = driver; 
<span class="hljs-built_in">this</span>.wait = <span class="hljs-keyword">new</span> <span class="hljs-title class_">WebDriverWait</span>(driver, <span class="hljs-number">10</span>); 
<span class="hljs-comment">// Initialize WebDriverWait with a timeout of 10 seconds </span>
} 
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">performHybridAppTest</span><span class="hljs-params">()</span> { 
<span class="hljs-comment">// Perform some actions that trigger the loading of web content </span>
<span class="hljs-comment">// ... </span>
<span class="hljs-comment">// Wait for a specific element to be visible using explicit wait </span>
<span class="hljs-type">By</span> <span class="hljs-variable">elementLocator</span> <span class="hljs-operator">=</span> By.id(<span class="hljs-string">"exampleElementId"</span>); wait.until(ExpectedConditions.visibilityOfElementLocated(elementLocator)); 
<span class="hljs-comment">// Perform actions on the loaded element </span>
<span class="hljs-type">WebElement</span> <span class="hljs-variable">element</span> <span class="hljs-operator">=</span> driver.findElement(elementLocator); element.click(); 
<span class="hljs-comment">// Continue with the test </span>
<span class="hljs-comment">// ... </span>
} 
}
</code></pre>
</li>
<li>
<p><strong>Use parameterization:</strong> Parameterizing your tests can help you to run the same test with different data inputs, making your tests more flexible and reusable. You can use tools such as TestNG or JUnit to define data-driven tests. Here’s an example of how to use parameterization:</p>
<pre><code class="hljs language-java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ParameterizedTest</span> { 

<span class="hljs-meta">@DataProvider(name = "testdata")</span> 
<span class="hljs-keyword">public</span> Object[][] testData() { 
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Object</span>[][] { 
{<span class="hljs-string">"username1"</span>, <span class="hljs-string">"password1"</span>}, 
{<span class="hljs-string">"username2"</span>, <span class="hljs-string">"password2"</span>}, 
{<span class="hljs-string">"username3"</span>, <span class="hljs-string">"password3"</span>}
    };
} 
<span class="hljs-meta">@Test(dataProvider = "testdata")</span> 
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">loginTest</span><span class="hljs-params">(String username, String password)</span> { 
<span class="hljs-comment">// Perform login with the provided username and password </span>
<span class="hljs-comment">// ... </span>
}
}
</code></pre>
</li>
<li>
<p><strong>Continuous integration and reporting:</strong> Integrate your hybrid app automation tests into a continuous integration (CI) system, such as Jenkins or CircleCI. This allows for automatic test execution on code changes and provides detailed test reports and notifications. Additionally, leverage reporting tools like Extent Reports or Allure to generate comprehensive and visually appealing test reports.</p>
</li>
<li>
<p><strong>Use cloud-based devices:</strong> Testing on real devices can be expensive and time-consuming. Using a cloud-based device provider such as AWS Device Farm, Sauce Labs, or BrowserStack can help you test your app on a wide range of devices and configurations without managing physical devices.</p>
</li>
</ul>
<p>In conclusion, this two-part blog post series provides valuable insights into accelerating Android app testing with Appium and Java, specifically focusing on hybrid app automation. The first part covers the initial setup of Appium, obtaining the necessary package and activity information, and writing your first Appium test. The second part delves deeper into Appium by discussing best practices for locating hybrid app elements, automating hybrid Android apps, implementing the Page Object Model (POM) for test automation, and highlighting general best practices for Appium test automation.</p>
<p><em>Article Photo by <a href="https://unsplash.com/@agk42">Alex Knight</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Exploring the Power of AWS Amplify - Simplifying Web and Mobile App Development]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/07/14/Exploring-the-Power-of-AWS-Amplify-Simplifying-Web-and-Mobile-App-Development</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/07/14/Exploring-the-Power-of-AWS-Amplify-Simplifying-Web-and-Mobile-App-Development</guid>
            <pubDate>Fri, 14 Jul 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>In today's fast-paced world, businesses need to quickly develop and deploy web and mobile applications to stay competitive. AWS Amplify, a powerful development framework provided by Amazon Web Services (AWS), enables developers to build scalable and secure applications with ease. In this article, we'll explore AWS Amplify and delve into its key features and benefits.</p>
<figure>
<img src="/assets/img/articles/2023-07-14-Exploring-the-Power-of-AWS-Amplify-Simplifying-Web-and-Mobile-App-Development/1amplify.webp">
<figcaption>What is Amplify?</figcaption>
</figure>
<h2>What is AWS Amplify?</h2>
<p>AWS Amplify is a comprehensive set of tools and services designed to simplify the development and deployment of web and mobile applications. It offers a seamless workflow for frontend and backend development, including features such as authentication, storage, APIs, and serverless functions.</p>
<h2>Key Features of AWS Amplify</h2>
<ol>
<li><strong>Authentication</strong>: Amplify provides built-in authentication capabilities, including authentication providers such as Amazon Cognito, social sign-ins, and even custom authentication flows.
Here is an example:</li>
</ol>
<p>First, add the authentication library:</p>
<pre><code class="hljs language-commandline">amplify add auth
</code></pre>
<p>In your React application directory, install the Amplify library:</p>
<pre><code class="hljs language-commandline">npm install aws-amplify @aws-amplify/ui-react
</code></pre>
<p>In your, <em>App.js</em> make sure you import the required components you need from the <em>aws-amplify/ui-react</em> library. For now, we are importing <em>withAuthenticator</em> and <em>AmplifyAuthenticator</em>:</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">import</span> <span class="hljs-title class_">React</span> <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { withAuthenticator, <span class="hljs-title class_">AmplifySignOut</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'@aws-amplify/ui-react'</span>;
<span class="hljs-keyword">const</span> <span class="hljs-title function_">App</span> = (<span class="hljs-params"></span>) => ( <span class="xml"><span class="hljs-tag">&#x3C;<span class="hljs-name">div</span>></span> <span class="hljs-tag">&#x3C;<span class="hljs-name">AmplifySignOut</span> /></span> My App <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span></span> ); <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-title function_">withAuthenticator</span>(<span class="hljs-title class_">App</span>);
</code></pre>
<p>You will also notice the <em>AmplifySignOut</em> component here, which renders a signout button.</p>
<ol start="2">
<li><strong>Data Storage</strong>: With Amplify, developers can easily integrate data storage solutions such as Amazon DynamoDB, Amazon S3, or even custom data sources to store and retrieve application data. Here’s an example:</li>
</ol>
<p>The fastest way to get started with using datastore in React is:</p>
<pre><code class="hljs language-commandline">npx create-react-app amplify-datastore --use-npm
cd amplify-datastore
npx amplify-app@latest
</code></pre>
<ol start="3">
<li><strong>APIs and AppSync</strong>: Amplify simplifies the process of creating and integrating APIs into your applications. It supports both REST and GraphQL APIs and provides a robust backend infrastructure with features like real-time data synchronization using AWS AppSync.</li>
</ol>
<figure>
<img src="/assets/img/articles/2023-07-14-Exploring-the-Power-of-AWS-Amplify-Simplifying-Web-and-Mobile-App-Development/2api.webp">
<figcaption>Amplify's API</figcaption>
</figure>
<ol start="4">
<li><strong>Serverless Functions</strong>: Amplify integrates with AWS Lambda to enable developers to build and deploy serverless functions easily. This allows for the execution of backend logic without worrying about managing servers or infrastructure.</li>
</ol>
<figure>
<img src="/assets/img/articles/2023-07-14-Exploring-the-Power-of-AWS-Amplify-Simplifying-Web-and-Mobile-App-Development/3serverless.webp">
<figcaption>Serverles functions</figcaption>
</figure>
<ol start="5">
<li><strong>Hosting and Continuous Deployment</strong>: Amplify offers a streamlined process for hosting and deploying web applications. It supports automatic deployment through Git-based workflows, making it easy to deploy updates to your application in a controlled and efficient manner.</li>
</ol>
<figure>
<img src="/assets/img/articles/2023-07-14-Exploring-the-Power-of-AWS-Amplify-Simplifying-Web-and-Mobile-App-Development/4cicd.webp">
<figcaption>CICD</figcaption>
</figure>
<h2>Amplify CLI and Amplify Console</h2>
<ol>
<li><strong>Amplify CLI</strong>: The Amplify Command Line Interface (CLI) provides a convenient way to initialize, configure, and manage Amplify projects from the command line. It offers a simple and consistent interface to set up services, configure authentication, and deploy your application.</li>
</ol>
<figure>
<img src="/assets/img/articles/2023-07-14-Exploring-the-Power-of-AWS-Amplify-Simplifying-Web-and-Mobile-App-Development/5cli.webp">
<figcaption>CLI</figcaption>
</figure>
<ol start="2">
<li><strong>Amplify Console</strong>: The Amplify Console provides a hosting and continuous deployment service specifically designed for Amplify applications. It automatically deploys updates as you push changes to your repository, simplifying the deployment process and ensuring a smooth development workflow.</li>
</ol>
<figure>
<img src="/assets/img/articles/2023-07-14-Exploring-the-Power-of-AWS-Amplify-Simplifying-Web-and-Mobile-App-Development/6console.webp">
<figcaption>Console</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2023-07-14-Exploring-the-Power-of-AWS-Amplify-Simplifying-Web-and-Mobile-App-Development/7save.webp">
<figcaption>Save</figcaption>
</figure>
<h2>Benefits of AWS Amplify</h2>
<ol>
<li>
<p><strong>Rapid Development</strong>: Amplify's pre-built UI components, simplified workflows, and ready-to-use backend services enable developers to quickly prototype and develop applications.</p>
</li>
<li>
<p><strong>Scalability and Security</strong>: Leveraging AWS services, Amplify ensures that your applications can scale seamlessly as demand increases while maintaining a high level of security.</p>
</li>
<li>
<p><strong>Platform Flexibility</strong>: Amplify supports multiple platforms, including web, iOS, Android, and React Native, providing developers with the flexibility to build applications for various devices and environments.</p>
</li>
<li>
<p><strong>Developer Experience</strong>: With its intuitive interface, extensive documentation, and community support, Amplify offers a pleasant development experience, enabling developers to focus on building great applications.</p>
</li>
</ol>
<h2>Conclusion</h2>
<p>AWS Amplify empowers developers to build feature-rich and scalable web and mobile applications by simplifying the development process and integrating with AWS services. Its comprehensive set of tools, including authentication, data storage, APIs, and serverless functions, along with the Amplify CLI and Amplify Console, accelerate the development and deployment cycles. Embrace the power of AWS Amplify to streamline your application development process and deliver exceptional user experiences in a shorter time frame.</p>
<p><em>Article Photo by <a href="https://docs.aws.amazon.com/">AWS Documentation</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Jetpack Compose UI Architecture]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/07/14/Jetpack-Compose-UI-Architecture</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/07/14/Jetpack-Compose-UI-Architecture</guid>
            <pubDate>Fri, 14 Jul 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Jetpack Compose is by far the most exciting thing that has happened in my developer career. It has changed how I work and how I think about problems. It has introduced many exciting tools that are both easy to use and flexible, allowing you to do almost anything with ease.</p>
<p>I adopted Jetpack Compose early on for a project that was already in production, and I was immediately hooked. Even though I already had experience creating UI with Compose at that point, organizing and architecting my new and shiny Jetpack Compose-powered features introduced a lot of back and forth.</p>
<p>The goal of this article is to share the results of those findings, present an architecture that is scalable, easy to use and operate in, and, of course, collect feedback to make it even better.</p>
<blockquote>
<p>Disclaimer ⚠️:
This article will only deal with the UI side. The way the rest of the application is built will be left out, but you can assume it follows the classic Clean Architecture approach. I also assume that you are familiar with Jetpack Compose, as I will not dive deep into the specifics of UI implementation.</p>
</blockquote>
<h2>Example</h2>
<p>To provide a concrete example, let me introduce the guinea pig for this article going forward. The application, we are going to build allows the user to cycle through different Landmarks  and navigate to them. Here is a basic description of the flow:</p>
<ol>
<li>Users can swipe through Place Cards and view different information about the Place, such as the Place's picture, name, and rating.</li>
<li>Users can mark/unmark the Place as a favorite.</li>
<li>Users can navigate and plan a route to the selected Place from their location. To do that, we will need the user's permission to collect their location.</li>
<li>If there is an error, we would like to show a toast.</li>
<li>The permission is only asked if the user chooses to plan the route. If the user declines the permission, we navigate to a different screen (Location Rationale Screen).</li>
<li>We also want to track user interactions with the Analytics service.</li>
</ol>
<h2>Basics</h2>
<p>The very first thing I remember learning about Jetpack Compose is this equation: <code>UI = f(state)</code>. This means that the UI is a result of a function applied to a certain state. Let's do a quick catch-up on the important aspects of Compose and Reactive UI in general, specifically regarding state handling: State Hoisting and Uni-Directional Data Flow.</p>
<h3>State Hoisting</h3>
<p>State hoisting is a technique used in software development, particularly in UI programming, where the responsibility for managing and manipulating the state of a component is moved to a higher-level component or a more centralized location. The purpose of state hoisting is to improve code organization, reusability, and maintainability. You can learn more about state hoisting <a href="https://developer.android.com/jetpack/compose/state">here</a>.</p>
<h3>Uni-directional Data Flow</h3>
<blockquote>
<p>A uni-directional data flow (UDF) is a design pattern where the state flows down, and events flow up. By following uni-directional data flow, you can decouple composables that display the state in the UI from the parts of your app that store and change the state.</p>
</blockquote>
<p>The gist of it is that we want our UI components to consume the state and emit events. Having our components handle events that originated from outside breaks this rule, introducing multiple sources of truth. The important part is that any "event" we introduce should be based on the state.</p>
<h2>Getting Started</h2>
<p>First, let us introduce core components, the bricks that will be the groundwork of our architecture.</p>
<h3>State</h3>
<p>Let us start with the obvious thing first, the <code>State</code>
The <code>State</code> can be whatever makes sense to you and your use case. It can be a data class that has all the properties your UI might need or a sealed interface that represent all the possible scenarios. In any case, the <code>State</code> is a "static" representation of your component or entire Screen UI that you can easily manipulate.</p>
<p>Based on our requirements, we have a list of Places and an optional error so our state could look like this</p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">data</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PlacesState</span></span>(
    <span class="hljs-keyword">val</span> places: List&#x3C;Place> = emptyList(),
    <span class="hljs-keyword">val</span> error: String? = <span class="hljs-literal">null</span>
)
</code></pre>
<h3>Screen</h3>
<p>The <code>Screen</code> is the <code>f</code> function of our equation. To follow the state hoisting pattern, we need to make this component stateless and expose user interactions as callbacks. This will make our Screen testable, previewable, and reusable!</p>
<p>We already have the state, and based on our requirements, we have only two user interactions that we need to handle. So here is how our <code>Screen</code> could look like. We are also including other Composable states we might need, so they are hoisted outside of <code>Screen</code>.</p>
<pre><code class="hljs language-kotlin"><span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">PlacesScreen</span><span class="hljs-params">(
    state: <span class="hljs-type">PlacesState</span>,
    pagerState: <span class="hljs-type">PagerState</span>,
    onFavoritesButtonClick: (<span class="hljs-type">Place</span>) -> <span class="hljs-type">Unit</span>,
    onNavigateToPlaceButtonClick: (<span class="hljs-type">Place</span>) -> <span class="hljs-type">Unit</span>
)</span></span> {
    Scaffold {
        PlacesPager(
          pagerState = pagerState,
          state = state,
          onFavoritesButtonClick = onFavoritesButtonClick,
          onNavigateToPlaceButtonClick = onNavigateToPlaceButtonClick
        )
    }
}
</code></pre>
<h3>Route</h3>
<p>Our last missing piece of the puzzle is the component that would handle these callbacks, provide state to <code>Screen</code> and emit it—introducing <code>Route</code>. <code>Route</code> is an entry point to our flow. Let's see how our version of <code>Route</code> would look like</p>
<pre><code class="hljs language-kotlin"><span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">PlacesRoute</span><span class="hljs-params">(
    navController: <span class="hljs-type">NavController</span>,
    viewModel: <span class="hljs-type">PlacesViewModel</span> = hiltViewModel()</span></span>,
) {
    <span class="hljs-comment">// ... state collection</span>

    LaunchedEffect(state.error) {
        state.error?.let {
            context.showError()
            viewModel.dismissError()
        }
    }

    PlacesScreen(
        state = uiState,
        onFavoritesButtonClick = <span class="hljs-comment">//..</span>
        onNavigateToPlaceClick = {
            <span class="hljs-keyword">when</span> {
                permissionState.isGranted -> {
                    analyitcs.track(<span class="hljs-string">"StartRoutePlanner"</span>)
                    navController.navigate(<span class="hljs-string">"RoutePlanner"</span>)
                }
                permissionState.shouldShowRationale -> {
                     analytics.track(<span class="hljs-string">"RationaleShown"</span>)
                     navController.navigate(<span class="hljs-string">"LocationRationale"</span>)
                }
                <span class="hljs-keyword">else</span> -> {
                    permissionState.launchPermissionRequest()
                }
            }
        }
    )
}
</code></pre>
<p>This is a very stripped version of the <code>PlacesRoute</code> function, and it is already quite big for what it is. With every new user interaction and state-based effects, this function will grow in size, making it harder to understand and maintain. Another issue is the callbacks. With every new user interaction, we will have to add another callback to the <code>PlacesScreen</code> declaration, and it can also become quite big.</p>
<p>On another note, let's think about testing. We can easily test the <code>Screen</code>, and the <code>ViewModel</code>, but what about the <code>Route</code>? It has a lot going on, and not everything can be easily mocked. For one, it is coupled with the <code>Screen</code>, so we won't be able to unit-test it properly without referencing it. Replacing other components with stubs will require us to move everything from into the <code>Route</code> declaration.</p>
<h2>Making changes</h2>
<p>Let's try and address these problems we have identified so far</p>
<h3>Actions</h3>
<p>The very first thing that to came to my mind looking at those callbacks is to group them somehow. And the very first thing I did  back in the day was this:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">sealed</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">PlacesAction</span> </span>{
    <span class="hljs-keyword">data</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NavigateToButtonClicked</span></span>(<span class="hljs-keyword">val</span> place: Place) : ParcelAction
    <span class="hljs-keyword">data</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FavoritesButtonClicked</span></span>(<span class="hljs-keyword">val</span> place: Place) : ParcelAction
}
</code></pre>
<p>While this allows us to group our actions into a well-defined structure, it brings different issues.</p>
<p>On the <code>Screen</code> level, we will have to instantiate the classes and invoke our <code>onAction</code> callback. If you are familiar with how Re-composition works when it comes down to lambdas, you might also have the urge to also surround it with <code>remember</code> to avoid unnecessary UI re-rendering</p>
<pre><code class="hljs language-kotlin"><span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">PlacesScreen</span><span class="hljs-params">(
    state: <span class="hljs-type">PlacesState</span>,
    onAction: (<span class="hljs-type">PlacesAction</span>) -> <span class="hljs-type">Unit</span>
)</span></span> {
    PlacesPager(
        onFavoritesButtonClicked = { onAction(PlacesAction.FavoritesButtonClicked(it))}
    )
}
</code></pre>
<p>On the other side, the <code>Route</code> also introduces another thing that I didn't like that much—possibly giant <code>when</code> statements.</p>
<pre><code class="hljs language-kotlin">PlacesScreen(
        state = uiState,
        onAction = { <span class="hljs-keyword">when</span>(it) {
        FavoritesButtonClick = <span class="hljs-comment">//..</span>
        NavigateToPlaceClicked = {
            <span class="hljs-keyword">when</span> {
                permissionState.isGranted -> {
                    analyitcs.track(<span class="hljs-string">"StartRoutePlanner"</span>)
                    navController.navigate(<span class="hljs-string">"RoutePlanner"</span>)
                }
                permissionState.shouldShowRationale -> {
                     analytics.track(<span class="hljs-string">"RationaleShown"</span>)
                     navController.navigate(<span class="hljs-string">"LocationRationale"</span>)
                }
                <span class="hljs-keyword">else</span> -> {
                    permissionState.launchPermissionRequest()
                }
            }
        }
    )
</code></pre>
<p>All this lead me to the solution which works way better, and it is a simple data class</p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">data</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ParcelActions</span></span>(
    <span class="hljs-keyword">val</span> onFavoritesClicked: (Place) -> <span class="hljs-built_in">Unit</span> = {},
    <span class="hljs-keyword">val</span> onNavigateToButtonClicked: (Place) -> <span class="hljs-built_in">Unit</span> = {},
)
</code></pre>
<p>This allows us to introduce the same level and ease of grouping our <code>Actions</code> related to our <code>Screen</code> and a more simple way to pass these actions to the relevant components.</p>
<pre><code class="hljs language-kotlin"><span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">PlacesScreen</span><span class="hljs-params">(
    state: <span class="hljs-type">PlacesState</span>,
    actions: <span class="hljs-type">PlacesActions</span>
)</span></span> {
    PlacesPager(
        onFavoritesButtonClicked = actions.onFavoritesButtonClicked,
        onNavigateToPlaceButtonClicked = actions.onNavigateToPlaceButtonClicked
    )
}
</code></pre>
<p>Now, on the <code>Route</code> side, we can also avoid the <code>when</code> statements and introduce the following utility to not recreate the <code>Actions</code> class every re-composition, leaving the <code>Route</code> much more concise.</p>
<pre><code class="hljs language-kotlin"><span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">PlacesRoute</span><span class="hljs-params">(
    viewModel: <span class="hljs-type">PlacesViewModel</span>,
    navController: <span class="hljs-type">NavController</span>,
)</span></span> {

    <span class="hljs-keyword">val</span> uiState <span class="hljs-keyword">by</span> viewModel.stateFlow.collectAsState()
   
    <span class="hljs-keyword">val</span> actions = rememberPlacesActions(navController)


    LaunchedEffect(state.error) {
        state.error?.let {
            context.showError()
            viewModel.dismissError()
        }
    }

    PlacesScreen(
        state = uiState,
        actions = actions
    )

}

<span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">rememberPlacesActions</span><span class="hljs-params">(
    navController: <span class="hljs-type">NavController</span>,
    analytics: <span class="hljs-type">Analytics</span> = LocalAnalytics.current,
    permissionState: <span class="hljs-type">PermissionState</span> = rememberPermissionState()</span></span>,
) : PlacesActions {
    <span class="hljs-keyword">return</span> remember(permissionState, navController, analytics) {
        PlacesActsions(
            onNavigateToPlaceClick = {
            <span class="hljs-keyword">when</span> {
                permissionState.isGranted -> {
                    analyitcs.track(<span class="hljs-string">"RoutePlannerClicked"</span>)
                    navController.navigate(<span class="hljs-string">"RoutePlanner"</span>)
                }
                permissionState.shouldShowRationale -> {
                     analytics.track(<span class="hljs-string">"RationaleShown"</span>)
                     navController.navigate(<span class="hljs-string">"LocationRationale"</span>)
                }
                <span class="hljs-keyword">else</span> -> {
                    permissionState.launchPermissionRequest()
                }
            }
        }
        )
    }   
}
</code></pre>
<p>While the <code>PlacesRoute</code> is now more straightforward, all we did is moved all its Actions logic to another function, and it didn't improve either readability or scalability. Moreover, our second issue is still intact - state-based effects. Our UI logic is now also split, introducing inconsistencies, and we didn't make it any more testable. It is time we introduce one last component.</p>
<h3>Coordinator</h3>
<p>At its core, the <code>Coordinator,</code> as you might have guessed from its name, is here to coordinate different action handlers and state providers. The <code>Coordinator</code> observes and reacts to the state changes and handles user actions. You can think of it as Compose state of our flow. In our stripped example, the Coordinator would look like this.</p>
<p>Notice that since our <code>Coordinator</code> now is not inside a composable scope, we can everything in a more straightforward way, without the need for <code>LaunchedEffect</code>, just like we would normally do in our <code>ViewModel</code> except here our business logic here - is UI logic.</p>
<pre><code class="hljs language-kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PlacesCoordinator</span></span>(
    <span class="hljs-keyword">val</span> viewModel: PlacesViewModel,
    <span class="hljs-keyword">val</span> navController: NavController,
    <span class="hljs-keyword">val</span> context: Context,
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> permissionState: PermissionState,
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> scope: CoroutineScope
) {

    <span class="hljs-keyword">val</span> stateFlow = viewModel.stateFlow

    <span class="hljs-keyword">init</span> {
        <span class="hljs-comment">// now we can observe our state and react to it</span>
        stateFlow.errorFlow
            .onEach { error ->
                context.toast(error.message)
                viewModel.dismissError()
            }.launchIn(scope)
    }

    <span class="hljs-comment">// and handle actions</span>
    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">navigateToRoutePlanner</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">when</span> {
            permissionState.isGranted -> {
                viewModel.trackRoutePlannerEvent()
                navController.navigate(<span class="hljs-string">"RoutePlanner"</span>)
            }
            permissionState.shouldShowRationale -> {
                viewModel.trackRationaleEvent()
                navController.navigate(<span class="hljs-string">"LocationRationale"</span>)
            }
            <span class="hljs-keyword">else</span> -> permissionState.launchPermissionRequest()
        }
    }

}
</code></pre>
<p>This allows us to modify our Actions utility</p>
<pre><code class="hljs language-kotlin"><span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">rememberPlacesActions</span><span class="hljs-params">(
   coordinator: <span class="hljs-type">PlacesCoordinator</span>
)</span></span> : PlacesActions {
    <span class="hljs-keyword">return</span> remember(coordinator: PlacesCoordinator) {
        PlacesActsions(
            onFavoritesButtonClicked = coordinator.viewModel::toggleFavorites,
            onNavigateToPlaceButtonClicked = coordinator::navigateToRoutePlanner
        )
}
</code></pre>
<p>And our <code>Route</code></p>
<pre><code class="hljs language-kotlin"><span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">PlacesRoute</span><span class="hljs-params">(
    coordinator: <span class="hljs-type">PlacesCoordinator</span> = rememberPlacesCoordinator()</span></span>
) {

    <span class="hljs-keyword">val</span> uiState <span class="hljs-keyword">by</span> coordinator.stateFlow.collectAsState()
   
    <span class="hljs-keyword">val</span> actions = rememberPlacesActions(coordinator)

    PlacesScreen(
        state = uiState,
        actions = actions
    )

}
</code></pre>
<p>In our example, the <code>PlacesCoordinator</code> is now responsible for UI Logic happening in our feature flow. Since it knows about different states, we can easily react to state changes and build conditional logic for every user interaction. In case the interaction is straightforward, we can easily delegate it to the relevant component, such as <code>ViewModel</code>.</p>
<p>Another thing that we can do by having the Coordinator is to control which state is exposed to the Screen. In case we have multiple <code>ViewModels</code> or <code>ViewModel</code> state that is too big for the <code>Screen</code> that we are dealing with, we can either combine these states or expose a partial state.</p>
<pre><code class="hljs language-kotlin">    <span class="hljs-keyword">val</span> screenStateFlow = viewModel.stateFlow.map { PartialScreenState() }
    <span class="hljs-comment">// or</span>
    <span class="hljs-keyword">val</span> screenStateFlow = combine(vm1.stateFlow, vm2.stateFlow) { ScreenStateFlow() }
</code></pre>
<p>Another bonus, the entire flow UI logic is now decoupled from the <code>Route</code>, meaning we can use our <code>Coordinator</code> as part of another Route without duplicating the important stuff and keeping the <code>Screen</code> part state-less.</p>
<pre><code class="hljs language-kotlin"><span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">TwoPanePlacesRoute</span><span class="hljs-params">(
    detailsCoordinator: <span class="hljs-type">PlacesDetailsCoordinator</span>,
    placesCoordinator: <span class="hljs-type">PlacesCoordinator</span>
)</span></span> {
    
    TwoPane(
        first = {
            PlacesScreen(
                state = placesCoordinator.state,
                actions = rememberPlacesActions(placesCoordinator)
            )
        },
        second = {
            PlaceDetailsScreen(
                state = detailsCoordinator. state,
                actions = rememberDetailsActions(detailsCoordinator)
            )
        }
    )
}
</code></pre>
<p>And finally, now we can test our UI logic by testing the component that implements it. Let us see how we can test our Coordinator by using our navigate to Rationale Screen when permission was denied as the example.</p>
<blockquote>
<p>⚠️ This part assumes that you have some knowledge of how to test Composable components.</p>
</blockquote>
<pre><code class="hljs language-kotlin"><span class="hljs-comment">/**
 * GIVEN
 * - Permission was denied previously
 * WHEN
 * - Navigate Button Clicked
 * THEN
 * - Current destination is "locationRationale"
 */</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">test_NavigateToRatinoleIfPermissionWasDeniedBefore</span><span class="hljs-params">()</span></span> {
     composeRule.setContent {
            <span class="hljs-comment">// 1</span>
            ComposableUnderTest(
                coordinator = rememberPlacesCoordinator(
                    navController = testNavController,
                    viewModel = NearbyPlacesViewModel()
                )
            )
        }
        
        <span class="hljs-comment">// 2</span>
        composeRule.onNode(hasText(<span class="hljs-string">"Navigate"</span>)).performClick()

        <span class="hljs-comment">// 3</span>
        Assert.assertEquals(
            <span class="hljs-string">"locationRationale"</span>,
            navController.currentBackStackEntry?.destination?.route
        )
}
</code></pre>
<p>Let's quickly walk through this test.</p>
<ol>
<li>First, we emit the Composable UI that we use as our test subject. This  UI has a simple structure and directly calls our <code>Coordinator</code>.</li>
</ol>
<pre><code class="hljs language-kotlin"> <span class="hljs-meta">@Composable</span>
<span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">ComposableUnderTest</span><span class="hljs-params">(coordinator: <span class="hljs-type">NearbyPlacesCoordinator</span>)</span></span> {
    NavHost(
        navController = coordinator.navController,
        startDestination = <span class="hljs-string">"home"</span>
    ) {
        composable(<span class="hljs-string">"home"</span>) {
            Button(onClick = { coordinator.navigateToPlace(Place.Mock) }) {
                Text(text = <span class="hljs-string">"Navigate"</span>)                
            }
         }
        composable(<span class="hljs-string">"locationRationale"</span>) {
            Text(text = <span class="hljs-string">"No permission"</span>)
        }
    }
}
</code></pre>
<ol start="2">
<li>Then, we programmatically click the "Navigate" button to trigger the action and let the <code>Coordinator</code> handle it.</li>
<li>Finally, we check that our assumption is valid and our implementation is working by checking that the current destination in our <code>NavHostController</code> is the one we expect</li>
</ol>
<p>And that's it. Let us summarize the things we refactored and what we achieved.</p>
<ol>
<li>Our <code>Screen</code> remains completely state-less. It only relies on whatever is being passed as the function parameter. All the user interactions are exposed via <code>Actions</code> for the other components to handle.</li>
<li>The <code>Route</code> now serves as a simple entry point in our Navigation Graph. It collects the state, remembers our Actions across re-compositions</li>
<li>The <code>Coordinator</code> is now doing most of the heavy lifting: reacting to state changes and delegating user interactions to other relevant components. It is completely decoupled from our <code>Screen</code> and <code>Route</code> and can be reused in another <code>Route</code> and tested separately</li>
</ol>
<p>The following diagram demonstrates the flow of data we now have</p>
<p><img src="/assets/img/articles/2023-07-14-Jetpack-Compose-UI-Architecture/data_flow.svg" alt="Data Flow Diagram"></p>
<h2>Questions and answers</h2>
<p>This section contains some questions I've been asked a lot when presenting this approach. So I might as well give some answers right here in case you have been wondering the same thing while reading this article.</p>
<h3>Does every Composable Screen needs a Coordinator?</h3>
<p>The short answer: it depends! For a very simple flow, let's say a Dialog with two Actions. It might be an overkill. And you might as well drop the <code>Actions</code> data class altogether and handle these actions in your <code>Route</code>. For a screen that can grow in complexity over time, I would say it is worth investing in having it from the start or starting refactoring when you see that your <code>Route</code> grows.</p>
<h3>Is <code>LaunchedEffect</code> "Deprecated"?</h3>
<p>Of course, it is not! Again, a simple screen without <code>Coordinator</code> might as well use the <code>LaunchedEffect</code> to react to state changes, and it is completely fine. And you still can use <code>LaunchedEffect</code> in your <code>Screen</code> when the UI logic lives and dies in the <code>Screen</code> layer, for example, animations.</p>
<h3>The <code>Route</code> doesn't do much</h3>
<p>Yes, in our example, the <code>Route</code> is pretty lightweight in terms of responsibilities. But having it as a navigation entry point means much more. A lot of effects that are not state-based belong to the <code>Route</code> to handle. For example, we can use <code>SideEffect</code> to adjust the color or put a <code>BackHandler</code> to intercept back button presses which would not always makes sense inside the <code>Screen</code>.</p>
<h3>Won't the <code>Coordinator</code> grow over time in the same way the <code>Route</code> would?</h3>
<p>Most probably, yes. And that's probably a sign that it is doing too much, and some of the things can be extracted into another stateful component or even another coordinator. In the same way, you extract different UI components from your Screen that encapsulate some piece of UI; you can build other components or coordinators that would encapsulate UI logic.</p>
<h2>Additional Resources</h2>
<h3>IDE Plugin</h3>
<p>If you think that there are a lot of files in this approach, I've got you covered! Please check <a href="https://plugins.jetbrains.com/plugin/19034-jetpack-compose-ui-architecture-templates">Jetpack Compose UI Architecture IDE Plugin</a></p>
<p>This plugin has been published a while back, and there is a chance you are using it already. If you are, I thank you, and I hope now you have more context about how each component works. If not, it will be a nice place to get started. Either way, I hope both this article and the plugin will help you in your dev routine.</p>
<h3>Documentation and Best Practices</h3>
<p>I was not able to cover everything in this article, so there are also <a href="https://levinzonr.github.io/compose-ui-arch-docs/">GitHub Pages</a> available where you can learn more about the presented approach. I'm planning to update these alongside the IDE plugin to accommodate any feedback that will come my way.</p>
<h2>Afterword</h2>
<p>Thank you for reading this article, and I hope it has sparked your interest in the proposed solution. As I shared my thought process that started a few years ago, it was an enjoyable experience to recreate and reflect upon the journey.</p>
<p>_ Article Photo by <a href="https://unsplash.com/@chatelp?utm_source=unsplash&#x26;utm_medium=referral&#x26;utm_content=creditCopyText">Pierre Châtel-Innocenti</a> on <a href="https://unsplash.com/photos/3JASCX85G_w?utm_source=unsplash&#x26;utm_medium=referral&#x26;utm_content=creditCopyText">Unsplash</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Empowering Mobile Applications through Blockchain: Our Journey, Part 1]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/07/12/Empowering-Mobile-Applications-through-Blockchain-Our-Journey-Part-1</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/07/12/Empowering-Mobile-Applications-through-Blockchain-Our-Journey-Part-1</guid>
            <pubDate>Wed, 12 Jul 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>In a world that is rapidly digitalizing, the need for a secure, transparent, and decentralized mode of operations is ever-increasing. Enter the realm of Blockchain, a groundbreaking technology that has revolutionized industries across the globe. But how does it connect to mobile applications? Let's delve deeper.</p>
<p>Blockchain, at its core, is a distributed ledger that maintains a continuously growing list of records, called blocks, which are secured using cryptography. The most compelling feature of Blockchain is its invulnerability to data modification, instilling the technology with trust, transparency, and security.</p>
<p>Riding on the backbone of Blockchain, we come across the popular Web3. Web3 or Web 3.0, represents the new era of the internet - one that is built on the principles of decentralization. Web3 aims to create an online environment where users maintain control over their own data and interactions, a concept that is inherently symbiotic with Blockchain.</p>
<p>Based on that, we can tell that the integration of Blockchain and Web3 into mobile apps holds tremendous potential and is set to revolutionize various industries. Some key areas where we can expect to see the impact could be: decentralized finance, in-app digital identity or decentralized social networks, among others.</p>
<p>Therefore, we realized the potential of using this technology in mobile apps and decided to catch up within our team. Basically, we decided to create an initial, simple project where we could establish an environment for experimentation and gain practical experience with Blockchain. Furthermore, we aimed to leverage this experience for future projects.</p>
<p>In the following sections, we will introduce you to some crucial terminologies associated with Blockchain and share the idea behind our city hall app leveraging Blockchain. In the future, we will publish a second post to discuss its development process and technological background.</p>
<p>This journey will provide you insights into the future of Blockchain technology within the realm of mobile apps, and how it's set to revolutionize the way we engage with digital platforms. So, get ready for an intriguing exploration into the world of Blockchain and mobile applications. Let's delve in!</p>
<h1>Some new terminologies</h1>
<h3>dApps</h3>
<p>dApps stands for <strong>Decentralized Applications</strong>. dApps are software applications that run on a decentralized network, typically utilizing blockchain technology. Unlike traditional applications that rely on centralized servers, dApps operate on a network where data and transactions are distributed across multiple nodes. A node is a computer or device that maintains a copy of the entire blockchain and verifies the validity of transactions.</p>
<p>Key features:</p>
<ul>
<li>
<p><strong>Decentralization</strong>: dApps operate on a decentralized network, which means there is no single point of control or failure. The application's logic and data are stored and processed on a blockchain or similar distributed ledger technology.</p>
</li>
<li>
<p><strong>Open Source</strong>: dApps are often open source, meaning their source code is publicly available and transparent.</p>
</li>
<li>
<p><strong>Tokenization</strong>: Many dApps have their own native tokens or cryptocurrencies that facilitate transactions within the application or represent ownership rights and value.</p>
</li>
<li>
<p><strong>Smart Contracts</strong>: A smart contract in dapps is an automated, self-executing program that enforces predefined rules on a blockchain.</p>
</li>
</ul>
<figure>
<img src="/assets/img/articles/2023-07-12-Empowering-Mobile-Applications-through-Blockchain-Our-Journey-Part-1/image1.png">
<figcaption>Image of dApps as decentralized applications, source: https://cryptoadventure.com/wp-content/uploads/2020/06/dapps-developpment.jpg</figcaption>
</figure>
<p>In this article, we describe the development of a simple dApp with <strong>decentralized</strong> features and <strong>smart contracts</strong>.</p>
<h3>Smart Contract</h3>
<p>A smart contract is a computer program that automates, verifies, and enforces the terms of a contract or agreement between multiple parties. It is a self-executing contract with the terms and conditions directly written into code.</p>
<p>The key characteristics of a smart contract include:</p>
<ul>
<li>
<p><strong>Automation</strong>: A smart contract automatically executes actions or transactions when predefined conditions are met. It eliminates the need for intermediaries or centralized authorities, as the code itself enforces the terms.</p>
</li>
<li>
<p><strong>Digital and Immutable</strong>: Smart contracts exist in a digital form, written in code, and are stored on a blockchain. Once deployed, the code becomes immutable, meaning it cannot be altered or tampered with.</p>
</li>
<li>
<p><strong>Decentralization</strong>: Smart contracts operate on a decentralized network, such as a blockchain, where multiple participants validate and record transactions. This decentralized nature ensures transparency and removes the need for a single central authority.</p>
</li>
<li>
<p><strong>Trust and Security</strong>: Smart contracts leverage cryptographic techniques to secure transactions and ensure that parties involved can trust the execution and outcome of the contract. The decentralized consensus mechanism of the blockchain adds an extra layer of security.</p>
</li>
</ul>
<figure>
<img src="/assets/img/articles/2023-07-12-Empowering-Mobile-Applications-through-Blockchain-Our-Journey-Part-1/image2.webp">
<figcaption>Example of a Smart contract deployed in Polygonscan</figcaption>
</figure>
<p>For the project described in this article, in order to deploy smart contracts and add records to the blockchain we created a <strong>wallet</strong> in <a href="https://metamask.io/">Metamask</a>.</p>
<h3>Wallet</h3>
<p>A Web3 wallet is a cryptographic tool that empowers individuals to exercise control over their digital identity, assets, and interactions in the decentralized landscape. It encapsulates a combination of secure key management, authentication mechanisms, and user-friendly interfaces, allowing users to securely store, send, receive, and manage their blockchain-based assets while seamlessly interacting with dApps and blockchain networks.</p>
<p>For the project described in this article we created a Wallet in Metamask. Metamask functions as a bridge between the user and the blockchain network by a browser extension.</p>
<figure>
<img src="/assets/img/articles/2023-07-12-Empowering-Mobile-Applications-through-Blockchain-Our-Journey-Part-1/image3.webp">
<figcaption>Metamask, https://metamask.io/</figcaption>
</figure>
<p>All operations that triggered a modification in the blockchain, including the creation of the smart contract, transfers of tokens, or addition of records, are recorded in <strong>transactions</strong> that are visible in the Metamask Wallet.</p>
<h3>Transactions</h3>
<p>A transaction is an operation executed in a blockchain. Such operations involve interacting with a smart contract, the contract's code is executed according to the specified function call and parameters. The smart contract may modify its internal state, update balances, emit events, or trigger further actions based on the transaction's logic.</p>
<p>When a transaction is triggered on a blockchain network, several steps occur:</p>
<ol>
<li>
<p><strong>Initiation and Validation</strong>: The transaction is created and initiated by a user or an application. Then, the transaction is broadcasted to the blockchain network, where it undergoes a validation process. Miners (in a proof-of-work network) or validators (in a proof-of-stake network) verify the transaction's validity by checking if the sender has sufficient funds, the transaction format is correct, and any additional conditions imposed by the network's consensus rules are met.</p>
</li>
<li>
<p><strong>Inclusion in a Block</strong>: Once the transaction is validated, it is grouped together with other validated transactions to form a block. The block contains a collection of transactions and serves as a permanent record on the blockchain.</p>
</li>
<li>
<p><strong>Mining or Block Finalization</strong>: Mining in a blockchain is the process of validating transactions and adding them to the blockchain while earning rewards for doing so. On the other hand, miners in a blockchain are participants who use computational power to verify and process transactions, adding them to the blockchain and ensuring the network's security and consensus. They are rewarded with cryptocurrency for their efforts. In a proof-of-work network, miners compete to solve a cryptographic puzzle, which requires computational effort. The first miner to solve the puzzle adds the new block (containing the transaction) to the blockchain. In a proof-of-stake network, validators take turns adding blocks based on their stake and selection algorithms.</p>
</li>
<li>
<p><strong>Consensus and Confirmation</strong>: The newly added block is propagated to other nodes in the network, and they validate and confirm its inclusion. Confirmations represent the number of subsequent blocks added to the blockchain on top of the block containing the transaction. The more confirmations a transaction receives, the higher its finality and security.</p>
</li>
<li>
<p><strong>Updating the Blockchain State</strong>: After the transaction is executed, the state of the blockchain is updated to reflect the changes. This includes updating account balances, modifying the state of the smart contract, and recording any relevant events or logs associated with the transaction.</p>
</li>
</ol>
<figure>
<img src="/assets/img/articles/2023-07-12-Empowering-Mobile-Applications-through-Blockchain-Our-Journey-Part-1/image4.webp">
<figcaption>List of transactions of a smart contract</figcaption>
</figure>
<p>In the project described in this article, only two kinds of transactions were executed:</p>
<ul>
<li>
<p>When deploying a smart contract.</p>
</li>
<li>
<p>When adding Pension and Tax records to the blockchain by invoking a method that updated the state in the blockchain.</p>
</li>
</ul>
<h3>Our Idea: A City Hall residence tax records app</h3>
<p>The project in this blog post is a mobile application that allows users to verify their pension and tax records stored on the blockchain. For easy understanding, we called the app Manekin which stands for <strong>Money + Kin</strong> (Kin means money in Japanese, then マネー + 金 =マネキン) .</p>
<p>We thought that this kind of app would be a potential idea to be developed on a blockchain-based system instead of a typical cloud-based system in the upcoming years. The reason is that by storing pension and tax records on a blockchain, trust and security will be enhanced, because the data retrieved from the blockchain is immutable and reliable. However, it is important to note that blockchain technology may not be suitable for all types of applications, and in a real product, many constraints such as scalability and performance should be taken into account when designing a dApp.</p>
<p>These are the main features of our app:</p>
<ul>
<li><strong>User authentication</strong>:
<ul>
<li>The app provides a login process where users can authenticate by entering their user id.</li>
<li>The user id can be entered by scanning a QR code or manually through a text field.</li>
<li>User authentication ensures that only authorized individuals can access their specific records.</li>
<li>Logout process, stops access to the records until the next login.</li>
</ul>
</li>
</ul>
<figure>
<img src="/assets/img/articles/2023-07-12-Empowering-Mobile-Applications-through-Blockchain-Our-Journey-Part-1/image5.webp">
<figcaption>Mock up of our app idea</figcaption>
</figure>
<ul>
<li><strong>Tax and pension records retrieval</strong>:
<ul>
<li>The app invokes a method in the smart contract, which interacts with the blockchain and retrieves the records associated with the user id.</li>
<li>The retrieved records include details such as the paid amount, timestamp, contribution type (tax or pension) and a note with relevant information about the record.</li>
</ul>
</li>
</ul>
<figure>
<img src="/assets/img/articles/2023-07-12-Empowering-Mobile-Applications-through-Blockchain-Our-Journey-Part-1/image6.webp">
<figcaption>Mock up of our app idea</figcaption>
</figure>
<ul>
<li><strong>User-friendly interface</strong>:
<ul>
<li>We offer an intuitive and user-friendly interface with easy-to-navigate screens.</li>
<li>The application communicates with a smart contract and is deployed on a Blockchain Test network. This all goes under the hood and the user has the experience as a normal database.</li>
<li>By providing a user-friendly interface and seamless integration with the smart contract, the app enables users to securely access and review their records.</li>
</ul>
</li>
</ul>
<h1>Beyond a City Hall app</h1>
<p>The above idea is an idea from the digital identity and security field, where users have control over their personal data and information is more secure without relying on traditional centralized authentication systems. Digital identity and security is one of the areas expected to see an impact using Blockchain technology in the near future. Other examples from other areas could be:</p>
<ol>
<li>
<p><strong>Decentralized Finance (DeFi)</strong>: A bank app enabling users to access decentralized financial services, such as lending, borrowing, and trading cryptocurrencies, directly from their smartphones. Blockchain-based smart contracts ensure transparency and security in financial transactions, allowing for peer-to-peer interactions without intermediaries.</p>
</li>
<li>
<p><strong>Supply Chain Management</strong>: A fashion brand integrated with blockchain technology. This one could track and verify the provenance and authenticity of products throughout the supply chain. This transparency helps in combating counterfeit goods, ensuring fair trade practices, and enhancing consumer trust.</p>
</li>
<li>
<p><strong>Decentralized Social Networking</strong>: Web3-powered mobile apps can enable decentralized social networking, where users have control over their data and interactions. They can participate in social platforms without concerns about data breaches or censorship, as content is distributed across a decentralized network.</p>
</li>
<li>
<p><strong>Voting and Governance</strong>: An elections app could enable secure and transparent voting systems, ensuring the integrity of elections and decision-making processes. Web3 principles of decentralization and immutability enhance trust and participation in governance.</p>
</li>
</ol>
<p>The integration of Blockchain and Web3 into mobile apps expands the possibilities for user-centric, secure, and transparent experiences. As the technology evolves and user adoption increases, we can expect to witness a paradigm shift in how we interact with mobile applications, empowering individuals and transforming various industries.</p>
<h1>To be continued...</h1>
<p>In conclusion, the integration of Blockchain and Web3 into mobile apps marks an exciting frontier in technology and innovation. The decentralized nature of blockchain technology, coupled with the principles of Web3, empowers individuals with greater control over their data, privacy, and digital interactions.</p>
<p>The potential of Blockchain and Web3 in mobile apps is vast, and as the technology advances, we can anticipate even more innovative use cases. Developers and entrepreneurs are increasingly exploring these possibilities, paving the way for a decentralized future where individuals have greater control and ownership over their digital lives.</p>
<p>In the next blog post, we will delve deeper into the development phase and how we did it to develop an app using this new evolving technology. Stay tuned to learn more about it.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building desktop app with flutter : Tutorial]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/07/05/Desktop-App-in-Flutter</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/07/05/Desktop-App-in-Flutter</guid>
            <pubDate>Wed, 05 Jul 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Welcome to Flutter calculator app tutorial blog for desktop! In this tutorial, I will guide you through the process of creating a fully functional calculator application using Flutter, Google's powerful framework for building cross-platform applications.</p>
<p>Calculators are essential tools that are used by millions of people every day, and by creating a calculator app using Flutter, you'll not only learn the fundamentals of desktop app development but also gain valuable insights into building user-friendly and efficient interfaces.</p>
<p>Throughout this tutorial blog, we will start from scratch and cover all the necessary steps to develop a feature-rich calculator app. We'll assume you have a basic understanding of Flutter, but even if you're new to Flutter, don't worry! I will explain the concepts and code snippets in a beginner-friendly manner.</p>
<p>Our journey will begin by setting up the development environment for Flutter desktop app development. I will guide you through the installation process, ensuring that you have all the necessary tools and dependencies ready to start coding your calculator app.</p>
<p>Next, we'll dive into the design and layout of the calculator user interface. We'll explore various Flutter widgets and layout options to create a visually appealing and intuitive UI for our calculator. You'll learn how to handle user input, process mathematical operations, and display results in real-time.</p>
<p>Throughout the tutorial, we'll focus on best practices for code organization, reusability, and maintainability. You'll learn how to structure your codebase effectively, separate concerns, and utilize Flutter's widget composition model to build a scalable and extensible calculator app.</p>
<p>By the end of this tutorial series, you'll have a solid understanding of how to build a complete calculator application for desktop using Flutter. You'll be equipped with the skills to extend and customize your calculator app further or even venture into building other types of desktop applications.</p>
<p>So, get ready to embark on this exciting journey of Flutter desktop app development, where you'll learn, create, and have fun building your very own calculator app. Let's dive into the world of Flutter!</p>
<h2>Setting Up the Development Environment</h2>
<p>Enabling Configuration:</p>
<p>Go to your root folder in your project and type in the following command for macOS:</p>
<pre><code class="hljs language-lua">flutter <span class="hljs-built_in">config</span> <span class="hljs-comment">--enable-macos-desktop</span>
</code></pre>
<p>For Linux:</p>
<pre><code class="hljs language-lua">flutter <span class="hljs-built_in">config</span> <span class="hljs-comment">--enable-linux-desktop</span>
</code></pre>
<p>For Windows:</p>
<pre><code class="hljs language-lua">flutter <span class="hljs-built_in">config</span> <span class="hljs-comment">--enable-windows-desktop</span>
</code></pre>
<p>The terminal will ask you to restart; after restarting there would be no changes.
Now type the following command in the terminal:</p>
<p><code>flutter create .</code></p>
<p>and then run the command:</p>
<p><code>flutter run -d macos</code></p>
<p>You can replace macos according to your platform
Once the command is run, the macos, linux and windows folders are seen and a screen is displayed as below:</p>
<p><img src="/assets/img/articles/2023-07-05-Desktop-App-in-Flutter/initial_screen.webp" alt=""></p>
<h2>Designing the User Interface</h2>
<h3>The state class</h3>
<p>The <code>CalculatorState</code> class represents the state of a calculator in a Flutter application. It contains two properties: <code>input</code> and <code>result</code>.</p>
<p><code>input</code>: Represents the current input entered by the user in the calculator. It is a String type that holds the user's input, such as numbers, operators, or mathematical expressions.</p>
<p><code>result</code>: Represents the calculated result based on the user's input. It is also a String type that holds the result of the calculations performed on the input.</p>
<p>The <code>CalculatorState</code> class has a constructor that initializes the input and result properties. The constructor takes optional named parameters <code>input</code> and <code>result</code>, which default to empty strings ('') if not provided.</p>
<pre><code class="hljs language-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CalculatorState</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> input;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> result;

  CalculatorState({<span class="hljs-keyword">this</span>.input = <span class="hljs-string">''</span>, <span class="hljs-keyword">this</span>.result = <span class="hljs-string">''</span>});
}
</code></pre>
<h3>Button widget</h3>
<p>The <code>Button</code> class is a custom Flutter widget that represents a calculator button with customizable properties. Let's go through the different parts of the code to understand its functionality:</p>
<ul>
<li>
<p>Properties:</p>
<ul>
<li><code>buttonColor</code>: Represents the background color of the button.</li>
<li><code>textColor</code>: Represents the color of the button text.</li>
<li><code>buttonText</code>: Represents the text displayed on the button.</li>
<li><code>buttontapped</code>: Represents a callback function that will be invoked when the button is tapped.</li>
</ul>
</li>
<li>
<p>Constructor:</p>
<ul>
<li>The <code>Button</code> class has a constructor that takes named parameters:
<ul>
<li><code>buttonColor</code>, <code>textColor</code>, <code>buttonText</code>: Optional parameters that allow you to customize the button's appearance.</li>
<li><code>buttontapped</code>: A required parameter that takes a <code>VoidCallback</code>, which is a function that doesn't return a value and is called when the button is tapped.</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>With this <code>Button</code> widget, you can create buttons in your Flutter UI by providing the desired properties, such as <code>buttonColor</code>, <code>textColor</code>, <code>buttonText</code>, and <code>buttontapped</code> callback. This allows you to create reusable buttons with customizable styles and behavior.</p>
<pre><code class="hljs language-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Button</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{

  <span class="hljs-keyword">final</span> Color? buttonColor;
  <span class="hljs-keyword">final</span> Color? textColor;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String?</span> buttonText;
  <span class="hljs-keyword">final</span> VoidCallback buttontapped;

  <span class="hljs-keyword">const</span> Button({
    Key? key,
    <span class="hljs-keyword">this</span>.buttonColor,
    <span class="hljs-keyword">this</span>.textColor,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.buttonText,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.buttontapped,
  }) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> GestureDetector(
      onTap: buttontapped,
      child: Padding(
        padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">6</span>),
        child: ClipRRect(
          child: Container(
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(<span class="hljs-number">56</span>),
              color: buttonColor ?? Colors.grey,
            ),
            child: Center(
              child: Text(
                buttonText ?? <span class="hljs-string">''</span>,
                style: TextStyle(
                  color: textColor ?? Colors.black,
                  fontSize: <span class="hljs-number">25</span>,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

</code></pre>
<h3>Calculator screen</h3>
<p>The list of buttons which will be included in the calculator :</p>
<pre><code class="hljs language-dart"><span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&#x3C;<span class="hljs-built_in">String</span>> buttons = [
<span class="hljs-string">'C'</span>,
<span class="hljs-string">'+/-'</span>,
<span class="hljs-string">'%'</span>,
<span class="hljs-string">'DEL'</span>,
<span class="hljs-string">'7'</span>,
<span class="hljs-string">'8'</span>,
<span class="hljs-string">'9'</span>,
<span class="hljs-string">'/'</span>,
<span class="hljs-string">'4'</span>,
<span class="hljs-string">'5'</span>,
<span class="hljs-string">'6'</span>,
<span class="hljs-string">'x'</span>,
<span class="hljs-string">'1'</span>,
<span class="hljs-string">'2'</span>,
<span class="hljs-string">'3'</span>,
<span class="hljs-string">'-'</span>,
<span class="hljs-string">'0'</span>,
<span class="hljs-string">'.'</span>,
<span class="hljs-string">'='</span>,
<span class="hljs-string">'+'</span>,
];


</code></pre>
<p>The following code snippet represents the building of a <code>GridView</code> with buttons inside a <code>Container</code>. Let's break it down:</p>
<pre><code class="hljs language-dart">Container(
  child: GridView.builder(
    itemCount: buttons.length,
    gridDelegate: <span class="hljs-keyword">const</span> SliverGridDelegateWithFixedCrossAxisCount(
      crossAxisCount: <span class="hljs-number">4</span>,
    ),
    itemBuilder: (BuildContext context, <span class="hljs-built_in">int</span> index) {
      <span class="hljs-keyword">if</span> (index == <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">return</span> Button(
          buttontapped: () {},
          buttonText: buttons[index],
        );
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (index == <span class="hljs-number">1</span>) {
        <span class="hljs-keyword">return</span> Button(
          buttonText: buttons[index],
          buttontapped: () {},
        );
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (index == <span class="hljs-number">2</span>) {
        <span class="hljs-keyword">return</span> Button(
          buttontapped: () {},
          buttonText: buttons[index],
        );
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (index == <span class="hljs-number">3</span>) {
        <span class="hljs-keyword">return</span> Button(
          buttontapped: () {},
          buttonText: buttons[index],
        );
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (index == <span class="hljs-number">18</span>) {
        <span class="hljs-keyword">return</span> Button(
          buttontapped: () {},
          buttonText: buttons[index],
          buttonColor: Colors.orange,
          textColor: Colors.white,
        );
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">return</span> Button(
          buttontapped: () {},
          buttonText: buttons[index],
          buttonColor: isOperator(buttons[index])
              ? Colors.orange
              : Colors.white30.withOpacity(<span class="hljs-number">0.3</span>),
          textColor: isOperator(buttons[index])
              ? Colors.white
              : Colors.black,
        );
      }
    },
  ),
),
</code></pre>
<p>The below grid view is used to create the Calculator screen:</p>
<pre><code class="hljs language-dart">Container(
  child: GridView.builder(
    itemCount: buttons.length,
    gridDelegate: <span class="hljs-keyword">const</span> SliverGridDelegateWithFixedCrossAxisCount(
      crossAxisCount: <span class="hljs-number">4</span>,
    ),
    itemBuilder: (BuildContext context, <span class="hljs-built_in">int</span> index) {
      <span class="hljs-keyword">if</span> (index == <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">return</span> Button(
          buttontapped: () {},
          buttonText: buttons[index],
        );
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (index == <span class="hljs-number">1</span>) {
        <span class="hljs-keyword">return</span> Button(
          buttonText: buttons[index],
          buttontapped: () {},
        );
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (index == <span class="hljs-number">2</span>) {
        <span class="hljs-keyword">return</span> Button(
          buttontapped: () {},
          buttonText: buttons[index],
        );
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (index == <span class="hljs-number">3</span>) {
        <span class="hljs-keyword">return</span> Button(
          buttontapped: () {},
          buttonText: buttons[index],
        );
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (index == <span class="hljs-number">18</span>) {
        <span class="hljs-keyword">return</span> Button(
          buttontapped: () {},
          buttonText: buttons[index],
          buttonColor: Colors.orange,
          textColor: Colors.white,
        );
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">return</span> Button(
          buttontapped: () {},
          buttonText: buttons[index],
          buttonColor: isOperator(buttons[index])
              ? Colors.orange
              : Colors.white30.withOpacity(<span class="hljs-number">0.3</span>),
          textColor: isOperator(buttons[index])
              ? Colors.white
              : Colors.black,
        );
      }
    },
  ),
),
</code></pre>
<p>This code creates a <code>GridView</code> with a dynamically generated list of buttons based on the <code>buttons</code> list. Here's how it works:</p>
<ul>
<li>
<p>The <code>GridView.builder</code> widget is used to build the grid dynamically based on the number of buttons in the <code>buttons</code> list.</p>
</li>
<li>
<p>The <code>itemCount</code> property of <code>GridView.builder</code> is set to the length of the <code>buttons</code> list.</p>
</li>
<li>
<p>The <code>gridDelegate</code> property is set to <code>SliverGridDelegateWithFixedCrossAxisCount</code> with <code>crossAxisCount</code> set to 4, which means there will be 4 buttons per row.</p>
</li>
<li>
<p>The <code>itemBuilder</code> callback is responsible for building each item in the grid. It takes the <code>BuildContext</code> and <code>index</code> as parameters.</p>
</li>
<li>
<p>Inside the <code>itemBuilder</code> callback, there is a series of conditions to determine the type of button to display based on the <code>index</code>.</p>
</li>
<li>
<p>When <code>index</code> is 0, 1, 2, or 3, it represents specific buttons like 'C', '+/-', '%', and 'DEL'. For these buttons, a <code>Button</code> widget is created with the corresponding label and an empty <code>buttontapped</code> callback.</p>
</li>
<li>
<p>When <code>index</code> is 18, it represents the '=' button. This button is created with a specific button color (orange) and text color (white). Again, an empty <code>buttontapped</code> callback is provided.</p>
</li>
<li>
<p>For all other indices, regular buttons are created. The <code>buttonColor</code> and <code>textColor</code> are set based on whether the button is an operator or not. The <code>isOperator</code> function is called to determine this.</p>
</li>
<li>
<p>The <code>buttontapped</code> callback for all the regular buttons is empty, as specified by <code>() {}</code>. You can replace this with the desired logic to handle button taps.</p>
</li>
</ul>
<p>Overall, this code snippet generates a grid of buttons inside a <code>Container</code> based on the <code>buttons</code> list, with specific customization for certain buttons and dynamic styling based on whether a button is an operator or not.</p>
<pre><code class="hljs language-dart"> <span class="hljs-built_in">bool</span> isOperator(<span class="hljs-built_in">String</span> x) {
    <span class="hljs-keyword">if</span> (x == <span class="hljs-string">'/'</span> || x == <span class="hljs-string">'x'</span> || x == <span class="hljs-string">'-'</span> || x == <span class="hljs-string">'+'</span> || x == <span class="hljs-string">'='</span>) {
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
    }
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
  }
</code></pre>
<p>The <code>isOperator</code>  method is a helper function used to determine if a given button label is an operator (/, x, -, +, =).
It returns true if the button label is an operator, and false otherwise.</p>
<p>The code below is the input space and the result space:</p>
<pre><code class="hljs language-dart">Expanded(
            child: SizedBox(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: &#x3C;Widget>[
                  Container(
                    padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">20</span>),
                    alignment: Alignment.centerRight,
                    child: Text(
                      <span class="hljs-string">''</span>,
                      style: <span class="hljs-keyword">const</span> TextStyle(fontSize: <span class="hljs-number">18</span>, color: Colors.white),
                    ),
                  ),
                  Container(
                    padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">15</span>),
                    alignment: Alignment.centerRight,
                    child: Text(
                      <span class="hljs-string">''</span>,
                      style: <span class="hljs-keyword">const</span> TextStyle(
                          fontSize: <span class="hljs-number">30</span>,
                          color: Colors.white,
                          fontWeight: FontWeight.bold),
                    ),
                  )
                ],
              ),
            ),
          ),
</code></pre>
<h2>View model</h2>
<p>The provided code defines a CalculatorViewModel class, which extends StateNotifier<calculatorstate>. This class is responsible for managing the state and logic of a calculator application. Let's break down the code and understand its functionality:</calculatorstate></p>
<pre><code class="hljs language-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CalculatorViewModel</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StateNotifier</span>&#x3C;<span class="hljs-title">CalculatorState</span>> </span>{
  CalculatorViewModel() : <span class="hljs-keyword">super</span>(CalculatorState());
</code></pre>
<p>The clear method clears the calculator's input and sets the result to '0'. It updates the state by assigning a new instance of CalculatorState using state = CalculatorState(input: '', result: '0').</p>
<pre><code class="hljs language-dart">  <span class="hljs-keyword">void</span> clear() {
    state = CalculatorState(input: <span class="hljs-string">''</span>, result: <span class="hljs-string">'0'</span>);
  }
</code></pre>
<p>The equalPressed method is responsible for evaluating the input expression and updating the state with the calculated result.</p>
<ul>
<li>It retrieves the input from the state and replaces 'x' with '*' to match the expression format.</li>
<li>The expression is then parsed using the Parser class from math_expressions.</li>
<li>The parsed expression is evaluated using the Expression class and a ContextModel.</li>
<li>The result is obtained as a double value, converted to a string, and stored in the answer variable.</li>
<li>Finally, the state is updated with the modified input expression and the calculated result.</li>
</ul>
<pre><code class="hljs language-dart">  <span class="hljs-keyword">void</span> equalPressed() {
    <span class="hljs-built_in">String</span> finaluserinput = state.input;
    finaluserinput = state.input.replaceAll(<span class="hljs-string">'x'</span>, <span class="hljs-string">'*'</span>);

    Parser p = Parser();
    Expression exp = p.parse(finaluserinput);
    ContextModel cm = ContextModel();
    <span class="hljs-built_in">double</span> eval = exp.evaluate(EvaluationType.REAL, cm);
    <span class="hljs-keyword">final</span> answer = eval.toString();
    state = CalculatorState(input: finaluserinput, result: answer);
  }
</code></pre>
<p>The updateInput method is responsible for appending the given operation (button label) to the calculator's input.
It retrieves the current input from the state, concatenates the operation, and assigns the updated input to the input variable.
The state is then updated with the modified input while keeping the result unchanged.</p>
<pre><code class="hljs language-dart"><span class="hljs-keyword">void</span> updateInput(<span class="hljs-built_in">String</span> operation) {
<span class="hljs-keyword">var</span> input = state.input;
input += operation;
state = CalculatorState(input: input, result: state.result);
}
</code></pre>
<p>The delete method removes the last character from the calculator's input.
It retrieves the current input from the state and creates a substring of the input excluding the last character.
The modified input is assigned to the finalInput variable.
The state is then updated with the modified input while keeping the result unchanged.</p>
<pre><code class="hljs language-dart">  <span class="hljs-keyword">void</span> delete() {
    <span class="hljs-keyword">var</span> input = state.input;
    <span class="hljs-keyword">final</span> finalInput = input.substring(<span class="hljs-number">0</span>, input.length - <span class="hljs-number">1</span>);

    state = CalculatorState(input: finalInput, result: state.result);
  }
</code></pre>
<p>The CalculatorViewModel class encapsulates the logic for handling button presses, performing calculations, and managing the state of the calculator. It utilizes the state property inherited from StateNotifier to update the state and notify listeners of state changes.</p>
<h2>Connecting the UI with the view model</h2>
<p>The provided code defines a <code>calculatorProvider</code> using the <code>StateNotifierProvider</code> from the <code>flutter_riverpod</code> package. This provider is responsible for creating and managing an instance of the <code>CalculatorViewModel</code> class and providing access to its associated state of type <code>CalculatorState</code> to other parts of the application.</p>
<p>Here's an explanation of the code:</p>
<pre><code class="hljs language-dart"><span class="hljs-keyword">final</span> calculatorProvider =
    StateNotifierProvider&#x3C;CalculatorViewModel, CalculatorState>(
  (ref) => CalculatorViewModel(),
);
</code></pre>
<ul>
<li>The <code>calculatorProvider</code> is declared as a constant using the <code>final</code> keyword.</li>
<li>It is assigned the result of the <code>StateNotifierProvider</code> constructor, which takes two type parameters: <code>CalculatorViewModel</code> and <code>CalculatorState</code>.</li>
<li>The first type parameter, <code>CalculatorViewModel</code>, specifies the type of the state notifier being provided. In this case, it is an instance of the <code>CalculatorViewModel</code> class.</li>
<li>The second type parameter, <code>CalculatorState</code>, specifies the type of the state managed by the state notifier. Here, it is the <code>CalculatorState</code> class.</li>
<li>The constructor argument <code>(ref) => CalculatorViewModel()</code> is a callback function that gets executed when the provider is first accessed. It creates a new instance of the <code>CalculatorViewModel</code> class and returns it. The <code>ref</code> parameter provides access to the provider's container and can be used to read other providers or perform additional setup.</li>
</ul>
<p>With the <code>calculatorProvider</code> defined, other parts of the application can access the <code>CalculatorViewModel</code> instance and its associated state using the <code>ProviderContainer</code> or by using the <code>ConsumerWidget</code> or <code>Consumer</code> widget provided by <code>flutter_riverpod</code>. This allows components to interact with the calculator's state and trigger updates based on user interactions or changes in the state.</p>
<p>To acees the state and reflect it in the UI we will add the below in the build function</p>
<pre><code class="hljs language-dart"><span class="hljs-keyword">final</span> viewModel = ref.read(calculatorProvider.notifier);
<span class="hljs-keyword">final</span> state = ref.watch(calculatorProvider);
</code></pre>
<p>After bulilding and running the project, the final app would look like this:
<img src="/assets/img/articles/2023-07-05-Desktop-App-in-Flutter/final_img.webp" alt=""></p>
<p>The full code can be found here:
<a href="https://github.com/huma11farheen/desktop_app">GithubRepo</a></p>
<p><em>Article Photo by <a href="https://bitrise.io/blog/post/build-and-deploy-a-flutter-desktop-app-for-linux">Moataz Nabil</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Embracing the Future of Data Analytics with Microsoft Fabric]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/06/15/Embracing-Future-Data-Analytics-Microsoft-Fabric</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/06/15/Embracing-Future-Data-Analytics-Microsoft-Fabric</guid>
            <pubDate>Thu, 15 Jun 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Launched on May 24, 2023, Microsoft Fabric is an innovative cloud-based SaaS analytics platform that streamlines data analytics by integrating multiple workloads into one comprehensive solution. Fabric covers a wide range of areas, including data ingestion, data warehousing, and data engineering, and represents a bold step toward the future of data analytics.</p>
<p>Previously, Microsoft's data analytics tools—Data Factory, Synapse, Power BI, Purview, and others—operated as individual services. That meant users had to set up, pay for, and connect to each tool individually. Such an approach complicated the end-to-end data process and increased costs. Microsoft Fabric, however, offers a strategic solution by combining these services into one integrated system, providing a more user-friendly and cost-effective solution. It provides a comprehensive range of fully integrated analytical experiences, each tailored to a unique task and user profile and addressing an end-to-end analytical requirement. Data Engineering, Azure Data Factory, Data Science, Data Warehouse, Real-Time Analytics, and Power BI are all included.</p>
<h2>An overview of the core features</h2>
<p>Microsoft Fabric can be seen as an all-in-one platform that gathers all of the necessary components for data analysis and is developed with various user roles in mind. It consists of several components, all of which are designed to make dealing with data easier. Microsoft has added Azure Data Factory in Fabric as a connector, bringing information from over 200 different places together in a simple and powerful way.</p>
<p>While Fabric's Data Warehouse functions as a sizable storage facility that houses all of your data efficiently and conveniently, Fabric's Data Science component, which is completely integrated with Azure Machine Learning, functions like a science lab for tech specialists to develop and deploy their data prediction models. Also available are real-time analytics and Power BI, a user-friendly tool for data visualization that enables anybody to access, comprehend, and base decisions on data. Last but not least we have Microsoft Fabric Spark integration with Data Factory which enables the scheduling and orchestration of Spark notebooks and jobs.</p>
<p>Let's take a closer look at some of Microsoft Fabric's standout features and have a quick debate on whether you should switch over immediately to this cutting-edge new data and analytics platform from Microsoft.</p>
<p><img src="/assets/img/articles/2023-06-15-Embracing-Future-Data-Analytics-Microsoft-Fabric/fabric_ml.webp" alt="Microsoft Fabric Overview"></p>
<h2>Storing and managing data with OneLake</h2>
<p>OneLake, a layer that lies above Azure Data Lake Storage Gen-2, is a fundamental component of Fabric. It transforms data lake management by consolidating several storage accounts from many geographical regions into a single user-friendly layer. Furthermore, whenever a new workspace is created, OneLake immediately establishes a new storage area within the existing Data Lake, ensuring easy access, speed, and compliance with data governance rules.</p>
<p>OneLake is a Fabric component that simplifies how organizations store and retrieve data. Consider it a gigantic virtual filing cabinet that consolidates data from various locations into a single, easily accessible location. This makes finding and using data easier while also keeping it secure.</p>
<h2>Improved collaboration with Workspaces</h2>
<p>Fabric's use of workspaces, which might be familiar to you from Power BI, operating as a centralized access point within Fabric, is one of its notable features. These workspaces provide secure, specialized areas for certain groups to store, distribute, and manage data and project components, which are referred to as "artifacts." This novel solution allows several users to collaborate inside the same workspace while using the same data, without the requirement for data duplication or relocation between environments or cloud services.</p>
<p>Fabric workspaces are similar to digital rooms where teams may store and share data. They're similar to the shared folders that many of us use every day, but they're built to manage large amounts of data and give teams more control over who can access them.</p>
<h2>Keeping your data secure with OneSecurity</h2>
<p>OneSecurity, a Fabric security feature, delivers full workspace-level security that integrates seamlessly with Power BI. Governance and compliance measures are already built into Azure Data Lake Storage and Power BI. Furthermore, Microsoft's Purview is intended to support Fabric's governance and compliance.</p>
<p>Simply defined, OneSecurity is a Fabric feature that aids in data security. It operates in the background to guarantee that only authorized users have access to the data they require. This capability, combined with Microsoft's robust data governance and compliance policies and tools, helps keep your data safe and secure.</p>
<h2>An overview of pricing and licenses</h2>
<p>In terms of pricing and licensing, Microsoft is following the Power BI Premium strategy with a capacity licensing model for Fabric. Customers buy Fabric Quality Units (FQUs), which give a common degree of performance capacity for all tools. Although this strategy is adaptable, details concerning scaling remain unknown. Furthermore, the licensing restrictions for Power BI continue to apply within Fabric.</p>
<p>In a way, Fabric employs a 'pay for what you use' model. Customers purchase 'units' of capacity, which govern how much work they may perform on the platform. However, the pricing details are a little complicated, and Microsoft hasn't provided all of them yet. However, businesses may find it more cost-effective than the alternatives, if they utilize it wisely and take advantage of its capacity to simplify.</p>
<h2>Should I move my data architecture to Microsoft Fabric?</h2>
<p>With the release of Fabric, existing customers of Microsoft's data analytics products may question the need for migration. While immediate migration is not required, Microsoft's emphasis on Fabric indicates that it may become the preferred approach in the future. Current Power BI customers will likely find the transfer easiest, as enabling Fabric provides instant access to powerful capabilities such as Synapse Analytics, Data Science, and Data Engineering services.</p>
<p>So, if you're already utilizing Microsoft's data tools, you might be wondering if you need to migrate to Fabric. The quick answer is no, not right away. Microsoft has stated that it will continue to support earlier technologies, but it is focusing more on Fabric, implying that it may become the primary way to handle data in the future.</p>
<p>Another thing to consider, just like any other SaaS offering, is that it’s cloud-based. If you are still using on-premises architectures due to legal, compliance, or just internal decisions, keep in mind the cost and effort of moving to the cloud.</p>
<p>At Monstarlab, we have a specialized data and analytics team ready to support you on all types of investments you might be considering, being just a simple discovery use case, a cloud migration one, or a lift and shift from your existing Azure-based data architecture to a new Microsoft Fabric based one.</p>
<p>Thank you,
Rui Machado</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Streamlining App Development with FlutterFlow: An Introduction]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/05/29/Streamlining-App-Development-with-FlutterFlow</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/05/29/Streamlining-App-Development-with-FlutterFlow</guid>
            <pubDate>Mon, 29 May 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>In today's fast-paced world, creating robust and user-friendly mobile applications is essential for businesses and developers alike. However, the process of building an app can be complex and time-consuming. That's where FlutterFlow comes in. FlutterFlow is a powerful visual development platform that simplifies the app development process using Google's Flutter framework. In this article, we'll explore the features and benefits of FlutterFlow and how it can revolutionize your app development workflow.</p>
<h3>What is FlutterFlow?</h3>
<p>FlutterFlow is an intuitive and powerful visual development platform that allows developers to design, prototype, and build mobile applications using a visual interface. It leverages the power of Google's Flutter framework, a cross-platform UI toolkit, to create high-quality native apps for iOS and Android.</p>
<h3>Amazing Visual Interface</h3>
<p>One of the standout features of FlutterFlow is its visual interface, which enables developers to design app screens and create interactive prototypes without writing a single line of code. With its drag-and-drop functionality, you can quickly build UI layouts, add components, and define interactions between screens. This visual approach makes it easy to iterate and refine your app's design, saving time and effort in the development process.</p>
<h3>Easy Code Generation</h3>
<p>While FlutterFlow simplifies the visual design aspect of app development, it doesn't limit you to visual-only workflows. FlutterFlow generates clean, production-ready Flutter code based on your designs, giving you the flexibility to customize and extend your app's functionality using traditional coding practices. This allows developers to seamlessly transition from the visual interface to writing code, providing the best of both worlds.</p>
<h3>Element Library</h3>
<p>FlutterFlow comes with an extensive library of pre-built UI components that you can use to create stunning app interfaces. From buttons and input fields to complex elements like lists and grids, FlutterFlow offers a wide range of ready-to-use components. This saves developers from reinventing the wheel and accelerates the development process.</p>
<h3>Real-Time Collaboration</h3>
<p>Collaboration is key when working on app development projects, and FlutterFlow offers real-time collaboration features that streamline teamwork. Multiple team members can work simultaneously on the same project, making it easy to share ideas, gather feedback, and track progress. With FlutterFlow's collaboration capabilities, you can ensure efficient communication and maximize productivity.</p>
<h3>Integration with Backend Services</h3>
<p>Building a complete app often involves backend integration, and FlutterFlow offers seamless integration with popular backend services. This allows developers to easily connect their apps to databases, authenticate users, and fetch data from APIs. These built-in integrations simplify the process of incorporating backend functionality, such as working with services like Firebase, GraphQL, and REST APIs, and enable robust functionality in your app. However, it's worth noting that there are scenarios where an app can be considered complete and fully functional without the need for complex backend code or integration.</p>
<h2>To use FlutterFlow, follow these steps</h2>
<ol>
<li>
<p>Sign up and Create a Project:</p>
<ul>
<li>Go to the <a href="https://flutterflow.io/">FlutterFlow website</a> and sign up for an account.</li>
<li>Once you're signed in, create a new project by clicking on the "Create Project" button.</li>
</ul>
</li>
<li>
<p>Design Your App:</p>
<ul>
<li>After creating a project, you'll be directed to the FlutterFlow visual editor.</li>
<li>Start designing your app's screens by dragging and dropping UI components from the sidebar onto the canvas.</li>
<li>Customize the appearance of the components, define layouts, and add interactions between screens using the visual interface.</li>
</ul>
</li>
<li>
<p>Configure Data Sources:</p>
<ul>
<li>If your app requires data from a backend service or API, you can configure data sources in FlutterFlow.</li>
<li>Connect your app to external services like Firebase, REST APIs, or GraphQL by setting up the necessary configurations within FlutterFlow.</li>
</ul>
</li>
<li>
<p>Add Logic and Functionality:</p>
<ul>
<li>While FlutterFlow simplifies visual design, you can also add logic and functionality to your app using code blocks.</li>
<li>Utilize FlutterFlow's built-in functionality blocks or create custom logic using Dart code snippets.</li>
</ul>
</li>
<li>
<p>Preview and Test:</p>
<ul>
<li>Use the Preview mode in FlutterFlow to test your app's design and interactions within the visual editor.</li>
<li>Preview your app on different device sizes and orientations to ensure responsiveness.</li>
</ul>
</li>
<li>
<p>Generate Flutter Code:</p>
<ul>
<li>Once you're satisfied with your app's design and functionality, you can generate the Flutter code.</li>
<li>Click on the "Export Code" button to generate clean and structured Flutter code based on your FlutterFlow project.</li>
</ul>
</li>
<li>
<p>Import to Flutter:</p>
<ul>
<li>Open your preferred Flutter development environment (such as Android Studio or Visual Studio Code) and create a new Flutter project or open an existing one.</li>
<li>Copy the generated Flutter code from FlutterFlow and replace the default code in your Flutter project with the generated code.</li>
</ul>
</li>
<li>
<p>Build and Launch Your App:</p>
<ul>
<li>Build and run your Flutter app using the Flutter CLI commands (<code>flutter run</code>) or through your integrated development environment.</li>
<li>Test your app on different devices or emulators to ensure it functions as intended.</li>
</ul>
</li>
</ol>
<h2>Let's build a simple screen using FlutterFlow</h2>
<p>Go to the FlutterFlow website (<a href="https://flutterflow.io/">https://flutterflow.io/</a>) and sign up for an account.
Image</p>
<p>A prompt to enter the project details along with firebase integration will be displayed, for this blog post I will not integrate Firebase.</p>
<p><img src="/assets/img/articles/2023-05-29-Streamlining-App-Development-with-FlutterFlow/create-project.webp" alt=""></p>
<h3>The tools</h3>
<p>FlutterFlow provides a set of tools and panels on the left and right sides of the interface to assist with the visual development process. Let's take a closer look at each side and their respective tools:</p>
<p>The tools on the left panel:</p>
<ul>
<li>
<p>Screen Selection: At the top left corner, you'll find a dropdown menu that allows you to select and switch between different screens in your app. This is where you can manage and access various screens within your project.</p>
</li>
<li>
<p>Component Tree: Below the screen selection dropdown, you'll find the Component Tree panel. It provides a hierarchical view of all the UI components present on the currently selected screen. You can expand and collapse the tree to navigate and select specific components for editing.</p>
</li>
<li>
<p>Properties Panel: The Properties panel, located on the left side, displays the properties and attributes of the selected UI component. You can modify these properties to customize the appearance, behavior, and styling of the selected component.</p>
</li>
<li>
<p>Data Sources: The Data Sources panel allows you to manage and connect data sources to your app. You can define data models, integrate APIs, and create dynamic content for your app using this panel. It provides a visual interface for managing data and API connections.</p>
</li>
<li>
<p>Actions: The Actions panel enables you to define and manage interactive behaviors within your app. You can create events and actions that respond to user interactions, such as button clicks or form submissions. This panel allows you to add logic and interactivity to your app without writing code.</p>
</li>
</ul>
<p>The tools on the right panel:</p>
<ul>
<li>
<p>Widget Library: The Widget Library panel, located on the right side, provides a comprehensive collection of pre-built UI components that you can drag and drop onto your app screens. It includes various categories of widgets, such as buttons, text fields, images, lists, and more. You can customize these widgets to fit your design requirements.</p>
</li>
<li>
<p>Styles: The Styles panel allows you to manage and apply styles to your UI components. It provides options for defining and customizing text styles, colors, spacing, and other visual properties. You can create reusable styles to maintain consistency across your app.</p>
</li>
<li>
<p>Asset Manager: The Asset Manager panel lets you upload and manage the assets (images, icons, fonts, etc.) used in your app. You can upload assets, organize them into folders, and easily access and use them within your app screens.</p>
</li>
<li>
<p>Component Structure: The Component Structure panel shows a hierarchical overview of your app's components and screens. It provides a visual representation of the entire structure of your app and allows you to navigate between screens and components easily.</p>
</li>
<li>
<p>Preview and Simulator: At the top right corner, you'll find buttons to preview your app's design and functionality. You can view your app's appearance on different device sizes and orientations, and even interact with the previewed app using the built-in simulator.</p>
</li>
</ul>
<p>In the below image, I have dragged and dropped a Column as a parent Widget and the pageView component and when you click the component, you will have options to position the elements and design them accordingly.
<img src="/assets/img/articles/2023-05-29-Streamlining-App-Development-with-FlutterFlow/first-step.webp" alt=""></p>
<p>Next, I added a Container Widget with Row and three more Container with image Components
<img src="/assets/img/articles/2023-05-29-Streamlining-App-Development-with-FlutterFlow/second-step.webp" alt=""></p>
<p>Then, I added a ListView component below and added Cards with Images. The listView is Expanded by selecting the option on the right and making a horizontal scroll.
<img src="/assets/img/articles/2023-05-29-Streamlining-App-Development-with-FlutterFlow/third-step.webp" alt=""></p>
<p>Finally, when you are satisfied with the look of the screen select on the run option to preview the app. This is how it would look like
<img src="/assets/img/articles/2023-05-29-Streamlining-App-Development-with-FlutterFlow/final.webp" alt=""></p>
<h2>Limitations and areas where FlutterFlow may fall short</h2>
<p>While FlutterFlow offers many benefits, it's important to consider its limitations and areas where it may fall short. Here are some downsides and considerations:</p>
<ul>
<li>
<p>Limited Customization: FlutterFlow provides a visual interface for building UI components, but it may have limitations when it comes to fine-grained customization. Advanced design elements or complex animations may require manual coding or customization outside of the visual builder.</p>
</li>
<li>
<p>Learning Curve: While FlutterFlow simplifies the app development process, there is still a learning curve associated with using the tool. It may take time to familiarize yourself with its interface, components, and workflows.</p>
</li>
<li>
<p>Less Control and Flexibility: Since FlutterFlow is a low-code tool, it abstracts certain aspects of app development. This can limit the level of control and flexibility you have over your app's code and architecture. Custom or complex requirements may require direct coding in Flutter instead of relying solely on FlutterFlow.</p>
</li>
<li>
<p>Production Readiness: While FlutterFlow has improved over time, it may still have some limitations when it comes to production-level app development. It's important to thoroughly test and validate your app's behavior, performance, and scalability when using FlutterFlow.</p>
</li>
<li>
<p>Complex Scenarios: While FlutterFlow can handle many common app scenarios, it may struggle with more complicated use cases that require access to device sensors, camera functionality, Bluetooth connectivity, or advanced platform-specific features. In such cases, manual coding or integrating additional Flutter packages may be necessary.</p>
</li>
<li>
<p>Accessibility Support: FlutterFlow provides basic accessibility features, but it may not cover all the requirements for building fully accessible apps. Additional manual coding and testing may be needed to ensure proper accessibility support.</p>
</li>
<li>
<p>Unit Testing: FlutterFlow's visual builder approach may make it challenging to write comprehensive unit tests. Extensive testing and validation may require writing custom code and utilizing Flutter's testing framework directly.</p>
</li>
<li>
<p>Time Savings: The amount of time saved using FlutterFlow versus building from scratch depends on various factors such as the complexity of the app, your familiarity with FlutterFlow, and the level of customization required. While FlutterFlow can accelerate the development process by generating code and providing visual editing capabilities, it may still require additional manual coding for specific requirements, resulting in varying time savings.</p>
</li>
</ul>
<h2>Conclusion:</h2>
<p>FlutterFlow is a game-changer for mobile app development. Its visual interface, code generation capabilities, extensive component library, and seamless backend integrations make it a go-to platform for developers looking to build high-quality native apps efficiently. By simplifying the development process and fostering collaboration, FlutterFlow empowers developers to bring their app ideas to life quickly and effectively. Whether you're a beginner or an experienced developer, FlutterFlow is a tool worth exploring to unlock your app development potential.
Mind you, FlutterFlow is a powerful visual development tool that simplifies the app development process, but it's important to have a basic understanding of Flutter and Dart programming concepts to fully leverage its capabilities. FlutterFlow complements your development workflow by providing a visual interface and code generation, but you may need to customize and extend your app's functionality through manual coding.
However, it's important to evaluate its suitability for your specific project's requirements, complexity, and customization needs. Careful consideration should be given to its limitations, production readiness, support for advanced scenarios, accessibility, unit testing, and the trade-offs between time saved and flexibility.</p>
<p>Refer to FlutterFlow's documentation, tutorials, and community resources to explore its features in-depth and make the most of this powerful app development platform.</p>
<p>Remember, the best way to truly experience the power of FlutterFlow is to try it out for yourself. So, dive in.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Exploring Traefik: A simpler and faster web server]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/05/26/Exploring-Traefik-A-simpler-and-faster-web-server</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/05/26/Exploring-Traefik-A-simpler-and-faster-web-server</guid>
            <pubDate>Fri, 26 May 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>In this article, I will introduce a powerful web server named Traefik, written in the Go language. Compared with Nginx, currently the most popular web proxy server, Traefik stands out for its simplicity and speed.</p>
<p>Here are the official website and source code of Traefik:</p>
<ul>
<li><a href="https://traefik.io">Traefik official site</a></li>
<li><a href="https://github.com/traefik/traefik">Traefik GitHub repository</a></li>
</ul>
<p>To follow and run the demo provided in this article, you should have:</p>
<ul>
<li>Experience using other web servers like Nginx or Apache</li>
<li>Basic knowledge of Docker and Docker Compose</li>
</ul>
<p>As described in the Github repository, Traefik has the following features:</p>
<ul>
<li>Continuous configuration updates (No restarts!)</li>
<li>Support for multiple load balancing algorithms</li>
<li>Provides HTTPS to your microservices by leveraging Let's Encrypt (wildcard certificates support)</li>
<li>Circuit breakers, retry mechanisms</li>
<li>A clean, user-friendly web UI</li>
<li>Readiness for Websocket, HTTP/2, GRPC</li>
<li>Provides metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB)</li>
<li>Keeps access logs (JSON, CLF)</li>
<li>Fast</li>
<li>Exposes a Rest API</li>
<li>Packaged as a single binary file and available as an official docker image</li>
</ul>
<p>I personally recommend Traefik for the following three reasons. I encourage you to consider using it as the proxy server in your next startup project:</p>
<ul>
<li>Easier configuration files</li>
<li>Simpler SSL support</li>
<li>Faster</li>
</ul>
<p>Next, I will run a Traefik instance using a Docker container to demonstrate these advantages.</p>
<h2>Simplified Configuration</h2>
<p>When compared to Nginx, Traefik's configuration files are simpler to handle. In this section, I will use the following three configuration files to start an instance:</p>
<ul>
<li><code>docker-compose.yml</code></li>
<li><code>traefik.yml</code></li>
<li><code>dynamic-traefik.yml</code></li>
</ul>
<h3>Docker</h3>
<p>Being written in the Go language, Traefik naturally aligns well with Docker and Kubernetes. In this article, I will use the following Docker Compose file to start a Traefik instance.</p>
<pre><code class="hljs language-yml"><span class="hljs-attr">version:</span> <span class="hljs-string">"3"</span>

<span class="hljs-attr">services:</span>
  <span class="hljs-attr">reverse-proxy:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">traefik:v2.5.6</span>
    <span class="hljs-attr">command:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">--api.insecure=true</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">80</span><span class="hljs-string">:80</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">443</span><span class="hljs-string">:443</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">7000</span><span class="hljs-string">:7000</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/etc/traefik/:/etc/traefik/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/var/log/traefik/:/var/log/traefik/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/etc/ssl/certs/:/etc/ssl/certs/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/etc/ssl/letsencrypt:/letsencrypt/</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span>
</code></pre>
<p>The following ports are specified in our <code>yml</code> file:</p>
<ul>
<li><code>80</code>: http</li>
<li><code>443</code>: https</li>
<li><code>7000</code>: Visual web panel for easier Traefik management</li>
</ul>
<p>The following volumes are added in our <code>yml</code> file:</p>
<ul>
<li><code>/etc/traefik:/etc/traefik/</code>: Stores the configuration files</li>
<li><code>/var/log/traefik/:/var/log/traefik/</code>: Stores the log files</li>
<li><code>/etc/ssl/certs/:/etc/ssl/certs/</code>: Stores the SSL certificate files</li>
<li><code>/etc/ssl/letsencrypt:/letsencrypt/</code>: Stores LetsEncrypt-related files</li>
</ul>
<h3>Main configuration file</h3>
<p>The main configuration file, <code>traefik.yml</code>, holds the global configurations of Traefik.
This is similar to the <code>event</code>, <code>server</code>, and <code>http</code> nodes defined in the main configurations of Traefik.
Traefik supports the standard yml format to define the configurations, which is easier to read and edit.</p>
<pre><code class="hljs language-yml"><span class="hljs-attr">entryPoints:</span>
  <span class="hljs-attr">web:</span>
    <span class="hljs-attr">address:</span> <span class="hljs-string">":80"</span>
    <span class="hljs-attr">http:</span>
      <span class="hljs-attr">redirections:</span>
        <span class="hljs-attr">entryPoint:</span>
          <span class="hljs-attr">to:</span> <span class="hljs-string">websecure</span>
          <span class="hljs-attr">scheme:</span> <span class="hljs-string">https</span>
  <span class="hljs-attr">websecure:</span>
    <span class="hljs-attr">address:</span> <span class="hljs-string">":443"</span>
  <span class="hljs-attr">metrics:</span>
    <span class="hljs-attr">address:</span> <span class="hljs-string">":7000"</span>
<span class="hljs-attr">providers:</span>
  <span class="hljs-attr">file:</span>
    <span class="hljs-attr">directory:</span> <span class="hljs-string">/etc/traefik/</span>
    <span class="hljs-attr">watch:</span> <span class="hljs-literal">true</span>
<span class="hljs-attr">api:</span>
  <span class="hljs-attr">dashboard:</span> <span class="hljs-literal">true</span>
<span class="hljs-attr">log:</span>
  <span class="hljs-attr">level:</span> <span class="hljs-string">WARN</span>
</code></pre>
<h3>Dynamic configuration file</h3>
<p>The dynamic configuration file, <code>dynamic-traefik.yml</code>, contains site configurations.
It is similar to the <code>include</code> command in Nginx like the following:</p>
<pre><code class="hljs language-groovy">include <span class="hljs-regexp">/etc/</span>nginx<span class="hljs-regexp">/vhost/</span>*.conf;
</code></pre>
<p>In the <code>dynamic-traefik.yml</code> file, the domain <code>test.fczm.site</code> is bound to a backend server instance <code>http://10.0.0.1:8080</code>.
While Traefik can serve as a static web server, it's usually deployed as a proxy server.</p>
<pre><code class="hljs language-yml"><span class="hljs-attr">http:</span>
  <span class="hljs-attr">routers:</span>
    <span class="hljs-attr">router-test-api:</span>
      <span class="hljs-attr">entryPoints:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">"websecure"</span>
      <span class="hljs-attr">rule:</span> <span class="hljs-string">Host(`test.fczm.site`)</span>
      <span class="hljs-attr">service:</span> <span class="hljs-string">test-api</span>

  <span class="hljs-attr">services:</span>
    <span class="hljs-attr">test-api:</span>
      <span class="hljs-attr">loadBalancer:</span>
        <span class="hljs-attr">servers:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">url:</span> <span class="hljs-string">"http://172.31.1.57:8080"</span>
        <span class="hljs-attr">healthCheck:</span>
          <span class="hljs-attr">path:</span> <span class="hljs-string">/actuator/health</span>
          <span class="hljs-attr">interval:</span> <span class="hljs-string">"10s"</span>
          <span class="hljs-attr">timeout:</span> <span class="hljs-string">"3s"</span>
</code></pre>
<p>This demo file is a simple dynamic configuration file, containing two nodes:</p>
<ul>
<li><code>routers</code>: Defines the routers, including the domain, SSL certificate, backend service, etc.</li>
<li><code>services</code>: Defines the specific service, in this case, a backend server.</li>
</ul>
<p>After preparing the Traefik-related configuration file, simply move it into the folder <code>/etc/traefik/</code> defined in the Docker Compose file.</p>
<p>I have a simple Spring Boot backend server set up for this demonstration.
The backend server tested successfully:</p>
<pre><code class="hljs language-csharp">➜  ~ curl http:<span class="hljs-comment">//172.31.4.26:8070/actuator/health</span>
{<span class="hljs-string">"status"</span>:<span class="hljs-string">"UP"</span>}<span class="hljs-meta">#</span>
</code></pre>
<p>Finally, you can launch it with Docker Compose.</p>
<h2>Easier SSL support</h2>
<p>With Traefik, it is easy to use the SSL certificates provided by <code>LetsEncrypt</code>, which can be automatically updated.
To support <code>LetsEncrypt</code> SSL certificates, append a <code>certificatesResolvers</code> node should be added to the main configuration file <code>traefik.yml</code>.</p>
<pre><code class="hljs language-yml"><span class="hljs-attr">certificatesResolvers:</span>
  <span class="hljs-attr">testResolver:</span>
    <span class="hljs-attr">acme:</span>
      <span class="hljs-attr">email:</span> <span class="hljs-string">test@fczm.site</span>
      <span class="hljs-attr">storage:</span> <span class="hljs-string">/letsencrypt/acme.json</span>
      <span class="hljs-attr">httpChallenge:</span>
        <span class="hljs-attr">entryPoint:</span> <span class="hljs-string">web</span>
</code></pre>
<p>Next, insert the resolver into the routers in the <code>dynamic-traefik.yml</code> file, as shown below:</p>
<pre><code class="hljs language-yml"><span class="hljs-attr">http:</span>
  <span class="hljs-attr">routers:</span>
    <span class="hljs-attr">router-test-api:</span>
      <span class="hljs-attr">entryPoints:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">"websecure"</span>
      <span class="hljs-attr">rule:</span> <span class="hljs-string">Host(`test.fczm.site`)</span>
      <span class="hljs-attr">service:</span> <span class="hljs-string">test-api</span>
      <span class="hljs-attr">tls:</span>
        <span class="hljs-attr">certResolver:</span> <span class="hljs-string">testResolver</span>
</code></pre>
<p>Upon restarting Traefik and waiting a while, the SSL certificate becomes active.
Manual SSL certificate management is also supported.</p>
<h2>Improved speed</h2>
<p>Traefik performs faster than Nginx.
For performance testing, I've prepared the following environment:</p>

































<table><thead><tr><th>Item</th><th>Value</th></tr></thead><tbody><tr><td>Server Provider</td><td>AWS</td></tr><tr><td>Region</td><td>Tokyo</td></tr><tr><td>CPU</td><td>1 Core</td></tr><tr><td>Memory</td><td>2 GB</td></tr><tr><td>Backend</td><td>Spring Boot Demo Application</td></tr><tr><td>SSL</td><td>Required</td></tr></tbody></table>
<p>I employed the command-line tool <code>wrk</code> for the performance tests.</p>
<p>Initially, I set up a Nginx proxy server for the backend instance and executed the following command to test its performance:</p>
<pre><code class="hljs language-shell">➜  ~ wrk  -t 10 -c 200 -d 60s -T 30s --latency https://test.fczm.site/actuator/health
Running 1m test @ https://test.fczm.site
  10 threads and 200 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   147.38ms  117.23ms   1.80s    80.74%
    Req/Sec   157.30     60.24   380.00     69.43%
  Latency Distribution
     50%  119.11ms
     75%  194.24ms
     90%  290.68ms
     99%  523.39ms
  87393 requests in 1.00m, 30.42MB read
Requests/sec:   1455.54
Transfer/sec:    518.85KB
</code></pre>
<p>Afterwards, I ran the same script with Traefik:</p>
<pre><code class="hljs language-shell">➜  ~ wrk -t 10 -c 200 -d 60s -T 30s --latency https://test.fczm.site/actuator/health
Running 1m test @ https://test.fczm.site/actuator/health
  10 threads and 200 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   106.62ms   83.21ms   1.41s    87.12%
    Req/Sec   211.13     58.45   400.00     70.40%
  Latency Distribution
     50%   83.21ms
     75%  133.38ms
     90%  203.80ms
     99%  388.29ms
  123927 requests in 1.00m, 48.81MB read
Requests/sec:   2061.95
Transfer/sec:    831.63KB
</code></pre>
<p>In this performance test, Nginx sent 87,393 requests within 1 minute, while Traefik sent 123,927 requests during the same period, making it 1.4x faster.</p>
<h2>Conclusion</h2>
<p>Traefik is simpler and faster than Nginx.
Written in Go, it naturally integrates with Docker and Kubernetes.
If you're struggling with Nginx, consider trying Traefik in your next project.</p>
<p><em>Article Photo by <a href="https://traefik.io/blog/announcing-traefik-proxy-2-6/">Traefik</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Build your first smart contract with Ethereum and Solidity]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/05/25/Build-your-first-smart-contract-with-Ethereum-and-Solidity</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/05/25/Build-your-first-smart-contract-with-Ethereum-and-Solidity</guid>
            <pubDate>Thu, 25 May 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Smart contracts are an exciting way to build decentralized applications (DApps) on a blockchain. This tutorial will guide you in building your first smart contract on the Ethereum blockchain using Solidity.</p>
<h2>What is a Smart Contract?</h2>
<p>According to Wikipedia, a <a href="https://en.wikipedia.org/wiki/Smart_contract">Smart Contract</a> is a computer program or a transaction protocol stored on the Blockchain network.</p>
<p>You can say it's like an agreement between two people in the form of computer code. Transactions within a smart contract are processed by the blockchain and stored as a character chain hex address with the prefix "0x". This means they can be sent automatically, eliminating the need for a third party.</p>
<p><em>Example</em>: <strong>0xe827a5db9559dce2091c535e3f9ba9409defcfef18936da72c79dac190bfbab9</strong></p>
<p><strong>Note</strong>: When your Smart Contract is deployed, everything stored on blockchain can not be change.</p>
<h2>What is Solidity?</h2>
<p>Solidity is an object-oriented programming language created specifically by the Ethereum Network team for constructing and designing smart contracts on Blockchain platforms.</p>
<ul>
<li>Solidity was proposed in August 2014 by Gavin Wood.</li>
<li>Solidity is a high-level language for smart contract applications.</li>
<li>It is an object-oriented language designed specifically for the Ethereum Virtual Machine.</li>
<li>Solidity is most profoundly influenced by C++, but also borrowed concepts from languages like Python, JavaScript.</li>
<li>The open source project can be found on <a href="https://github.com/ethereum/solidity">Github</a></li>
<li>All documentation can be accessed <a href="https://docs.soliditylang.org/en/v0.8.19/">here</a></li>
</ul>
<h2>Problem to solve</h2>
<p>Firstly, we will <strong>create an amount token</strong>. To do so, please follow the steps below:</p>
<ul>
<li>Create the Minter (originator)</li>
<li>Create the Supply</li>
<li>Create the Balance (account balance)</li>
</ul>
<p>Next, we will <strong>create a sent money</strong> function. This involves several key points:</p>
<ul>
<li>Identify the Receiver's address (it's a character chain hex address)</li>
<li>Check if your amount is less than or equal to the balance. If it's not, throw an error message stating, <strong>"Not enough money to make the transaction"</strong></li>
<li>If the transaction to send money is successful, we will update the total amount for the sender. This means: the sender's balance will decrease by the amount sent (<strong>balance sender -= amount</strong>). In parallel with this, we will also update the receiver's amount: the receiver's balance will increase by the amount received (<strong>balance receiver += amount</strong>).</li>
</ul>
<h2>Build your first Smart Contract</h2>
<ol>
<li>Open Remix IDE from <a href="https://remix.ethereum.org/">here</a></li>
<li>Click Accept and get ready</li>
<li>Under <strong>default_workspace</strong>, click on <em>create new file</em></li>
<li>Set the name to be <strong>FirstSmartContract.sol</strong></li>
</ol>
<p>Great, now we're ready to start coding.</p>
<p><strong>Contract code</strong></p>
<ol>
<li>In the first line of the Smart Contract, we have to specify the <em>solidity version</em>.</li>
</ol>
<pre><code class="hljs language-js">    pragma solidity ^<span class="hljs-number">0.8</span><span class="hljs-number">.18</span>;
</code></pre>
<ol start="2">
<li>We then create a contract with the name <strong>FirstSmartContract</strong>.</li>
</ol>
<pre><code class="hljs language-cpp">    contract FirstSmartContract {
        ...
    }
</code></pre>
<ol start="3">
<li>In the <strong>FirstSmartContract {..}</strong> contract, we will input some code, please see below.</li>
<li>We will create variables where the smart contract will store the minter <a href="https://docs.soliditylang.org/en/v0.8.19/types.html#address">address</a> (a 42 character hex string with a "0x" prefix) of the both the Landlord &#x26; the Tenant.</li>
</ol>
<pre><code class="hljs language-ts">    address <span class="hljs-keyword">public</span> minter;
</code></pre>
<ol start="5">
<li>Create the <strong>balances</strong> value of type <a href="https://docs.soliditylang.org/en/v0.8.19/types.html#mapping-types">mapping</a>.</li>
</ol>
<pre><code class="hljs language-ts">    mapping (<span class="hljs-function"><span class="hljs-params">address</span> =></span> uint) <span class="hljs-keyword">public</span> balances;
</code></pre>
<p>This line indicates that for each <a href="https://docs.soliditylang.org/en/v0.8.19/types.html#address">address</a> input, we will get a value of type <strong>uint</strong>. In this case, the <strong>uint</strong> represents the amount associated with each <strong>address</strong>. Since the balance for each address can never be negative, the value type is <strong>uint</strong> (range from 0 to 4294967295).</p>
<ol start="6">
<li>Create an <a href="https://docs.soliditylang.org/en/v0.8.19/structure-of-a-contract.html#events">event</a> named <em>sendSuccess</em>.</li>
</ol>
<p>This event requires three input values: the address of the minter, the address of the wallet you want to send to, and finally, the amount you want to send.</p>
<pre><code class="hljs language-cpp">    <span class="hljs-function">event <span class="hljs-title">sendSuccess</span><span class="hljs-params">(address from, address to, uint amount)</span></span>;
</code></pre>
<ol start="7">
<li>In the <strong>constructor</strong>, we will set minter address.</li>
</ol>
<pre><code class="hljs language-ts">    <span class="hljs-title function_">constructor</span>(<span class="hljs-params"></span>) {
        minter = msg.<span class="hljs-property">sender</span>;
    }
</code></pre>
<p>In Solidity, if you want to get the address of the minter (sender) you can use <code>msg.sender;</code>.</p>
<ol start="8">
<li>Next, we create the <strong>sendMoney</strong> function. We need to pass two values as input: the <strong>receiver's address</strong> and the <strong>amount sent</strong>.</li>
</ol>
<pre><code class="hljs language-ts">    <span class="hljs-keyword">function</span> <span class="hljs-title function_">sendMoney</span>(<span class="hljs-params">address receiver, uint amount</span>) <span class="hljs-keyword">public</span> {
        ....
    }
</code></pre>
<p>Firstly, we need to check whether the amount you want to send is less than your total balance. If this condition is not met, the program should throw the message <strong>Not enough money to make the transaction</strong> and stop.</p>
<p>This check can be implemented with the following line:</p>
<pre><code class="hljs language-ts"><span class="hljs-built_in">require</span>(amount &#x3C;
  balances[msg.<span class="hljs-property">sender</span>], <span class="hljs-string">"Not enough money to make the transaction"</span>);
</code></pre>
<p>If the condition is met, the program will continue to the next lines. The next steps are:</p>
<ul>
<li>Update the receiver's total balance:</li>
</ul>
<pre><code class="hljs language-ts">balances[receiver] += amount;
</code></pre>
<ul>
<li>Subtract the sent amount from your balance:</li>
</ul>
<pre><code class="hljs language-ts">balances[msg.<span class="hljs-property">sender</span>] -= amount;
</code></pre>
<p><strong>Remember</strong>: For each of your transactions, you will be charged a gas fee, which is regulated by Ethereum.</p>
<p>Almost there.
To register the transaction on the blockchain, we will emit an event:</p>
<pre><code class="hljs language-ts">    emit <span class="hljs-title function_">sendSuccess</span>(msg.<span class="hljs-property">sender</span>, receiver, amount);
</code></pre>
<p>In summary, the <strong>sendMoney</strong> function will look like this:</p>
<pre><code class="hljs language-ts">    <span class="hljs-keyword">function</span> <span class="hljs-title function_">sendMoney</span>(<span class="hljs-params">address receiver, uint amount</span>) <span class="hljs-keyword">public</span> {
        <span class="hljs-built_in">require</span> (amount &#x3C; balances[msg.<span class="hljs-property">sender</span>], <span class="hljs-string">"Not enough money to make the transaction"</span>);
        balances[receiver] += amount;
        balances[msg.<span class="hljs-property">sender</span>] -= amount;
        emit <span class="hljs-title function_">sendSuccess</span>(msg.<span class="hljs-property">sender</span>, receiver, amount);
    }
</code></pre>
<ol start="9">
<li>Finally, we will create the <strong>mint</strong> function, which is used to generate balance for a given address.</li>
</ol>
<pre><code class="hljs language-ts">    <span class="hljs-keyword">function</span> <span class="hljs-title function_">mint</span>(<span class="hljs-params">address receiver, uint amount</span>) <span class="hljs-keyword">public</span> {
        balances[receiver] += amount;
    }
</code></pre>
<ol start="10">
<li>Now, it's time for a demonstration:</li>
</ol>
<h2>Compile</h2>
<p>Click on the <code>Solidity Compile</code> option in the left sidebar.</p>
<ol>
<li>Select the compiler version <code>0.8.18+</code></li>
<li>Then, click on <code>Compile FirstSmartContract.sol</code></li>
</ol>
<p><img src="/assets/img/articles/2023-05-25-Build-your-first-smart-contract-with-Ethereum-and-Solidity/solidity-compile.webp" alt=""></p>
<h2>Deploy</h2>
<p>Click on the <code>Deploy &#x26; Run Transactions</code> option in the left sidebar.</p>
<ol>
<li>Choose <code>Environment > JavaScript VM (London)</code></li>
<li>Click on <code>Deploy</code></li>
</ol>
<p><img src="/assets/img/articles/2023-05-25-Build-your-first-smart-contract-with-Ethereum-and-Solidity/solidity-deploy.webp" alt=""></p>
<p>🎉 Congratulations, your smart contract has been deployed. 🎉</p>
<p><img src="/assets/img/articles/2023-05-25-Build-your-first-smart-contract-with-Ethereum-and-Solidity/solidity-transaction-success.webp" alt=""></p>
<h2>Create Sample Transaction</h2>
<p>Under <code>Deployed Contracts</code> click on <code>FIRSTSMARTCONTRACT AT... (MEMORY) </code> to expand the section and fill in the details.</p>
<p><img src="/assets/img/articles/2023-05-25-Build-your-first-smart-contract-with-Ethereum-and-Solidity/solidity-transaction.webp" alt=""></p>
<p>We have 4 buttons here:</p>
<ul>
<li>The first is <strong>mint</strong>. This button initializes the balance of the input address.</li>
<li>The second is <strong>sendMoney</strong>. When you input a value in the box next to it and click the <code>sendMoney</code> button, a transaction will be executed.</li>
<li>Next, we have the <strong>balances</strong> variable you already created in the code. Just input the wallet address (e.g., 0x...), and the available balance will be displayed.</li>
<li>Finally, there's the <strong>minter</strong> variable. The address of the minter will be printed when you click this button.</li>
</ul>
<p>Remember, your first Ether Address is the landlord of the room. (The one with 99.99 test ether in the wallet.)</p>
<p><img src="/assets/img/articles/2023-05-25-Build-your-first-smart-contract-with-Ethereum-and-Solidity/solidity-current-address.webp" alt=""></p>
<p>Copy your current account address and input it into the <strong>mint</strong> field, followed by the amount. It should look like this:
Press the <strong>mint</strong> button:</p>
<p><img src="/assets/img/articles/2023-05-25-Build-your-first-smart-contract-with-Ethereum-and-Solidity/solidity-mint-current.webp" alt=""></p>
<p>To check your balances, paste the account address into the <strong>balances</strong> field and press the <code>balances</code> button. You should now see that the balance is <strong>1000</strong>.</p>
<p><img src="/assets/img/articles/2023-05-25-Build-your-first-smart-contract-with-Ethereum-and-Solidity/solidity-mint-balances.webp" alt=""></p>
<p>Change the <strong>Account Address</strong> from the dropdown menu. (Choose any address except the one with 99.99 ether).</p>
<p>In this example, I will choose the last address.</p>
<p><img src="/assets/img/articles/2023-05-25-Build-your-first-smart-contract-with-Ethereum-and-Solidity/solidity-change-account-address.webp" alt=""></p>
<p>Copy the address you just selected and paste it into the <strong>sendMoney</strong> field, followed by the amount you want to send. It should look similar to the following:</p>
<p><img src="/assets/img/articles/2023-05-25-Build-your-first-smart-contract-with-Ethereum-and-Solidity/solidity-send-money.webp" alt=""></p>
<p>After that, change the selected account address to your 1st Ether Address and press the <strong>sendMoney</strong> button.</p>
<p>🎉 Well done. Congratulations, the money has been sent. 🎉</p>
<p><img src="/assets/img/articles/2023-05-25-Build-your-first-smart-contract-with-Ethereum-and-Solidity/solidity-send-money-success.webp" alt=""></p>
<figure>
<img src="/assets/img/articles/2023-05-25-Build-your-first-smart-contract-with-Ethereum-and-Solidity/solidity-check-money-sent.webp">
<figcaption>Check balances of account address receiver</figcaption>
</figure>
<h2>Gas fee</h2>
<p>If you pay close attention, you will notice that you lose a few wei each time you make a transaction. This is called a gas fee.</p>
<ul>
<li>1 ether = 1,000,000,000,000,000,000 wei</li>
</ul>
<p>The Ethereum gas fee exists to compensate network validators for their work in securing the blockchain and network.</p>
<p>Without these fees, there would be little incentive to stake ETH and become a validator. The network would be at risk without the presence of validators and the essential work they do.</p>
<h2>Conclusion</h2>
<p>You successfully understand now what Solidity is and how smart contracts work. Furthermore, you've successfully built and deployed a smart contract.</p>
<h2>Resources</h2>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Smart_contract">Wikipedia Smart Contract</a></li>
<li><a href="https://github.com/ethereum/solidity">Github Repository solidity</a></li>
<li><a href="https://docs.soliditylang.org/en/v0.8.19/">Solidity official document</a></li>
</ul>
<p><em>Article Photo by <a href="https://www.bitpanda.com/academy/en/lessons/what-is-ethereum/">bitpanda</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Safe Home Initiative for Children]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/05/25/The-problem-and-the-solution-Safe-Home</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/05/25/The-problem-and-the-solution-Safe-Home</guid>
            <pubDate>Thu, 25 May 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>As part of Monstarlab's 3rd edition of the internal hackathon, <a href="https://www.linkedin.com/showcase/monstarhacks">MonstarHacks</a>, our team, Innovation Infusion, developed <strong>The Safe Home Initiative for Children</strong> which is a project designed to tackle the challenge faced by the partnering NGO <a href="https://ekmattra.org/">Ekmattra</a>.</p>
<p>The difficulty lies in analyzing the orphaned children's needs and finding the ideal match from a pool of organizations and schools that may not necessarily possess the required information to provide effective support.</p>
<p>This is where the Safe Home Initiative comes in, using advanced technologies like AI, blockchain, and machine learning to match children with the best organizations while ensuring their privacy and security.</p>
<p><img src="/assets/img/articles/2023-05-25-The-problem-and-the-solution-Safe-Home/1.webp" alt=""></p>
<p><img src="/assets/img/articles/2023-05-25-The-problem-and-the-solution-Safe-Home/2.webp" alt=""></p>
<p>In this article, we outline the technologies used in building our solution.</p>
<h2>Backend technologies</h2>
<ul>
<li><strong>Java</strong></li>
<li><strong>Spring Boot</strong></li>
<li><strong>AI Models</strong></li>
<li><strong>Blockchain</strong></li>
<li><strong>Data Security</strong></li>
</ul>
<p>The backend of the <strong>Safe Home Initiative</strong> is built using Java and the Spring Boot framework, which allows for efficient data processing and management. AI models, such as ChatGPT-3.5, DeepFace, Facenet, and Facenet512, are used to gather information about children's interests, skills, and future plans. This information is then fed into a matching algorithm that utilizes blockchain technologies like Hyperledger Fabric and Corda, to guarantee the security and reliability of the system.</p>
<p>To ensure the privacy and security of the children's information, the Safe Home Initiative follows the OWASP and NIST Cybersecurity Framework. These frameworks provide guidelines and best practices to protect data from cyber threats, thereby safeguarding the children's information against unauthorized access and usage.</p>
<h2>Frontend technologies</h2>
<ul>
<li><strong>JavaScript</strong></li>
<li><strong>React</strong></li>
</ul>
<p>The frontend of the Safe Home Initiative is built using JavaScript and the React framework, providing a user-friendly interface for NGOs and organizations to access and manage information about children and potential matches. This frontend interface facilitates the communication and evaluation of placements, thereby improving effectiveness and ensuring that children are matched with the most suitable organizations to meet their needs.</p>
<h2>Mobile technologies</h2>
<ul>
<li><strong>Dart</strong></li>
<li><strong>Kotlin</strong></li>
<li><strong>Java</strong></li>
<li><strong>Firebase</strong></li>
<li><strong>Android</strong></li>
<li><strong>Flutter</strong></li>
<li><strong>iOS</strong></li>
<li><strong>HealthKit</strong></li>
</ul>
<p>The Safe Home Initiative also incorporates mobile technologies to monitor the health and wellbeing of the children. Mobile apps built using Dart, Kotlin, and Java, along with Firebase and HealthKit, facilitate easy tracking of the children's physical and emotional health. These apps are available on Android, Flutter, and iOS devices, making it easy for caregivers and organizations to monitor the children's progress and provide tailored support.</p>
<figure>
<img src="/assets/img/articles/2023-05-25-The-problem-and-the-solution-Safe-Home/diagram.webp">
<figcaption>User story</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2023-05-25-The-problem-and-the-solution-Safe-Home/architecture.webp">
<figcaption>Architecture</figcaption>
</figure>
<h2>Conclusion</h2>
<h3>Making a positive impact on orphaned children</h3>
<p>Our team's MonstarHacks project combines advanced technologies with a commitment to social impact to revolutionize the matchmaking process for orphaned children.</p>
<p>By leveraging AI models, blockchain technologies, and machine learning algorithms, the Safe Home Initiative provides personalized matches and tailored support to help orphaned children succeed in the future.</p>
<p>With a focus on data security and privacy, the Safe Home Initiative offers a safe and reliable solution for NGOs like Ekmattra and other organizations to find the best matches for the children under their care.</p>
<p>The project's ultimate goal is to make a significant positive impact on the lives of orphaned children and provide them with the support they need to thrive.</p>
<p><em>Article Photo by <a href="https://ekmattra.org/about-us/">Ekmattra</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Establishing connections for children living on the streets, the journey of The CzechMate Coders in MonstarHacks 2023]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/05/23/Establishing-connections-for-children-living-on-the-streets-the-journey-of-The-CzechMate-Coders-in-MonstarHacks-2023</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/05/23/Establishing-connections-for-children-living-on-the-streets-the-journey-of-The-CzechMate-Coders-in-MonstarHacks-2023</guid>
            <pubDate>Tue, 23 May 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><a href="https://www.linkedin.com/showcase/monstarhacks">MonstarHacks</a>, the esteemed annual competition hosted by Monstarlab, has reached its 3rd edition, bringing together talented individuals from diverse backgrounds who are driven by innovation and dedicated to creating a positive social impact. This year, the theme was "NGOs" where 4 different organizations brought their own individual challenges for the hackathon participants to try to solve.</p>
<h2>Problem statement</h2>
<p>One of the challenges put forward by <a href="https://ekmattra.org/">Ekmattra</a>, one of the partnering NGOs, draws attention to the pressing issue of children who are abandoned or who have fled from their villages. The task is to connect these children with the appropriate schools and NGOs that can offer them the necessary support for living and studying. The matchmaking process is considerably complex, often relying on impromptu decisions and relationships within a restricted network. This frequently sparks concerns about whether the decisions made truly serve the best interests of the children.</p>
<p>Recognizing the urgency of this challenge, our borderless team, known as <code>The CzechMate Coders</code>, selected this particular challenge to provide a solution that could have a lasting impact.</p>
<h2>The proposed solution</h2>
<p>In this blog post, we will take you through our team's journey as we tackled the challenge presented by Ekmattra. We will discuss the reasons behind our choice of this specific challenge, the technical decisions we made, and the exceptional efforts we put forth during the competition. With collaboration, ingenuity, and a deep understanding of the problem at hand, our team aimed to make a lasting impact and provide vulnerable children with the support and opportunities they truly deserve.</p>
<p>We believe that a data-driven solution leveraging machine learning strategies can yield promising results. By identifying similarities and comparing profiles of children with those of potential partners, we can significantly simplify the process. To accomplish this, we employ a matching model that continuously learns and improves based on real data from actual cases. Given the wide variety of children's profiles, including factors such as age, health condition, social circumstances, special needs, and cultural-specific requirements, continuous training is crucial for the efficacy of the model.</p>
<p>Safety is our top priority, especially when it comes to children. We ensure that the system is not used for unethical activities or purposes. Moreover, we take into account the safety precautions implemented by partner sites when making the final choice. We maintain a system of regular feedback loops input by the NGO agent after each successful or unsuccessful case, with restricted access to the system to guarantee safe usage. This continuous training of the machine learning model ensures its reliability.</p>
<p>While our solution is digitalized, we strive to preserve the human factor, particularly in the final and most critical phase of the process. By combining technology with human intervention, we aim to create a comprehensive and effective matchmaking system for these vulnerable children.</p>
<h2>Our Tech stack</h2>
<ul>
<li>Machine Learning</li>
<li>React JS (Frontend)</li>
<li>Python (Backend)</li>
</ul>
<h3>Short presentation video</h3>
<ul>
<li><a href="https://drive.google.com/file/d/1XkAZUCVquuK1x_ubMI4IMpcqENCsdtHT/view?usp=share_link">Demo</a></li>
<li><a href="https://docs.google.com/presentation/d/1-B_zK7ExYyKdKOn_ciBlUdQxsaIf1iAr57xjKBNnLBs/edit?usp=sharing">Slides</a></li>
</ul>
<h3>Attachments</h3>
<figure>
<img src="/assets/img/articles/2023-05-23-Establishing-connections-for-children-living-on-the-streets-the-journey-of-The-CzechMate-Coders-in-MonstarHacks-2023/er-diagram.webp">
<figcaption>ER diagram</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2023-05-23-Establishing-connections-for-children-living-on-the-streets-the-journey-of-The-CzechMate-Coders-in-MonstarHacks-2023/ml-model.webp">
<figcaption>ML model</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2023-05-23-Establishing-connections-for-children-living-on-the-streets-the-journey-of-The-CzechMate-Coders-in-MonstarHacks-2023/homepage.webp">
<figcaption>Homepage</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2023-05-23-Establishing-connections-for-children-living-on-the-streets-the-journey-of-The-CzechMate-Coders-in-MonstarHacks-2023/match-page.webp">
<figcaption>Matches Page</figcaption>
</figure>
<h3>Conclusion</h3>
<p>The matchmaking system developed by The CzechMate Coders in response to Ekmattra's challenge has the potential to change the lives of vulnerable children living on the streets. The team's dedication, technical expertise, and innovation have enabled them to create a solution that can have a lasting impact and address a pressing social need. Thanks to Monstarlab, the MonstarHacks competition serves as a testament to the power of collaboration and ingenuity in creating positive social change. The impact of this project will undoubtedly be felt for years to come as children are given a chance for a better future.</p>
<p><em>Article Photo by <a href="https://ekmattra.org/contact-us/">Ekmattra</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[An Introduction to Event-Driven Architecture]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/05/23/Introduction-To-Event-Driven-Architecture</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/05/23/Introduction-To-Event-Driven-Architecture</guid>
            <pubDate>Tue, 23 May 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>With the growing need to build scalable, performing and maintainable enterprise services, microservices and event-driven architectures are often brought up as solutions to the problem, but what should you keep in mind when building services with such architectures?</p>
<h2>Introduction</h2>
<p>This article explains the motivations for using Event-Driven Architecture, the core concepts, and its main challenges.
It also explains how to implement Event-Driven Architecture with NestJS as the backend and RabbitMQ as the communication tool to handle events and messaging.</p>
<h2>Microservices and their shortcomings</h2>
<p>With the growing need for scalable, modular and maintainable systems, microservices architecture is becoming the de facto architecture for building complex enterprise systems.
With a microservices architecture, the system is broken down into smaller fault-tolerant modules that can be scaled, deployed, and maintained separately; individual services can even use different technologies stacks that might be more suitable to the domain or the task that the service is responsible for. For instance, a system where most services are mostly written in Java could be using Python for the service responsible for the Machine-Learning model.</p>
<p>One of the big advantages of going for a microservices architecture is having loosely coupled services so that a failure in one service does not cause failures in other services as a single point of failure. But that advantage can be lost if a blocking communication between services is causing it to become a single point of failure. This problem is very common when two services are communicating over HTTP/TCP in the request/response style.</p>
<p>We may also face latency problems as our service might be waiting for the other service to complete a long process to produce the response, causing slow waiting times that could be felt by the end user.</p>
<h2>Event-Driven Architecture</h2>
<p>To solve this problem a non-blocking method of communication is needed, and that is where Event-Driven Architecture can solve those problems. With Event-Driven Architecture, instead of services communicating through a request/response method, <strong>Event Producers</strong> will post events to an <strong>Event Queue</strong> in the form of messages that will be consumed by <strong>Event Consumers</strong>. Different technologies will use different protocols to deliver their message and some will have support for streaming real-time data, but the basic architecture is as follows:</p>
<p>![Event-driven architecture diagram](/assets/img/articles/2023-05-23-Introduction-To-Event-Driven-Architecture/eda-general.webp</p>
<p>Whenever an Event Producer produces an event, the event is registered in the Event Queue, the Event Producer then can start doing whatever other task it needs to do, as the Producer no longer cares about the response of that event. The event is now stored in the Queue and it can be consumed by the Event consumers that in turn will read the event message and do whatever processing is needed. When the necessary process is successfully finished, the message is deleted from the queue.</p>
<p>We can also have multiple instances of the Event Queue running, so we can guarantee that our Event Queue is fault-tolerable and that our services are now truly loose-coupled. Even in the event of one of our Consumers becoming unavailable, our Producers will still run as they should and the events will be stacked in our Queue, which can be picked up as soon as the needed Consumers are back up. This will also massively help with problems regarding data loss, as our Event Queue can have our messages saved for as long as we need, for as many retries as we need for the Consumer to process the event message.</p>
<h2>Event-Driven Architecture's main concepts</h2>
<p>Here I would like to discuss some of the main concepts regarding Event-Driven Architecture:</p>
<ul>
<li><strong>Event</strong>: A change in the status of an object or a resource. Eg: <em>A new order was made.</em></li>
<li><strong>Event Producer</strong>: A service that produces events to the Event Queue.</li>
<li><strong>Event Queue/Broker</strong>: A queue that holds event messages sent by Event Producers to be consumed by Event Consumers.</li>
<li><strong>Event Consumer</strong>: Consume events in the Event Queue and performs the necessary processing.</li>
<li><strong>Dead Letter Queue</strong>: A parallel queue to the main Event Queue where messages that for some reason could not be processed are moved to, so they can be analyzed and reprocessed if necessary at a later time. For instance, a message with some sort of corrupted information reaches that max number of retries and then is moved to the DLQ.</li>
<li><strong>Ack</strong>: Stands for acknowledgment; it acknowledges that an Event was successfully processed and that it can be safely removed from the queue.</li>
</ul>
<h2>Event-Driven Architecture's challenges</h2>
<p>Despite addressing many of the microservices architecture's problems, Event-Driven Architecture introduces its challenges.</p>
<h3>Complexity</h3>
<p>To become able to develop well designed Event-Driven Architecture based enterprise system has a steep learning curve in both designing and implementing the architecture.</p>
<h3>Compatibility</h3>
<p>Deciding if event-based communication is ideal for a pair or group of web services can be difficult. In some cases, there is no clear need for one service to be hanging waiting for another service to finish its processing. In other cases, the response from the consuming service process is utterly necessary to proceed with the task.</p>
<p>But sometimes it is not as clear if an event-based communication is the best answer for the services in question, and the trade-offs of different communication patterns must be weighed to decide which better suits the specific case.</p>
<h3>Event Duplication and Event Order</h3>
<p>With queues running on multiple instances due to the fault-tolerant and horizontal-scaling nature of microservices, Event duplication is not a matter of if, but when. Dealing with event duplication is one of the big challenges of microservices, and it can be very dangerous depending on the kind of system you are developing. Eg: Billing your client twice, or adding funds to their wallet twice. Some message brokers will provide solutions to this out of the box, <em>with their own trade-offs</em>, and some will prefer to implement the deduplication logic themselves using event ids and other techniques.</p>
<p>On the same note, when event ordering matters it is important to implement a logic that guarantees that events are processed in the intended order. In systems where internet connection is rarer, a large number of events might be stacked before going to the server (Eg: IoT systems) and that could cause events to be all out of order if the logic to prevent it is not implemented.</p>
<h3>Error Handling</h3>
<p>With more moving parts, different situations, different communication methods, etc. introduced by the Event-Driven Architecture, error handling becomes a lot more complex, but it is also a key part of fine-tuning your services.</p>
<h2>Event-Driven Architecture patterns</h2>
<h3>Simple event processing</h3>
<p>![Simple event processing](/assets/img/articles/2023-05-23-Introduction-To-Event-Driven-Architecture/simple-event-processing.webp</p>
<h3>Complex Event Processing</h3>
<p>![Complex event processing](/assets/img/articles/2023-05-23-Introduction-To-Event-Driven-Architecture/complex-event-processing.webp</p>
<h3>Streaming Event Processing</h3>
<p>![Streaming event processing](/assets/img/articles/2023-05-23-Introduction-To-Event-Driven-Architecture/streaming-event-processing.webp</p>
<h2>When you SHOULD use Event-Driven Architecture</h2>
<ul>
<li>When there is asynchronous processing between services or the current synchronous services can be remodeled to asynchronous with no major drawbacks.</li>
<li>When the response from another service is not needed right away and the processing result can be managed by state/status transition. (Eg: Processing, Billing, Completed, Fail, etc).</li>
<li>When process retries are needed and/or the failures need to avoid data loss.</li>
</ul>
<h2>When you SHOULD NOT use Event-Driven Architecture</h2>
<ul>
<li>The response from the service is integral for the process to continue. (Eg. Fraud detection)</li>
<li>When the system is not complex enough for the benefits of an Event-Driven Architecture to outweigh the operational cost and complex implementation.</li>
<li>The team is not used to Event-Driven Architecture and not enough time is available for the team to transition to the new architecture.</li>
</ul>
<h2>Message Brokers and related technologies</h2>
<ul>
<li><strong>RabbitMQ</strong>: Works as a queue, pub/sub message broker using the AMQP protocol. Popular due to its simplicity and reliability.</li>
<li><strong>Apache Kafka</strong>: Distributed event store and streaming processing platform. One of the more popular options due to its performance and implementation possibilities.</li>
<li><strong>AWS SQS/SNS, GCP Pub/sub</strong>: Cloud-based message queue services that are ideal for those who want less operational overhead while working in the cloud.</li>
<li><strong>AWS Kinesis</strong>: A managed data streaming platform for AWS. Similar to Kafka, but with less operational overhead.</li>
</ul>
<p><em>Managed versions of RabbitMQ and Kafka can also be found in some cloud providers.</em></p>
<h2>Architecture example</h2>
<p>A simple example of an ordering app using Event-Driven architecture:</p>
<p>![Ordering app architecture example](/assets/img/articles/2023-05-23-Introduction-To-Event-Driven-Architecture/ordering-app-architecture.webp</p>
<p>With this architecture the Orders Service is decoupled from the Billing service, helping us achieve low latency to the end user, fault tolerance with the loosely coupled services that are able to perform retries on failure, and no data duplication since the Billing service does not need to have its own database.</p>
<p>On the other hand, the billing service is tightly coupled with the Fraud service, since any indication of fraud should cancel the Order right away. But even in the case of a failure in the Fraud service process, the billing service will only <strong>Ack</strong> the event message after the process is finished, so retries are also possible here thanks to the Event-Driven Architecture and Rabbit MQ.</p>
<h2>Code example</h2>
<p>Here are a few code snippets of the main components of this architecture using the <strong>NestJS</strong> backend framework for <strong>NodeJS</strong> and <strong>RabbitMQ</strong>.</p>
<h3>Event Producer</h3>
<p><code>orders.controller.ts</code>:</p>
<pre><code class="hljs language-typescript"><span class="hljs-meta">@Controller</span>(<span class="hljs-string">"orders"</span>)
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">OrdersController</span> {
  <span class="hljs-title function_">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> ordersService: OrdersService</span>) {}

  <span class="hljs-meta">@Post</span>()
  <span class="hljs-keyword">async</span> <span class="hljs-title function_">createOrder</span>(<span class="hljs-params"><span class="hljs-meta">@Body</span>() createOrderRequest: CreateOrderRequest</span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">ordersService</span>.<span class="hljs-title function_">createOrder</span>(createOrderRequest);
  }
}
</code></pre>
<p><code>orders.service.ts</code>:</p>
<pre><code class="hljs language-typescript"><span class="hljs-meta">@Injectable</span>()
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">OrdersService</span> {
  <span class="hljs-title function_">constructor</span>(<span class="hljs-params">
    <span class="hljs-meta">@InjectModel</span>(Order.name) <span class="hljs-keyword">private</span> orderModel: Model&#x3C;OrderDocument>,
    <span class="hljs-meta">@Inject</span>(BILLING_SERVICE) <span class="hljs-keyword">private</span> billingClient: ClientProxy
  </span>) {}

  <span class="hljs-keyword">async</span> <span class="hljs-title function_">createOrder</span>(
    <span class="hljs-attr">createOrderRequest</span>: <span class="hljs-title class_">CreateOrderRequest</span>
  ): <span class="hljs-title class_">Promise</span>&#x3C;<span class="hljs-title class_">OrderInterface</span>> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> order = <span class="hljs-keyword">new</span> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">orderModel</span>({
        ...createOrderRequest,
        <span class="hljs-attr">status</span>: <span class="hljs-string">"PENDING"</span>,
      });

      <span class="hljs-keyword">await</span> order.<span class="hljs-title function_">save</span>();
      <span class="hljs-keyword">const</span> <span class="hljs-attr">orderPayload</span>: <span class="hljs-title class_">OrderInterface</span> = {
        <span class="hljs-attr">orderId</span>: order.<span class="hljs-property">_id</span>.<span class="hljs-title function_">toString</span>(),
        <span class="hljs-attr">productName</span>: order.<span class="hljs-property">productName</span>,
        <span class="hljs-attr">price</span>: order.<span class="hljs-property">price</span>,
        <span class="hljs-attr">buyer</span>: order.<span class="hljs-property">buyer</span>,
        <span class="hljs-attr">status</span>: order.<span class="hljs-property">status</span>,
      };

      <span class="hljs-keyword">await</span> <span class="hljs-title function_">lastValueFrom</span>(
        <span class="hljs-variable language_">this</span>.<span class="hljs-property">billingClient</span>.<span class="hljs-title function_">emit</span>(<span class="hljs-string">"order_created"</span>, {
          orderPayload,
        })
      );

      <span class="hljs-keyword">return</span> orderPayload;
    } <span class="hljs-keyword">catch</span> (error) {
      <span class="hljs-keyword">throw</span> error;
    }
  }
}
</code></pre>
<p>Our entry point is on our <strong>controller</strong> which will send the Order data to the <strong>service</strong> layer that will save the Order as <strong>PENDING</strong> and <strong>emit</strong> an <strong>order_created</strong> event to the <strong>BILLING</strong> queue with a payload, the queue is registered under the <strong>BILLING_SERVICE</strong> const variable. ( <code>export const BILLING_SERVICE = 'BILLING';</code>)</p>
<h3>Event Consumer</h3>
<p><code>billing.controller.ts</code></p>
<pre><code class="hljs language-typescript"><span class="hljs-meta">@Controller</span>()
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">BillingController</span> {
  <span class="hljs-title function_">constructor</span>(<span class="hljs-params">
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> billingService: BillingService,
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> rmqService: RmqService,
    <span class="hljs-meta">@Inject</span>(ORDERS_SERVICE) <span class="hljs-keyword">private</span> ordersClient: ClientProxy
  </span>) {}

  <span class="hljs-meta">@EventPattern</span>(<span class="hljs-string">"order_created"</span>)
  <span class="hljs-keyword">async</span> <span class="hljs-title function_">handleOrderCreated</span>(<span class="hljs-params">
    <span class="hljs-meta">@Payload</span>(<span class="hljs-string">"orderPayload"</span>) order: Order,
    <span class="hljs-meta">@Ctx</span>() context: RmqContext
  </span>) {
    <span class="hljs-comment">//check for fraud</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">billingService</span>.<span class="hljs-title function_">isFraud</span>(order)) {
      order.<span class="hljs-property">status</span> = <span class="hljs-string">"CANCELED_FRAUD"</span>;
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-variable language_">this</span>.<span class="hljs-property">billingService</span>.<span class="hljs-title function_">bill</span>(order);
      order.<span class="hljs-property">status</span> = <span class="hljs-string">"PROCESSED"</span>;
    }

    <span class="hljs-comment">// emit and event to update the order status</span>
    <span class="hljs-keyword">await</span> <span class="hljs-title function_">lastValueFrom</span>(
      <span class="hljs-variable language_">this</span>.<span class="hljs-property">ordersClient</span>.<span class="hljs-title function_">emit</span>(<span class="hljs-string">"order_procesed"</span>, {
        order,
      })
    );

    <span class="hljs-comment">// if no errors happened, acknowledge the order_created process is over and remove it from the queue</span>
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">rmqService</span>.<span class="hljs-title function_">ack</span>(context);
  }
}
</code></pre>
<p>The Billing service now will be watching for the <strong>Event Pattern</strong> <strong><em>order_created</em></strong>, it will check for fraud and then try to bill the order. If no error occurs, it will then <strong>emit</strong> an <strong><em>order_processed</em></strong> event with the new order status. And if no errors happen it will then <strong>Ack</strong> the <strong><em>order_created</em></strong> event message.</p>
<p>The Order service will then update the database with the new status and proceed to <strong>ack</strong> the <strong>order_processed</strong> event message to end the whole process.</p>
<h3>Event Broker</h3>
<p><code>rmq.module.ts</code> (Configuration needed for services using RabbitMQ as Producers)</p>
<pre><code class="hljs language-typescript"><span class="hljs-meta">@Module</span>({
  <span class="hljs-attr">providers</span>: [<span class="hljs-title class_">RmqService</span>],
  <span class="hljs-attr">exports</span>: [<span class="hljs-title class_">RmqService</span>],
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">RmqModule</span> {
  <span class="hljs-keyword">static</span> <span class="hljs-title function_">register</span>({ serviceName, serviceEnvVar }: <span class="hljs-title class_">RmqModuleOptions</span>): <span class="hljs-title class_">DynamicModule</span> {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">module</span>: <span class="hljs-title class_">RmqModule</span>,
      <span class="hljs-attr">imports</span>: [
        <span class="hljs-comment">/*
          Creates a dynamic module based on the serviceName
          This module creates/connects to a queue as an event producer
        */</span>
        <span class="hljs-title class_">ClientsModule</span>.<span class="hljs-title function_">registerAsync</span>([
          {
            <span class="hljs-attr">name</span>: serviceName,
            <span class="hljs-attr">useFactory</span>: <span class="hljs-function">(<span class="hljs-params">configService: ConfigService</span>) =></span> ({
              <span class="hljs-attr">transport</span>: <span class="hljs-title class_">Transport</span>.<span class="hljs-property">RMQ</span>,
              <span class="hljs-attr">options</span>: {
                <span class="hljs-attr">urls</span>: [configService.<span class="hljs-property">get</span>&#x3C;<span class="hljs-built_in">string</span>>(<span class="hljs-string">'RABBIT_MQ_URI'</span>)],
                <span class="hljs-attr">queue</span>: configService.<span class="hljs-property">get</span>&#x3C;<span class="hljs-built_in">string</span>>(serviceEnvVar),
              },
            }),
            <span class="hljs-attr">inject</span>: [<span class="hljs-title class_">ConfigService</span>],
          },
        ]),
      ],
      <span class="hljs-attr">exports</span>: [<span class="hljs-title class_">ClientsModule</span>],
    };
}
</code></pre>
<p>The <strong>register</strong> method will be used by services that need to use RabbitMQ as <strong>Producers</strong>. The service will need to provide a <strong><em>serviceName</em></strong>, the <strong><em>URI for RabbitMQ</em></strong> (eg. running container), and <strong><em>serviceEnvVar</em></strong> as the environment variable holding the <strong><em>queue name</em></strong>.
For instance, you could have an environment variable <code>RABBIT_MQ_BILLING_QUEUE=BILLING</code>, so you would call the <strong><em>register</em></strong> method like <code>RmqModule.register({ serviceName: 'BILLING', serviceEnvVar: 'RABBIT_MQ_BILLING_QUEUE'}),</code>.</p>
<p><code>rmq.service.ts</code>:</p>
<pre><code class="hljs language-typescript"><span class="hljs-meta">@Injectable</span>()
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">RmqService</span> {
  <span class="hljs-title function_">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> configService: ConfigService</span>) {}

  <span class="hljs-comment">// return the config options to subscribe to a queue as an event consumer</span>
  <span class="hljs-title function_">getOptions</span>(<span class="hljs-attr">queue</span>: <span class="hljs-built_in">string</span>, noAck = <span class="hljs-literal">false</span>): <span class="hljs-title class_">RmqOptions</span> {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">transport</span>: <span class="hljs-title class_">Transport</span>.<span class="hljs-property">RMQ</span>,
      <span class="hljs-attr">options</span>: {
        <span class="hljs-attr">urls</span>: [<span class="hljs-variable language_">this</span>.<span class="hljs-property">configService</span>.<span class="hljs-property">get</span>&#x3C;<span class="hljs-built_in">string</span>>(<span class="hljs-string">"RABBIT_MQ_URI"</span>)],
        <span class="hljs-attr">queue</span>: <span class="hljs-variable language_">this</span>.<span class="hljs-property">configService</span>.<span class="hljs-property">get</span>&#x3C;<span class="hljs-built_in">string</span>>(queue),
        noAck,
        <span class="hljs-attr">persistent</span>: <span class="hljs-literal">true</span>,
      },
    };
  }
}
</code></pre>
<p>Returns the configuration needed for a service to consume from a RabbitMQ queue. (The queue variable is the env variable name).</p>
<h3>Event Message Ack</h3>
<p><code>rmq.service.ts</code>:</p>
<pre><code class="hljs language-typescript"><span class="hljs-meta">@Injectable</span>()
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">RmqService</span> {
  <span class="hljs-title function_">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> configService: ConfigService</span>) {}

  <span class="hljs-title function_">ack</span>(<span class="hljs-attr">context</span>: <span class="hljs-title class_">RmqContext</span>): <span class="hljs-built_in">void</span> {
    <span class="hljs-keyword">const</span> channel = context.<span class="hljs-title function_">getChannelRef</span>();
    <span class="hljs-keyword">const</span> originalMessage = context.<span class="hljs-title function_">getMessage</span>();
    channel.<span class="hljs-title function_">ack</span>(originalMessage);
  }
}
</code></pre>
<p>The method needed to <strong>Ack</strong> event messages from a queue when they are no longer needed.</p>
<h2>References</h2>
<ul>
<li>The complete code for the above architecture can be found in the <a href="https://github.com/FelipeCO14/nest-event-driven-app">following Github repository</a></li>
<li><a href="https://docs.nestjs.com/microservices/rabbitmq">NestJS + RabbitMQ integration</a></li>
<li><a href="https://www.ibm.com/topics/event-driven-architecture">IBM Event-Driven Architecture article</a></li>
</ul>
<p><em>Article Photo by <a href="https://kruschecompany.com/">Krusche &#x26; Company</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Improving the admissions process for seriously ill children and their families]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/05/22/Improving-the-admissions-process-for-seriously-ill-children-and-their-families</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/05/22/Improving-the-admissions-process-for-seriously-ill-children-and-their-families</guid>
            <pubDate>Mon, 22 May 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Dealing with families with a seriously ill child can be an emotionally challenging experience for them and requires the utmost sensitivity. Currently, the admissions process has its own challenges. The process involves the manual transfer of personal details and information from cards to an Excel spreadsheet, which is then transferred to their internal CRM system. This process is not only time-consuming but also prone to human errors, resulting in inaccurate records. Additionally, there are risks to the security of personal data due to the storage of cards in a secure box or filing cabinet.</p>
<p>To address these challenges and improve the admissions process, our Team Monstars Inc., as part of the <a href="https://www.linkedin.com/showcase/monstarhacks">MonstarHacks</a> 2023 Monstarlab internal hackathon, proposed a value-added solution for the NGO <a href="https://www.sickchildrenstrust.org/">The Sick Children's Trust</a> delivered in three phases (short-term, medium-term, and long-term) through the development of a super app. The short-term solution will consist of two steps. The first step will address the first pain point in the data collection process by implementing an API integration between hospitals partnered with the NGO and the CRM tools currently in place by the NGO for automatic data transfer.</p>
<p>The second step will involve configuring integration between the NGO CRM and ChatGPT image analysis capabilities to extract the relevant data into the CRM system automatically. This two-step solution will provide immediate value creation for the NGO, allowing the team to focus on developing plans to achieve the long-term vision of a super app called <strong>weCare</strong>. Our plan will deliver benefits in the short, medium, and long term for both the NGO and the families they help.</p>
<p><img src="/assets/img/articles/2023-05-22-Improving-the-admissions-process-for-seriously-ill-children-and-their-families/design.webp" alt=""></p>
<h2>Short-term solution</h2>
<p>We propose the creation of an API between hospitals and the NGO CRM to mitigate the risks of inaccurate records and data security. The API will retrieve patient data upon consent directly from the hospital's electronic medical records system. This will save time and effort while ensuring the accuracy of data. Additionally, this API will comply with GDPR regulations, ensuring the security and privacy of personal data.</p>
<p>By digitizing this process, we can eliminate human errors and ensure the security of personal data. This resolves the most pressing challenge of manual information collection with creative and simple digital solutions. We will also create an integration between ChatGPT image upload capabilities and the NGO CRM to automatically extract the relevant data from the physically stored cards directly into the CRM tool.</p>
<h2>Medium-term solution</h2>
<p>To further improve the admissions process, we propose the creation of an app that integrates the various functions of the NGO. The app will provide staff with a unified platform to manage guests' and partners' data. It will be designed with a user-friendly interface to ensure ease of use for both staff and families. The app will provide automated emotional support services and guides to the facilities and local points of interest.</p>
<h2>Long-term Solution</h2>
<p>In the longer perspective, the super app <strong>weCare</strong> will be gradually updated to further support the NGO and their guest experience to provide 360-degree support through automated processes. the NGO will have access to a larger set of guest data, which can be used to maintain and build on their existing guest relationship management. This can include automated notification of when it's a good time to reach out to previous guests for fundraising or other requests.</p>
<h2>Conclusion</h2>
<p>Improving the admissions process for families with seriously ill children is a crucial step towards ensuring that they receive the care and support they need. By implementing an API between hospitals and the NGO, along with the creation of a super app, we can streamline the admissions process, eliminate human errors, ensure data security, and improve the overall guest experience.</p>
<p>Our immediate solution will allow for a modular and scalable process that streamlines admissions and data collection, by replacing the manual card system with a digital solution. In the medium term, we propose the creation of the app weCare. In the long term, we propose the expansion of the created app into a super app to integrate the various functions of the NGO.</p>
<p>With these solutions, we can ensure that families feel welcome and supported during their stay, allowing the NGO to really focus on what matters - supporting the families.</p>
<p><em>Article Photo by <a href="https://www.sickchildrenstrust.org/">The Sick Children's Trust</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Team Rakshak's experience taking part in MonstarHacks 2023]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/05/17/Team-Rakshaks-experience-taking-part-in-MonstarHacks-2023</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/05/17/Team-Rakshaks-experience-taking-part-in-MonstarHacks-2023</guid>
            <pubDate>Wed, 17 May 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Participating in <a href="https://www.linkedin.com/showcase/monstarhacks">MonstarHacks</a>, Monstarlab's yearly internal hackathon, for the first time was an experience outside our everyday project routine. Below, we share our experience and provide insight into the product we aimed to create.</p>
<h2>Why was this challenge interesting to us?</h2>
<p>NGOs play an important role in addressing social and environmental challenges around the world. They often work with limited resources and staff, and technology can help them to increase their impact and reach more people in need. As engineers, our role in society is to utilize our technical skills and knowledge for its betterment.</p>
<h2>Our team, Team Rakshak:</h2>
<ul>
<li>Shaikh Huma: Flutter developer from the Monstarlab Tokyo office</li>
<li>Dinakar Maurya: Lead developer from the Monstarlab Tokyo office</li>
</ul>
<h2>Deciding on the topic</h2>
<p>On Day 1 of the hackathon, we opted for a morning call to decide on the NGO with which we would collaborate and to brainstorm ideas. We chose to proceed with <a href="https://www.asha-np.org/en">Asha</a>, an NGO based in Japan. Given that both team members were from the Tokyo office, we were eager to take up this challenge. Upon reading the problem statement and brainstorming a few ideas, we came across several challenges. We addressed these in discussions with our mentor. Subsequently, we decided to have a conversation with the NGO representative, thus scheduling a meeting with him on Day 2. After getting more insights on the problem statement, we moved forward with our product prototyping.</p>
<h2>Working on the product</h2>
<p>The hackathon spanned across 3 days, coinciding with Japan's Golden Week, which is typically a vacation period. Though we were in a holiday mood, we still went forward with all enthusiasm to build our product. Since we had no designers on the team, we sketched out a basic app structure with our features and began working on the app, which was coded in Flutter. With Dinakar leading the team, tasks were allocated, and we commenced work, holding meetings throughout the three days.</p>
<h2>The problem statement</h2>
<ul>
<li><strong>How do we deliver practical training sessions (Injury Assessment, First-Aid Kit)?</strong>
Digital Solution - We can deliver smooth and fast video lectures using the Agora SDK.</li>
<li><strong>How do we overcome the human resources barrier (limited lecturers) to provide lectures in each school?</strong>
We solve this issue by offering live lecture sessions and using auto-recording for future repeated lectures.</li>
<li><strong>How do we make it attractive to local people?</strong>
We have an easy-to-use app with a single screen. The app includes a feature powered by Chat GPT, which can answer all kinds of questions in the local language.</li>
<li><strong>How do we overcome the financial resource barrier?</strong>
We use a single codebase Flutter app that runs on all platforms, including iOS and Android.</li>
<li><strong>How do we overcome the language barrier when using the system (laptop, mobile phones, iPad)?</strong>
We provide solutions in four languages. We can offer support for multiple languages.</li>
<li><strong>Can the app be used offline if there is no internet?</strong>
Yes, the app can be used both online and offline for the convenience of the users.</li>
</ul>
<h2>Participating for the first time in MonstarHacks</h2>
<p>We participated in a hackathon for the first time, which was both a thrilling and challenging experience. It gave us a unique opportunity to collaborate with individuals from diverse backgrounds and work on a problem or project within a specific timeframe. As someone who has never been a part of a hackathon before, the experience was daunting, yet exhilarating.</p>
<p>At first, we felt overwhelmed by the fast-paced environment of a hackathon and the number of teams taking part from other offices. Other participants were bustling around, working on their projects with intense focus, which made us wonder if we could keep up and make a meaningful contribution.</p>
<p>As we started to work on the project, we felt a mix of emotions. On one hand, there was a sense of excitement and enthusiasm about the project and the opportunity to learn and collaborate with others. On the other hand, there was a feeling of anxiety or pressure to perform well and deliver a successful project within the time limit.</p>
<p>While working on our project, we encountered challenges and roadblocks. Despite these obstacles, there was a sense of camaraderie within the team. We were able to bounce ideas off each other, share knowledge and expertise, and work towards a common goal.</p>
<p>As the hackathon came to a close, we felt a sense of pride in what we had accomplished, yet were nervous about presenting our project to the judges and other participants. Regardless, the experience was incredibly rewarding, providing valuable lessons and insights. Whether we won or lost, we gained invaluable experience and skills that can be applied to future projects and endeavors.</p>
<h2>The proposed solution</h2>
<ul>
<li>Our solution is a Flutter based app.</li>
<li><strong>Volunteer Login</strong> - To be used in middle schools with the following features:
<ul>
<li>Login</li>
<li>Video list</li>
<li>See the past lectures list</li>
<li>Play lecture from the list</li>
<li>Chat/ Help</li>
<li>Join a live session</li>
</ul>
</li>
<li><strong>Lecturer Login</strong> - To be used for recording lectures with the following features:
<ul>
<li>Login</li>
<li>Video list</li>
<li>Chat/ Help</li>
<li>Host a live session</li>
<li>Record a lecture</li>
<li>Record a lecture using video screen</li>
<li>Preview and save lecture</li>
</ul>
</li>
</ul>
<h2>App flow</h2>
<p><img src="/assets/img/articles/2023-05-17-Team-Rakshaks-experience-taking-part-in-MonstarHacks-2023/app-flow.webp" alt="">
<img src="/assets/img/articles/2023-05-17-Team-Rakshaks-experience-taking-part-in-MonstarHacks-2023/app-flow-volunteer.webp" alt="">
<img src="/assets/img/articles/2023-05-17-Team-Rakshaks-experience-taking-part-in-MonstarHacks-2023/app-flow-lecturer.webp" alt=""></p>
<h2>App UI</h2>
<p><img src="/assets/img/articles/2023-05-17-Team-Rakshaks-experience-taking-part-in-MonstarHacks-2023/screen-en.webp" alt="">
<img src="/assets/img/articles/2023-05-17-Team-Rakshaks-experience-taking-part-in-MonstarHacks-2023/screen-jp.webp" alt="">
<img src="/assets/img/articles/2023-05-17-Team-Rakshaks-experience-taking-part-in-MonstarHacks-2023/screen-hi.webp" alt="">
<img src="/assets/img/articles/2023-05-17-Team-Rakshaks-experience-taking-part-in-MonstarHacks-2023/screen-ne.webp" alt=""></p>
<h2>Future scope proposed</h2>
<ul>
<li>Voice support through AI Chat GPT</li>
<li>Capability to find local professionals</li>
<li>Addition of FAQ, Contact Us, and other informative pages</li>
<li>Tutorial screens that appear immediately after login</li>
<li>Ability to answer custom NGO questions</li>
<li>Features related to education and teaching methods</li>
<li>A reference blog for additional information</li>
<li>Synchronization of local videos with remote videos</li>
<li>Chat feature during video calls for a more interactive live session</li>
</ul>
<p><em>Please note that these features are not yet available and may be implemented at a later time.</em></p>
<h2>Costs</h2>
<ul>
<li>Expense related to the necessary hardware such as mobile phones, Android phones, Android tablets, iPhones, and iPads.</li>
<li>Cost involved in local video recording such as server storage, local mobile phone storage, and free Google Cloud Hosting for backend server/cloud hosting.</li>
<li>Charges for using Agora video calls, with a pricing model of the first 10,000 minutes free every month, equivalent to approximately 166 hours. For further details, refer to their website <a href="https://www.agora.io/en/pricing/">https://www.agora.io/en/pricing/</a>.</li>
</ul>
<h2>Our tech stack</h2>
<ul>
<li><strong>ChatGPT</strong>, available at <a href="https://openai.com/">https://openai.com/</a>, offers an advanced chat engine that uses AI to enhance work and creativity.</li>
<li><strong>Agora Video Call</strong>, accessible at <a href="https://videocall.agora.io/welcome">https://videocall.agora.io/welcome</a>, provides free HD video and voice calls that can be made anytime and anywhere.</li>
<li><strong>Flutter</strong>, a framework created by Google and accessible at <a href="https://docs.flutter.dev/get-started/install">https://docs.flutter.dev/get-started/install</a>, is an open-source tool that enables building multi-platform, natively compiled, and visually stunning applications from a single codebase.</li>
<li><strong>Dart</strong>, programming language available at <a href="https://dart.dev/">https://dart.dev/</a>, is a language optimized for clients that enables fast app development on multiple platforms.</li>
</ul>
<h2>Conclusion</h2>
<p>In summary, we crafted a small and functioning prototype to enhance health literacy for Asha NGO as part of MonstarHacks.</p>
<h2>Related Articles</h2>
<ul>
<li><a href="https://engineering.monstar-lab.com/en/post/2023/05/13/ChatGPT-meets-Flutter-A-step-by-step-integration-guide/">ChatGPT meets Flutter: A step by step integration guide - English</a></li>
<li><a href="https://engineering.monstar-lab.com/jp/post/2023/05/08/Integration-of-ChatGPT-in-Flutter/">FlutterでのChatGPTの統合 - Japanese</a></li>
</ul>
<h2>References</h2>
<ul>
<li><a href="https://github.com/monstar-lab-oss/flutter-template-riverpod">MonstarLab Flutter Template - OSS Repository</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[An overview of PASETO Token-Based Authentication]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/05/16/An-overview-of-PASETO-Token-Based-Authentication</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/05/16/An-overview-of-PASETO-Token-Based-Authentication</guid>
            <pubDate>Tue, 16 May 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>When it comes to securing web applications, there are various options for authentication and authorization. Two popular choices are PASETO and JWT.</p>
<p>While JWT has been widely utilized for many years, PASETO is a relatively new technology that has gained traction due to its enhanced security features.</p>
<p>PASETO (Platform-Agnostic Security Tokens) is both a specification and a reference implementation for secure stateless tokens. It serves as a highly secure alternative to JWT.</p>
<h3>Understanding Token-Based Authentication</h3>
<p>The typical flow for authenticating a user in a secured API involves the following steps:</p>
<ol>
<li>The user provides their username and password for authentication.</li>
<li>Upon successful verification, the API returns an access token (either JWT or PASETO).</li>
<li>The access token is included in the Authorization header when making requests to protected endpoints.</li>
<li>The API server validates the token and responds with the appropriate secured data if the access is valid.</li>
</ol>
<p><img src="/assets/img/articles/2023-05-16-An-overview-of-PASETO-Token-Based-Authentication/token_based_authentication.webp" alt="Modern Token Base Authentication"></p>
<h3>Understanding JWT and Its Limitations</h3>
<p>JWT consists of three parts:</p>
<ul>
<li>The header, which contains the token's signing algorithm.</li>
<li>The payload, which holds information about the authenticated user and additional data. The server can customize this part of the payload.</li>
<li>The signature, generated by the server using a private key. This signature enables the server to verify the authenticity of the JWT during the validation process.</li>
</ul>
<p>![JWT Demo](/assets/img/articles/2023-05-16-An-overview-of-PASETO-Token-Based-Authentication/jwt.webp</p>
<p><em>Photo by <a href="https://www.wallarm.com/what/token-based-authentication">Wallarm</a></em></p>
<p>While JWT provides flexibility in choosing the digital signature algorithm and verification implementation, this flexibility also introduces vulnerabilities.</p>
<p>There are multiple algorithms to choose from, some of which may be weak and susceptible to attacks, such as ECDSA (vulnerable to invalid-curve attacks) or RSA PKCSv1.5 (vulnerable to padding oracle attacks).</p>
<p>JWT implementations are also prone to errors, which can result in security vulnerabilities like broken JWT validation.</p>
<p>When used correctly, JWT can be a reliable and flexible authentication system. However, caution must be exercised to avoid exposing the server to potential attacks.</p>
<p>In contrast, PASETO addresses these issues by simplifying the implementation process.</p>
<h2>PASETO: The Solution</h2>
<p>While JWT offers implementation flexibility, PASETO takes a more rigid approach. However, this rigidity helps prevent implementation errors and misuse.</p>
<p>PASETO is designed to be user-friendly and offers higher cryptographic resilience compared to JWT.</p>
<p>When using PASETO, the user only needs to configure two settings:</p>
<ul>
<li>The PASETO version (<strong><code>v1</code></strong>, <strong><code>v2</code></strong>, <strong><code>v3</code></strong>, or <strong><code>v4</code></strong>) specified via the <strong><code>version</code></strong> field of the token.</li>
<li>Whether encryption and decryption should be symmetric or asymmetric, indicated by the <strong><code>purpose</code></strong> field of the token.</li>
</ul>
<p>That's all it takes.</p>
<h3>PASETO Token Structure</h3>
<p>Similar to JSON Web Tokens (JWTs), PASETO tokens are composed of dot-separated base64url encoded data organized in the following format:</p>
<pre><code class="hljs">version.purpose.payload.footer
</code></pre>
<ul>
<li><strong><code>version</code></strong>: Allows for incremental improvements to the token format. The current versions are "v1," "v2," "v3," and "v4."
<ul>
<li><strong><code>v1</code></strong>: Utilizes strong cryptographic primitives that are widely available today.</li>
<li><strong><code>v2</code></strong>: Utilizes newer and stronger cryptographic primitives, but is supported by fewer cryptographic libraries.</li>
</ul>
</li>
<li><strong><code>purpose</code></strong>: A concise string that describes the token format as either "local" or "public."
<ul>
<li><strong><code>local</code></strong>: The token's payload is encrypted and can only be accessed by parties possessing the shared key.</li>
<li><strong><code>public</code></strong>: The payload is NOT encrypted; instead, it is signed and verified using a public key.</li>
</ul>
</li>
<li><strong><code>payload</code></strong>: Encoded data with a format specific to the token's version and purpose.</li>
<li><strong><code>footer</code></strong> (optional): Unencrypted JSON, typically used to store the ID of a public key for token validation.</li>
</ul>
<p>All PASETO token formats are tamperproof, ensuring that any modifications to the token result in failed validation.</p>
<h3>Local Tokens (Symmetric Encryption)</h3>
<p>Local tokens are always symmetrically encrypted using a shared secret key. This means that the contents of a local PASETO token cannot be viewed without the correct secret key.</p>
<p>![Symmetric encryption](/assets/img/articles/2023-05-16-An-overview-of-PASETO-Token-Based-Authentication/symmetric.webp</p>
<p>Here is an example of a local PASETO token, including its decoded payload, optional footer, and the signing key used to sign the information.</p>
<p><img src="/assets/img/articles/2023-05-16-An-overview-of-PASETO-Token-Based-Authentication/paseto_symmetric_demo.webp" alt="Symmetric encryption"></p>
<h3>Public Tokens (Asymmetric Encryption)</h3>
<p>Public PASETO tokens are suitable for scenarios where it is not safe to share a secret key with all involved parties.</p>
<p>Public tokens are not encrypted but are digitally signed. This means that if an attacker obtains a public PASETO token, they can view its contents but cannot modify it without detection due to the digital signatures used in PASETO tokens.</p>
<p>If an attempt is made to verify a maliciously modified public PASETO token, an error will occur.</p>
<p>![Asymmetric encryption](/assets/img/articles/2023-05-16-An-overview-of-PASETO-Token-Based-Authentication/asymmetric.webp</p>
<p>Here is an example of a public PASETO token, including its decoded payload, optional footer, and the public and private keys used to sign the information.</p>
<p><img src="/assets/img/articles/2023-05-16-An-overview-of-PASETO-Token-Based-Authentication/paseto_asymmetric_demo.webp" alt="Asymmetric encryption"></p>
<h3>Versions</h3>
<p>Each PASETO version introduces improvements over its predecessor. To implement the PASETO specification correctly, refer to the details provided for each version:</p>
<p><a href="https://github.com/paseto-standard/paseto-spec/tree/master/docs/01-Protocol-Versions">https://github.com/paseto-standard/paseto-spec/tree/master/docs/01-Protocol-Versions</a></p>
<h3>Libraries</h3>
<p>You can find all the libraries implementing PASETO for all the popular languages along with the supported versions here:</p>
<p><a href="https://paseto.io/">https://paseto.io/</a></p>
<h3>Implementation using the Ruby library</h3>
<p>We will follow an example of usage of the Ruby library implementing PASETO:</p>
<p><a href="https://github.com/bannable/paseto">https://github.com/bannable/paseto</a></p>
<p>We need to first install the gem.</p>
<pre><code class="hljs language-yaml"><span class="hljs-string">gem</span> <span class="hljs-string">'ruby-paseto'</span>
<span class="hljs-string">gem</span> <span class="hljs-string">'rbnacl'</span><span class="hljs-string">,</span> <span class="hljs-string">'~> 7.1.1'</span> <span class="hljs-comment"># optional - only if PASETO version 4 will be used</span>
</code></pre>
<h3>Symmetric encryption (local)</h3>
<pre><code class="hljs language-ruby"><span class="hljs-keyword">require</span> <span class="hljs-string">'paseto'</span>

<span class="hljs-comment">####################</span>
<span class="hljs-comment">#### ENCRYPTION ####</span>
<span class="hljs-comment">####################</span>

<span class="hljs-comment"># typically, this shared 32 bytes key is stored in both the authentication server</span>
<span class="hljs-comment"># and the client server</span>
shared_secret_key = SecureRandom.bytes(<span class="hljs-number">32</span>)

<span class="hljs-comment"># initialize the PASETO encrypter/decrypter</span>
crypt = <span class="hljs-title class_">Paseto::V4::Local</span>.new(<span class="hljs-symbol">ikm:</span> shared_secret_key) <span class="hljs-comment"># version: v4 / purpose: local</span>

<span class="hljs-comment"># payload in plain text</span>
claims = { <span class="hljs-string">"company"</span> => <span class="hljs-string">"monstarlab"</span> }
footer = { <span class="hljs-string">"viewable"</span> => <span class="hljs-string">"yes"</span> }

<span class="hljs-comment"># encode the payload and get the PASETO</span>
encrypted_token = crypt.encode(claims, <span class="hljs-symbol">footer:</span> <span class="hljs-variable constant_">JSON</span>.dump(footer))
<span class="hljs-comment"># => "v4.local.E1Y_KQ6Ek8lSOKrJ6kI1YjWXfAKJ0OEcdhUPywznjBjK5SGDUr4-6rbaZk-CIM_mdQgQHGPj8yAWQswktkCe_Sm_Nj9eEfDxNBFeAC2KsgqFCjF07VJo5ail0jnSTNM0-PekMytJleea8OvNkKdoLs4GKAsZTTJ_-DEMmOMyVlddlmaWoVwnF2JkpjBzFRO7d6PlIIY29rQWOXSvZxoLEqkE5XJvHpFs4NTuCHnF4Pko10X_sgHCPkTGXkWNDg.eyJ2aWV3YWJsZSI6InllcyJ9"</span>

<span class="hljs-comment">####################</span>
<span class="hljs-comment">#### DECRYPTION ####</span>
<span class="hljs-comment">####################</span>

<span class="hljs-comment"># typically, this shared 32 bytes key is stored in both the authentication server</span>
<span class="hljs-comment"># and the client server</span>
shared_secret_key = SecureRandom.bytes(<span class="hljs-number">32</span>)

<span class="hljs-comment"># initialize the PASETO encrypter/decrypter</span>
crypt = <span class="hljs-title class_">Paseto::V4::Local</span>.new(<span class="hljs-symbol">ikm:</span> shared_secret_key) <span class="hljs-comment"># version: v4 / purpose: local</span>
encrypted_token = <span class="hljs-string">"v4.local.E1Y_KQ6Ek8lSOKrJ6kI1YjWXfAKJ0OEcdhUPywznjBjK5SGDUr4-6rbaZk-CIM_mdQgQHGPj8yAWQswktkCe_Sm_Nj9eEfDxNBFeAC2KsgqFCjF07VJo5ail0jnSTNM0-PekMytJleea8OvNkKdoLs4GKAsZTTJ_-DEMmOMyVlddlmaWoVwnF2JkpjBzFRO7d6PlIIY29rQWOXSvZxoLEqkE5XJvHpFs4NTuCHnF4Pko10X_sgHCPkTGXkWNDg.eyJ2aWV3YWJsZSI6InllcyJ9"</span>

<span class="hljs-comment"># note that the last part of the token `eyJ2aWV3YWJsZSI6InllcyJ9` is only a base64 encoded string</span>
<span class="hljs-comment"># meaning anyone can see its contents</span>
Base64.decode64(<span class="hljs-string">"eyJ2aWV3YWJsZSI6InllcyJ9"</span>)
<span class="hljs-comment"># => "{\"viewable\":\"yes\"}"</span>

<span class="hljs-comment"># decoding the token and get the payload</span>
crypt.decode(encrypted_token)
<span class="hljs-comment"># => &#x3C;Paseto::Result</span>
<span class="hljs-comment">#           claims={</span>
<span class="hljs-comment">#              "exp"=>"2023-05-10T11:18:41+09:00",</span>
<span class="hljs-comment">#              "iat"=>"2023-05-10T10:18:41+09:00",</span>
<span class="hljs-comment">#              "nbf"=>"2023-05-10T10:18:41+09:00",</span>
<span class="hljs-comment">#              "company"=>"monstarlab"},</span>
<span class="hljs-comment">#           footer={"viewable"=>"yes"}</span>
<span class="hljs-comment">#    ></span>

<span class="hljs-comment"># if the token has been maliciously modified, an error will be raised</span>
encrypted_token[-<span class="hljs-number">1</span>] = <span class="hljs-string">"M"</span>
crypt.decode(encrypted_token)
<span class="hljs-comment"># Paseto::InvalidAuthenticator: Paseto::InvalidAuthenticator</span>
<span class="hljs-comment"># from /Users/tony_duong/.rvm/gems/ruby-3.1.3/gems/ruby-paseto-0.1.2/lib/paseto/symmetric_key.rb:53:in `decrypt'</span>

</code></pre>
<h3>Usage of asymmetric encryption (public)</h3>
<p>We first start by generating a public/private key pair.</p>
<pre><code class="hljs language-bash">ssh-keygen
<span class="hljs-comment"># Output: public key and private key</span>
</code></pre>
<pre><code class="hljs language-ruby"><span class="hljs-keyword">require</span> <span class="hljs-string">'paseto'</span>

<span class="hljs-comment">####################</span>
<span class="hljs-comment">#### ENCRYPTION ####</span>
<span class="hljs-comment">####################</span>

<span class="hljs-comment"># initialize the PASETO encrypter/decrypter</span>
pem = File.read(<span class="hljs-string">'private_key'</span>)
signer = <span class="hljs-title class_">Paseto::V4::Public</span>.new(pem)

<span class="hljs-comment"># payload in plain text</span>
claims = { <span class="hljs-string">"company"</span> => <span class="hljs-string">"monstarlab"</span> }
footer = { <span class="hljs-string">"viewable"</span> => <span class="hljs-string">"yes"</span> }

<span class="hljs-comment"># encode the payload and get the PASETO</span>
signed_token = signer.encode(claims, <span class="hljs-symbol">footer:</span> footer)
<span class="hljs-comment"># => "v4.public.eyJleHAiOiIyMDIzLTA1LTEwVDExOjQ2OjA0KzA5OjAwIiwiaWF0IjoiMjAyMy0wNS0xMFQxMDo0NjowNCswOTowMCIsIm5iZiI6IjIwMjMtMDUtMTBUMTA6NDY6MDQrMDk6MDAiLCJjb21wYW55IjoibW9uc3RhcmxhYiJ9taKQPCARAZHv85xk7yaWPDeWHaHt981eHmoiYIrIcA-monnIbMax2EDxIjObgr6qLLuYzAH4BK5N6q0TJANeBg.eyJ2aWV3YWJsZSI6InllcyJ9"</span>

<span class="hljs-comment">####################</span>
<span class="hljs-comment">#### DECRYPTION ####</span>
<span class="hljs-comment">####################</span>

verifier = <span class="hljs-title class_">Paseto::V4::Public</span>.new(<span class="hljs-string">'public_key'</span>)

<span class="hljs-comment"># when initialized with a public key, only verification/decoding can be performed</span>
<span class="hljs-comment"># if encode is called, error is raised</span>
verifier.encode({<span class="hljs-string">'foo'</span> => <span class="hljs-string">'bar'</span>})
<span class="hljs-comment"># => ArgumentError</span>

signed_token = <span class="hljs-string">"v4.public.eyJleHAiOiIyMDIzLTA1LTEwVDExOjQ2OjA0KzA5OjAwIiwiaWF0IjoiMjAyMy0wNS0xMFQxMDo0NjowNCswOTowMCIsIm5iZiI6IjIwMjMtMDUtMTBUMTA6NDY6MDQrMDk6MDAiLCJjb21wYW55IjoibW9uc3RhcmxhYiJ9taKQPCARAZHv85xk7yaWPDeWHaHt981eHmoiYIrIcA-monnIbMax2EDxIjObgr6qLLuYzAH4BK5N6q0TJANeBg.eyJ2aWV3YWJsZSI6InllcyJ9"</span>
verifier.decode(signed_token)
<span class="hljs-comment"># => &#x3C;Paseto::Result</span>
<span class="hljs-comment">#           claims={</span>
<span class="hljs-comment">#              "exp"=>"2023-05-10T11:18:41+09:00",</span>
<span class="hljs-comment">#              "iat"=>"2023-05-10T10:18:41+09:00",</span>
<span class="hljs-comment">#              "nbf"=>"2023-05-10T10:18:41+09:00",</span>
<span class="hljs-comment">#              "company"=>"monstarlab"},</span>
<span class="hljs-comment">#           footer={"viewable"=>"yes"}</span>
<span class="hljs-comment">#    ></span>
</code></pre>
<h2>Conclusion</h2>
<p>In conclusion, we have explored the vulnerabilities that can arise from the careless use of JSON Web Tokens (JWTs). While JWTs can serve as an effective means of incorporating authentication into a system when used correctly, their flexible specification can potentially lead to implementation errors and subsequent security issues.</p>
<p>To address these concerns, PASETO has been introduced as an alternative solution with specific design goals in mind:</p>
<ul>
<li>Simplicity of use: PASETO simplifies the token creation process by requiring only the specification of the <strong><code>purpose</code></strong> and <strong><code>version</code></strong> parameters.</li>
<li>Resistance to implementation errors: Unlike JWTs, PASETO eliminates the need to choose from a wide range of potentially insecure cryptographic algorithms, thereby reducing the risk of implementation mistakes.</li>
</ul>
<p>PASETO takes a developer-first approach to security tokens by streamlining the decision-making process for developers. By offering two distinct purposes, namely the choice between a symmetric or asymmetric security model, PASETO automatically selects the most suitable options for authenticated encryption and digital signatures. This ensures that your tokens remain secure and immune to cryptographic vulnerabilities.</p>
<p>Overall, PASETO provides a more robust and straightforward approach to security token management, mitigating the risks associated with JWTs while maintaining a high level of security for your system.</p>
<h2>References</h2>
<ul>
<li><a href="https://www.linkedin.com/pulse/paseto-jwt-killer-sandesh-dahake/">PASETO: The JWT Killer? - article by Sandesh Dahake</a></li>
<li><a href="https://github.com/paseto-standard/paseto-spec">PASETO: Platform-Agnostic Security Tokens - Github repository</a></li>
<li><a href="https://www.youtube.com/watch?v=nBGx-q52KAY&#x26;ab_channel=TECHSCHOOL">Why PASETO is better than JWT for token-based authentication? - Youtube video by Tech School</a></li>
<li><a href="https://developer.okta.com/blog/2020/07/23/introducing-jpaseto">Introducing JPaseto: Security Tokens For Java - article by Brian Demers</a></li>
<li><a href="https://token.dev/paseto/">Encode or Decode PASETO</a></li>
<li><a href="https://github.com/bannable/paseto">Paseto - Github repository</a></li>
</ul>
<p><em>Article Photo by <a href="https://unsplash.com/photos/XUJcmgEhpjA">Erik Mclean</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[MonstarHacks solution to decrease pet relinquishment in a Colombian NGO shelter]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/05/15/Monstarhacks-solution-to-decrease-pet-relinquishment-in-a-colombian-NGO-shelter</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/05/15/Monstarhacks-solution-to-decrease-pet-relinquishment-in-a-colombian-NGO-shelter</guid>
            <pubDate>Mon, 15 May 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>We had a blast participating for the first time in <a href="https://www.linkedin.com/showcase/monstarhacks">MonstarHacks</a> and even though the pressure can be very high due to the limited amount of time, the resulting ideas can be truly amazing. Here we would like to share a little bit of our discoveries and solution trying to solve the <a href="https://elgatio.org/">El Gatio</a> NGO challenge.</p>
<p>![Monstarlab colombian coders](/assets/img/articles/2023-05-15-Monstarhacks-solution-to-decrease-pet-relinquishment-in-a-colombian-NGO-shelter/chicharron-coders.webp</p>
<h3>The problem</h3>
<p>Overpopulation of stray pets is a complex problem that has far-reaching environmental consequences that should not be ignored. Stray pets can disrupt natural food chains, hunt and kill native wildlife, and spread disease to other animals in the area. But yet there's very limited governmental resources being directed to address this problem in a humane way. This is why cat rescue organizations face some big challenges as they are obligated to work with their own resources making it very difficult and time-consuming to find foster homes, match cats with the right families to avoid future relinquishments and perform a continuous follow-up process.</p>
<h3>Insights we found</h3>
<ol>
<li>
<p>After some investigation we found that the common approach for shelters is to treat foster home applications and adoption application as two different processes, but for us is actually a single one, because people's intention may vary during time and what first started as a fostering can evolve into an adoption.</p>
</li>
<li>
<p>Creating matches for successful adoptions (aka adoption without relinquishment) is much more complex than just measuring compatibility between two profiles. Because this is more like finding the “love of your life” and chemistry resulting from physical interaction plays a substantial role into determining whether you’ll commit to a pet or not.</p>
</li>
</ol>
<p><strong>Our conclusion</strong>: If we allow people to live with cats for a trial period in a foster home, we can likely focus on converting positive experiences into successful adoptions, and prevent adoptions from happening when people are not ready to commit.</p>
<h3>Our Solution</h3>
<p>Transform the shelter's fostering and adoption processes into a unified system by implementing SaaS (Software as a Service) trial best practices, with systematic follow-up checks to encourage adoption, enhance post-adoption retention, and expand the network of foster homes. Therefore:</p>
<ul>
<li>People who only want to be foster homes (for their own reasons) could remain in a "trial version" with different cats indefinitely.</li>
<li>People who wanted to be foster homes and, during the "trial," develop a good relationship with the cat could be led/converted into adopting the cat (aka "upgrading from the trial version").</li>
<li>People who want to adopt but are inexperienced may start to develop a poor relationship with the cat. In such cases, they could receive priority assistance to allow the shelter to provide guidance and improve the engagement.</li>
<li>People who want to adopt and, during the "trial," start developing a bad relationship with the cat, despite assistance, could have the option to trial a different cat or decide not to adopt if they realize they aren't prepared.</li>
<li>People who want to adopt and, during the "trial," develop a good relationship with the cat will likely cement their decision and become the most loyal adopters.</li>
</ul>
<p>This will enable us to solve the most complex problems of the challenge: increasing the number of available foster homes while at the same time reducing the number of people who would ultimately return an adopted cat. By incorporating systematic follow-ups, the shelters will be able to conserve resources with users who are managing well, while also providing timely guidance to those in need. This approach will also help identify opportunities for fostering to adoption conversion. Most importantly, it will allow us to understand and identify friction points in the cat-owner relationships and improve them for the benefit of people's and pets' well-being and harmony in our society.</p>
<p>![Applicant's app](/assets/img/articles/2023-05-15-Monstarhacks-solution-to-decrease-pet-relinquishment-in-a-colombian-NGO-shelter/app.webp</p>
<p>![Shelter's backoffice](/assets/img/articles/2023-05-15-Monstarhacks-solution-to-decrease-pet-relinquishment-in-a-colombian-NGO-shelter/backoffice.webp</p>
<h3>Resources</h3>
<ul>
<li><a href="https://www.nature.com/articles/s41598-022-05101-5">The impact of returning a pet to the shelter on future animal adoptions</a></li>
<li><a href="https://www.tandfonline.com/doi/abs/10.1080/10888705.2014.943836">What Do People Want? Factors People Consider When Acquiring Dogs, the Complexity of the Choices They Make, and Implications for Nonhuman Animal Relocation Programs</a></li>
<li><a href="https://elgatio.org/">El Gatio shelter</a></li>
</ul>
<p><em>Article Photo by <a href="https://elgatio.org/">El Gatio</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[An interactive and resource efficient gamified learning platform]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/05/13/An-interactive-and-resource-efficient-gamified-learning-platform</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/05/13/An-interactive-and-resource-efficient-gamified-learning-platform</guid>
            <pubDate>Sat, 13 May 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>This year's <a href="https://www.linkedin.com/showcase/monstarhacks">MonstarHacks</a> main theme was NGOs and 4 NGOs were selected to partner up with Monstarlab to provide real life challenges.</p>
<p>These 4 NGOs are working on 3 different topics:</p>
<ul>
<li>Children Rights</li>
<li>Health/Crisis</li>
<li>Animal Rescue</li>
</ul>
<p>For our project, we choose a Health/Crisis challenge, partnering with the <a href="https://www.asha-np.org/en">ASHA</a> NGO.</p>
<h2>Problem statement</h2>
<p>We believe there are good people on earth and we want to improve health literacy. <a href="https://www.asha-np.org/en">ASHA</a>, an NGO is conducting special lessons at middle schools to address this need. They provide learning sessions for first aid and basic medicine, as well as practical training sessions so that people can truly utilize these skills. However, the capacity of the lecturers limits the delivery of this education.</p>
<p>The NGO faces issues due to a lack of capacity of their lecturers. They are not able to send a teacher to each school or class, and pre-recorded video lessons won't work due to the lack of access to smart devices for some students.</p>
<h2>Why we choose this problem</h2>
<p>Our team <code>PartTimeHusters</code> decided to tackle this problem because it aims to address the critical issue of providing health education to middle school students in a scalable and accessible manner. It is crucial to provide students with the necessary knowledge and skills to make informed decisions about their health and emergency situations, and this challenge allows us to develop a creative and innovative solution to make this possible. We believe that our solution can effectively address ASHA's pain points while also providing an engaging and interactive learning experience for middle school students.</p>
<h3>The assumptions we are making about the challenge</h3>
<ul>
<li>The NGO is understaffed and under-resourced, both in-terms of teaching staff, and finances.</li>
<li>The location of the site is a very remote hilly area, and the transport communication is unpleasant.</li>
<li>School facilities don’t have consistent electricity and internet access.</li>
<li>Students do not have access to handheld devices. Teachers are not well educated on digital technologies.</li>
</ul>
<h2>The proposed solution</h2>
<p>To solve this organizational problem our team came up with the concept of <code>Lifesaver Academy - An interactive and resource efficient gamified learning platform</code>. We aimed to create an integrated passive learning system, incorporating a reinforcement rating mechanism that keeps students engaged. Our intention was to offer learning through games, utilizing advertising touch displays as interactive gaming consoles. These games would also be accessible on other platforms, such as mobile and PC.</p>
<p>Our approach addresses three main key challenges the NGO is facing and provides sustainable solutions:</p>
<p><strong>Challenge 1:</strong> Heavy dependency on the teacher/lecturer which hampers the delivery of education</p>
<p><strong>How our solution solves this challenge:</strong>
Our solution decouples the dependency on the teacher/lecturer. Students can have quality health education anytime at their school premises. The learning videos/slides will keep playing, seeking sudden attention from the nearby students. Also, they can go in front of the display to engage in a game when they have available time.</p>
<p><strong>Challenge 2:</strong>  Heavy dependence on tech infrastructure due to budget shortage</p>
<p><strong>How our solution solves this challenge:</strong>
This platform comes in the form of cheap advertise touch displays as interactive consoles. Although, our solution is platform-agnostic. We have built the web app version of the solution in a progressive manner hence it can be installed in a smartphone as well. We have also built an AR/VR prototype of the solution. Additionally, multiple students can interact with the app at once so the problem of device shortage is solved as well.
We are building the solution in such a way that it can also be used <strong>offline</strong>.</p>
<p><img src="/assets/img/articles/2023-05-13-An-interactive-and-resource-efficient-gamified-learning-platform.md/game_on_screen.webp" alt="Touch Display Monitor"></p>
<p><strong>Challenge 3:</strong> Sometimes static video lessons can be less interactive hence boring</p>
<p><strong>How our solution solves this challenge:</strong>
Scientific evidence supports the effectiveness of interactive methods for learning. Our gamification of the application will enable students to engage with the material, solidifying their understanding and preparing them to apply their knowledge in real-world scenarios.</p>
<p><strong>Challenge 4:</strong> Not all schools have electricity or internet access</p>
<p><strong>How our solution solves this challenge:</strong>
It is crucial to have electricity and internet for any digital learning platform to be usable. However, our proposed platform is able to run without the internet.</p>
<p><strong>Challenge 5:</strong> Language barriers</p>
<p><strong>How our solution solves this challenge:</strong>
Our solution supports multiple languages. Digital assets can be prepared and distributed in any localized form required.</p>
<h2>How does it work</h2>
<p>Traditional health education taught by a teacher is not sufficiently scalable, as the number of students a trained teacher can reach is limited. That's why we have decided to replace teachers with digital interactive stories. A school can implement our proposed learning platform, or even a laptop is enough for our solution to work. Students can explore the interactive stories on their own and learn basic health education. This approach is more engaging and interactive than receiving lessons from a teacher, resulting in better retention of each lesson for students.</p>
<p>Check out the gameplay video:</p>
<p><div class="embed-container">
      <iframe
          src="https://www.youtube.com/embed/5a93U3idU98"
          width="700"
          height="480"
          frameborder="0"
          allowfullscreen="">
      </iframe>
    </div>
    </p>
<h2>Our Tech stack</h2>
<ul>
<li><a href="https://gamemaker.io/en">GameMaker Language (GML)</a> - GameMaker is a software tool that allows users to create video games without the need for extensive programming knowledge. It includes a drag-and-drop interface for creating game elements and a scripting language called GameMaker Language (GML) for more advanced programming.</li>
<li><a href="https://www.figma.com/">Figma</a> - Intuitive tool for mocking and designing app flow.</li>
</ul>
<h3>Tools that we used to develop our game</h3>
<ul>
<li><a href="https://gamemaker.io/en">GameMaker Studio 2</a> (GameMaker IDE)</li>
<li><a href="https://www.narakeet.com/">Narakeet</a> (Text to Speech converter)</li>
<li><a href="https://online-audio-converter.com/">online-audio-converter</a> (Mp3 audio converter)</li>
<li><a href="https://loom.com/">Loom</a> (Video maker)</li>
<li><a href="https://www.canva.com/">Canva</a> (Presentation maker)</li>
<li><a href="https://picresize.com/en/results">picresize</a> (Image processor)</li>
<li><a href="https://www.opera.com/gx">opera.com/gx</a> (Game browser for demotime tryout, not needed for release)</li>
<li><a href="https://gx.games/">gx.games</a> (Game hosting platform)</li>
<li><a href="https://www.photopea.com/">Photopea</a> (Image editor)</li>
</ul>
<h3>Links to our Game and VR Design Prototype</h3>
<ul>
<li>💻 Demo Implementation: <a href="https://gx.games/games/j52x30/life-savers-academy/tracks/f34c2945-2af6-4be9-9dd1-b1a24eaf1a5b/">Game is published and can be played here</a></li>
<li>🎨 VR Design Prototype: <a href="https://www.figma.com/file/hbC4BLwbDVs1tkTY38GuhY/VR?type=design&#x26;node-id=0-1&#x26;t=bIJ6PN3oWGKmVGD3-0">VR Prototype Figma file </a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[An overview of the Little Lifelines application]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/05/13/An-overview-of-the-little-lifelines-application</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/05/13/An-overview-of-the-little-lifelines-application</guid>
            <pubDate>Sat, 13 May 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>For the past 2 years, <strong>Monstarlab</strong> has been hosting a hackathon for all its employees from every branch worldwide. This marked the third consecutive year of the event. We participated this year as Team 5 (Hacktivists). The topic this year was particularly exciting. We collaborated with four international NGOs, who presented us with real-world problem scenarios. Our task was to develop digital solutions based on the information provided by these organizations.</p>
<h2>A small introduction of our team</h2>
<p>We participated as Team 5 in this hackathon event, dubbed <strong>The Hacktivists</strong>. Our team consisted of 5 dynamic, diverse, and diligent people, all from the Dhaka office. So there was Tanvir Tasif Ahmed (myself), Susmita Saha, Md. Ariful Alam, Arun Kundu, and Zulfikar Ali Zihan. Our team was a blend of HR, front-end, and back-end professionals. We had great fun working together!</p>
<h2>Which problem we chose and why?</h2>
<p>We discussed and debated all the problems provided in this Hackathon. However, we did make up our minds and chose to solve the problem of one of the children's rights NGOs known as <a href="https://ekmattra.org/">Ekmattra</a> based in Bangladesh. Ekmattra's problem statement was:</p>
<p>"Ekmattra rescues abandoned children or children who fled from their villages to Dhaka and provides fore linkage support for those children by sending them to a school or NGOs who can provide them support to live and study.
The challenge is to determine which organization or school is appropriate for the rescued children living on the streets. The matchmaking process is extremely difficult and often relies on impromptu decisions and connections within a narrow network, leaving doubts about whether the choices made are truly the best for the children.
We currently have 13 partner organizations available to accommodate the rescued children. However, we need updated information on their capacity for children's placement. Additionally, we are seeking opportunities to connect with organizations and schools that cater to children's interests. Our goal is to place the children in organizations that can better support their interests, such as vocational institutes for those interested in mechanical work. Ultimately, our aim is to make connections and manage placements that accelerate the children's lives for a better future.
When it comes to matchmaking, it's important to consider both the child's interests and the availability of organizations. We aim to find organizations, institutions, or schools that align with the child's interests and have opportunities available that match their skills and future plans. For instance, we would look for coding camps or classes for a child interested in computer programming.
We also take into account the child's age and experience level, as some organizations may have specific requirements. Moreover, their schedule and location should be considered when matching them with an organization or school.
Effective communication with potential partners is crucial to determine if there is a good fit and arrange the logistics of the partnership."</p>
<p>Now why did we choose this problem?</p>
<p>Because:</p>
<ul>
<li>As we are a team from Bangladesh, we possess a deeper understanding of the challenges faced by Ekmattra and can propose a solution that is both feasible and viable within the Bangladeshi context.</li>
<li>Addressing this issue will have a major impact on society and we may have the opportunity to have it adopted on a broader scale in the future.</li>
<li>Solving this problem can save a generation, providing them with a better and more secure future, and enabling them to contribute positively to society in the years to come.</li>
<li>This project offers us the opportunity to suggest a simple solution or re-think existing ones with a new approach that aligns with current technology.</li>
<li>Tackling this problem can have a major impact on the following Sustainable Development Goals (SDGs): (a) Zero Hunger (b) Good health and Well-being and (c) Decent Work and Economic Growth.</li>
</ul>
<h2>Our approach in tackling this problem</h2>
<p>After reviewing the problem and focusing on key points we made a list of tasks that will help us accomplish the ultimate goal.</p>
<p>So we need to provide a platform that would:</p>
<ul>
<li>Aid Ekmattra in matching children's skills, interests, and locations with the most suitable schools and NGOs</li>
<li>Assist them in having updated information about the organizations they have partnered with to understand the availability and capacity for children’s placements</li>
<li>Ensure effective communication with the potential partners</li>
</ul>
<p>By achieving these objectives, we recognized that we would be able to provide Ekmattra with the solution they need.</p>
<h2>An overview of our solution</h2>
<p>We considered making an app to provide a digital solution for their problem, and we called our app "Little Lifelines."</p>
<p>Our solution will work the following way:
We would have all the necessary information about our partner organizations beforehand in our portal, we can then add any children’s profile based on their interests and location. We will simply put an algorithm where it matches the children with respective organizations based on, age-range, location, interests, and availability. The doubt of proper placement will be eliminated for Ekmattra because Ekmattra will know exactly how this process is working and can tweak the algorithm accordingly. It also allows Ekmattra to take prompt decisions because of the real-time data network we are aiming to provide.</p>
<p>Basically, our vision was to connect children with opportunities through technology.
We divided the solution into multiple phases.</p>
<h3>Phase 1</h3>
<p>The first phase of this solution is actually about the placement algorithm, a step-by-step guide on how an Ekmattra employee can use our platform to place a child by filling up the necessary information. They can also tweak this algorithm as they see fit.</p>
<p><img src="/assets/img/articles/2023-05-13-An-overview-of-the-little-lifelines-application/Phase-1-Image.webp" alt="">
<img src="/assets/img/articles/2023-05-13-An-overview-of-the-little-lifelines-application/add-child-info-image.webp" alt="">
<img src="/assets/img/articles/2023-05-13-An-overview-of-the-little-lifelines-application/placement-algorithm-image.webp" alt="">
<img src="/assets/img/articles/2023-05-13-An-overview-of-the-little-lifelines-application/Tweak-Algorithm-Image.webp" alt=""></p>
<h3>Phase 2:</h3>
<p>In phase two, we developed the interface for NGOs and schools, and their response system for handling requests submitted by Ekmattra. Both the partner organizations and Ekmattra will use the same portal for these procedures.</p>
<p><img src="/assets/img/articles/2023-05-13-An-overview-of-the-little-lifelines-application/phase-2-image.webp" alt=""></p>
<h3>Phase 3:</h3>
<p>A critical aspect of our solution is obtaining feedback from the children placed by Ekmattra and the NGOs responsible for their care. This feedback phase ensures safety, well-being, and trust among children, organizations, and Ekmattra itself.</p>
<p><img src="/assets/img/articles/2023-05-13-An-overview-of-the-little-lifelines-application/phase-3-image.webp" alt="">
<img src="/assets/img/articles/2023-05-13-An-overview-of-the-little-lifelines-application/child-feedback-forms-image.webp" alt=""></p>
<h3>Phase 4:</h3>
<p>Onboarding partner organizations on Ekmattra's portal to create a real-time data network of all the organizations.</p>
<p><img src="/assets/img/articles/2023-05-13-An-overview-of-the-little-lifelines-application/become-a-partner-image.webp" alt="">
<img src="/assets/img/articles/2023-05-13-An-overview-of-the-little-lifelines-application/partner-onboarding-image.webp" alt=""></p>
<h3>Phase 5:</h3>
<p>We understand that Ekmattra's work is challenging and requires as much support as possible. Therefore, we introduced a volunteering program for Ekmattra with a straightforward feature designed to increase the number of helping hands available to provide care.</p>
<p><img src="/assets/img/articles/2023-05-13-An-overview-of-the-little-lifelines-application/become-a-volunteer-image.webp" alt="">
<img src="/assets/img/articles/2023-05-13-An-overview-of-the-little-lifelines-application/phase-5-image.webp" alt="">
<img src="/assets/img/articles/2023-05-13-An-overview-of-the-little-lifelines-application/volunteer-program-image.webp" alt=""></p>
<h2>Wrapping up the hackathon weekend</h2>
<p>After a 3 days marathon with late nights, we pulled out a demo app with all the steps of the solution we could manage. Those 3 days were truly enjoyable for us. We had 2 other teams from our office who were competing as well. We had a lot of fun hanging out together, teasing each other, and even had a pizza party! Thanks to Team 7. Instead of feeling nervous about the results, we concluded our hackathon experience with sheer eagerness to learn more, learned to collaborate more outside our usual circles, gained valuable knowledge, and had a memorable experience etched in our hearts. We thank everyone who organized this hackathon and hope to return stronger than ever! Till then, Adios Amigos!</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/iDCtsz-INHI">Ben Wicks</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ChatGPT meets Flutter: A step by step integration guide]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/05/13/ChatGPT-meets-Flutter-A-step-by-step-integration-guide</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/05/13/ChatGPT-meets-Flutter-A-step-by-step-integration-guide</guid>
            <pubDate>Sat, 13 May 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>ChatGPT is a large language model that was trained by <a href="https://openai.com/">OpenAI</a>, an artificial intelligence research laboratory consisting of the for-profit corporation OpenAI LP and its parent company, the non-profit OpenAI Inc. OpenAI was founded in 2015 with the goal of advancing digital intelligence in a way that benefits humanity as a whole. They focus on developing cutting-edge AI technologies, conducting research, and promoting responsible use and safe deployment of AI. One of OpenAI's most famous achievements is the development of the GPT (Generative Pre-trained Transformer) language model series, of which ChatGPT is a part. ChatGPT is designed to be conversational and able to understand and generate natural language responses to a wide range of topics.</p>
<p>By integrating ChatGPT into your app, you can provide your users with an interactive chatbot that can understand and respond to their questions and requests in a human-like manner.</p>
<p>This tutorial will cover the basics of ChatGPT, including how it works and how to access it using OpenAI's API. It will also cover the steps required to integrate ChatGPT into a Flutter app, including setting up the necessary dependencies, creating a chatbot interface, and handling user input and responses. Overall, this will provide a valuable resource for developers looking to add powerful natural language processing capabilities to their Flutter apps.</p>
<h3>Model being used</h3>
<p><code>Text-Davinci-002</code> is one of the language models developed by OpenAI, and is a variant of the GPT-3 model. It has been trained on an enormous amount of text data, allowing it to generate human-like responses to a wide variety of prompts. <code>Text-Davinci-002</code> is a larger model than its predecessor, <code>Text-Curie</code>, and is capable of generating more sophisticated and coherent responses. It is currently only available through OpenAI's API, which allows developers to access its capabilities through a simple HTTP request.</p>
<h2>Steps to integrate the API</h2>
<p>To integrate the ChatGPT API into a Flutter app, follow these steps:</p>
<ul>
<li><strong>Get an API key:</strong> An API key will be required to access the ChatGPT API. You can sign up for an API key on the ChatGPT website.</li>
<li><strong>Integrate the backend server with the Flutter app:</strong> You can use Flutter's HTTP client to make API requests to the backend server and receive responses.</li>
<li><strong>Display responses in the chat UI:</strong> Once the response is received from the ChatGPT API, we will display it in the chat UI of the Flutter app.</li>
</ul>
<h3>Get the key</h3>
<p>Signup to get an API key from the website:
<a href="https://platform.openai.com/account/api-keys">Get your API key here</a></p>
<p><img src="/assets/img/articles/2023-05-13-ChatGPT-meets-Flutter-A-step-by-step-integration-guide/api_key.webp" alt=""></p>
<h2>Handling the Request/Response of the model</h2>
<p>Let's start with creating a repository to handle the API responses.</p>
<h3>Chat Repository</h3>
<pre><code class="hljs language-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:convert'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:cgp/repository/response.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:http/http.dart'</span> <span class="hljs-keyword">as</span> http;
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ChatRepository</span> </span>{
  Future&#x3C;Response?> makeRequest({<span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> question}) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> url = <span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'https://api.openai.com/v1/completions'</span>);
    <span class="hljs-keyword">final</span> headers = {
      <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
      <span class="hljs-string">'Authorization'</span>: <span class="hljs-string">'Bearer API-Token'</span>,
    };
    <span class="hljs-keyword">final</span> data = {
      <span class="hljs-string">'prompt'</span>: question,
      <span class="hljs-string">'model'</span>: <span class="hljs-string">'text-davinci-002'</span>,
      <span class="hljs-string">'temperature'</span>: <span class="hljs-number">0.5</span>,
      <span class="hljs-string">'max_tokens'</span>: <span class="hljs-number">50</span>,
    };

    <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.post(
        url, headers: headers, body: jsonEncode(data));
    <span class="hljs-keyword">if</span> (response.statusCode == <span class="hljs-number">200</span>) {
      <span class="hljs-keyword">final</span> jsonData = jsonDecode(response.body);
      <span class="hljs-keyword">final</span> text = jsonData[<span class="hljs-string">'choices'</span>][<span class="hljs-number">0</span>][<span class="hljs-string">'text'</span>];
      <span class="hljs-keyword">return</span> Response.success(text);
      <span class="hljs-comment">// handle the success response</span>
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">final</span> error = jsonDecode(response.body);
      <span class="hljs-keyword">final</span> errorMessage = error[<span class="hljs-string">'error'</span>][<span class="hljs-string">'message'</span>];
      <span class="hljs-keyword">return</span>  Response.error(errorMessage);
      <span class="hljs-comment">// handle the error response</span>
    }
  }
}
</code></pre>
<p>In this code, the OpenAI class is used to handle the <code>Text-Davinci-002</code> API of OpenAI. The constructor takes two arguments, apiKey and apiUrl, which are used to authenticate the API request and specify the API endpoint, respectively. Replace your API token in the above code.
The getResponse function takes a message argument, which is the text message to send to the API. The function sends a POST request to the API with the message as the prompt and additional parameters such as <code>temperature</code> and <code>max_tokens</code>. The model parameter is set to <code>text-davinci-002</code> to use the <code>Text-Davinci-002</code> model.
If the response status code is 200, the function decodes the response body using the jsonDecode function and returns the generated text from the response. If the response status code is not 200, the function throws an exception with the error message.</p>
<h3>Response class</h3>
<p>This is a <code>freezed</code> class that represents a response from an API with two possible subtypes: <code>SuccessResponse</code> and <code>ErrorResponse</code>. Here's what each part of the code does:</p>
<pre><code class="hljs language-dart"><span class="hljs-keyword">part</span> <span class="hljs-string">'response.freezed.dart'</span>;
<span class="hljs-keyword">part</span> <span class="hljs-string">'response.g.dart'</span>;
</code></pre>
<p>These two lines define the filenames for the generated code. <code>freezed</code> uses the <code>.freezed.dart</code> file to generate the <code>freezed</code> classes, and the <code>.g.dart</code> file to generate serialization/deserialization code.</p>
<pre><code class="hljs language-dart"><span class="hljs-meta">@freezed</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Response</span> <span class="hljs-title">with</span> <span class="hljs-title">_</span>$<span class="hljs-title">Response</span> </span>{}
</code></pre>
<p>This line defines the Response class as a <code>freezed</code> class with the <code>$Response mixin(Success and Error)</code>. The mixin provides useful methods like copyWith, toString, and operator ==.</p>
<pre><code class="hljs language-dart"><span class="hljs-keyword">factory</span> Response.success(<span class="hljs-built_in">String</span> text) = SuccessResponse;
<span class="hljs-keyword">factory</span> Response.error(<span class="hljs-built_in">String</span> message) = ErrorResponse;
</code></pre>
<p>This factory method deserializes a JSON object into a Response object. It uses the generated <code>_$ResponseFromJson</code> function from the <code>.g.dart</code> file to deserialize the JSON.
To use this <code>Response</code> class, you can create instances of <code>SuccessResponse</code> or <code>ErrorResponse</code> based on the API response, and then use them in your code.</p>
<p>The class will look like the following:</p>
<pre><code class="hljs language-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:freezed_annotation/freezed_annotation.dart'</span>;

<span class="hljs-keyword">part</span> <span class="hljs-string">'response.freezed.dart'</span>;
<span class="hljs-keyword">part</span> <span class="hljs-string">'response.g.dart'</span>;

<span class="hljs-meta">@freezed</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Response</span> <span class="hljs-title">with</span> <span class="hljs-title">_</span>$<span class="hljs-title">Response</span> </span>{
 <span class="hljs-keyword">factory</span> Response.success(<span class="hljs-built_in">String</span> text) = SuccessResponse;
 <span class="hljs-keyword">factory</span> Response.error(<span class="hljs-built_in">String</span> message) = ErrorResponse;

 <span class="hljs-keyword">factory</span> Response.fromJson(<span class="hljs-built_in">Map</span>&#x3C;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>> json) =>
     _$ResponseFromJson(json);
}
</code></pre>
<h2>Chat Page View Model</h2>
<p>To handle the logic between the UI and repository we will create a chat page view model class.</p>
<h3>Chat Page State Handling</h3>
<pre><code class="hljs language-dart"><span class="hljs-meta">@freezed</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ChatPageState</span> <span class="hljs-title">with</span> <span class="hljs-title">_</span>$<span class="hljs-title">ChatPageState</span> </span>{
  <span class="hljs-keyword">factory</span> ChatPageState({
    <span class="hljs-meta">@Default</span>([]) <span class="hljs-built_in">List</span>&#x3C;Message> messages,
    <span class="hljs-built_in">String?</span> question,
    <span class="hljs-meta">@Default</span>(<span class="hljs-keyword">true</span>) loading,
    <span class="hljs-built_in">String?</span> errorMessage,
  }) = _ChatPageState;
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Message</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> text;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">bool</span> isUserText;

  Message({
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.text,
    <span class="hljs-keyword">this</span>.isUserText = <span class="hljs-keyword">true</span>,
  });
}
</code></pre>
<p>The <code>Message</code> class has two properties:</p>
<ul>
<li><code>text</code>: a required string representing the message's content.</li>
<li><code>isUserText</code>: a boolean value indicating whether the message was sent by the user or received from the AI. The default value for this property is true, meaning that if it is not specified when creating a new <code>Message</code> object, it will be assumed that the message was sent by the user.</li>
</ul>
<p>The <code>ChatPageState</code> class uses the <code>freezed</code> package to generate immutable classes. It has four properties:</p>
<ul>
<li><code>messages</code>: a list of <code>Message</code> objects representing the chat history. The default value for this property is an empty list.</li>
<li><code>question</code>: a string representing the current question or prompt sent by the user.</li>
<li><code>loading</code>: a boolean value indicating the timing of API request.</li>
<li><code>errorMessage</code>: a string representing any error message that may have occurred while loading the chat page.</li>
</ul>
<p>The <code>ChatPageState</code> class has a named constructor called factory that takes in named parameters for each of its properties. It then passes these values to the private <code>_ChatPageState</code> constructor, which is generated by the <code>freezed</code> package. This constructor creates an immutable instance of the <code>ChatPageState</code> class.</p>
<h3>View Model</h3>
<p>This is a <code>StateNotifier</code> class named <code>ChatPageViewModel</code> that manages the state of a chat page. Here's what each part of the code does:</p>
<pre><code class="hljs language-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ChatPageViewModel</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StateNotifier</span>&#x3C;<span class="hljs-title">ChatPageState</span>> </span>{
  ChatPageViewModel({<span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.chatRepository}) : <span class="hljs-keyword">super</span>(ChatPageState());

  <span class="hljs-keyword">final</span> ChatRepository chatRepository;
}
</code></pre>
<p>This is the class definition for the <code>ChatPageViewModel</code>. It extends <code>StateNotifier</code> and manages a state of type <code>ChatPageState</code>. It also takes a <code>chatRepository</code> as a required parameter, which is an instance of a repository that communicates with the API.</p>
<pre><code class="hljs language-dart">Future&#x3C;<span class="hljs-keyword">void</span>> askQuestion({<span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> question}) <span class="hljs-keyword">async</span> {
<span class="hljs-keyword">final</span> userQuestion = Message(text: question, isUserText: <span class="hljs-keyword">true</span>);
state = state.copyWith(
loading: <span class="hljs-keyword">true</span>, messages: <span class="hljs-built_in">List</span>.from(state.messages)..add(userQuestion));
<span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> chatRepository.makeRequest(question: question);
response!.when(success: (response) {
  <span class="hljs-keyword">final</span> responseMsg = Message(text: response, isUserText: <span class="hljs-keyword">false</span>);
  <span class="hljs-built_in">List</span>&#x3C;Message> messages = <span class="hljs-built_in">List</span>.from(state.messages)..add(responseMsg);

  state = state.copyWith(messages: messages);
}, error: (errorMessage) {
  state = state.copyWith(errorMessage: errorMessage);
});
state = state.copyWith(loading: <span class="hljs-keyword">false</span>);
}
</code></pre>
<p>This method is called when the user sends a question. It creates a <code>Message</code> object representing the user's question, sets the loading state to true during the time of API request, and adds the user's question to the state's messages list.</p>
<p>A request is sent to the API with the user's question and waits for a response. If the response is successful, it creates a <code>Message</code> object representing the response and adds it to the state's messages list. If there's an error, it sets the state's error message. Finally, it sets the loading state to false.</p>
<p>Overall, this <code>ChatPageViewModel</code> manages the state of a chat page and communicates with an API to provide responses to user questions.</p>
<h2>Chat Page UI</h2>
<p>Lets make the Chat page UI.</p>
<p>The UI will consist of a basic chat page with two main widget components:</p>
<ul>
<li>Chat bubble</li>
<li>Message text box to compose the text message</li>
</ul>
<h3>Chat bubble</h3>
<pre><code class="hljs language-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ChatBubble</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> message;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">bool</span> isSentByMe;

  <span class="hljs-keyword">const</span> ChatBubble({
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.message,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.isSentByMe,
    <span class="hljs-keyword">super</span>.key,
  });

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Container(
      alignment: isSentByMe ? Alignment.centerRight : Alignment.centerLeft,
      child: Container(
        padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">10</span>),
        margin: <span class="hljs-keyword">const</span> EdgeInsets.symmetric(vertical: <span class="hljs-number">5</span>),
        decoration: BoxDecoration(
          color: isSentByMe ? Colors.blue : Colors.grey[<span class="hljs-number">300</span>],
          borderRadius: BorderRadius.only(
            topLeft: Radius.circular(<span class="hljs-number">20</span>),
            topRight: Radius.circular(<span class="hljs-number">20</span>),
            bottomLeft: isSentByMe ? Radius.circular(<span class="hljs-number">20</span>) : Radius.circular(<span class="hljs-number">0</span>),
            bottomRight: isSentByMe ? Radius.circular(<span class="hljs-number">0</span>) : Radius.circular(<span class="hljs-number">20</span>),
          ),
        ),
        child: Text(
          message,
          style: TextStyle(
            color: isSentByMe ? Colors.white : Colors.black,
          ),
        ),
      ),
    );
  }
}
</code></pre>
<p>In this widget, the <code>message</code> argument is the text message to display, the <code>isMe</code> argument is a boolean value that indicates whether the message is sent by the current user or the other user in the chat, and the <code>key</code> argument is an optional key for this widget.
The message bubble is displayed as a row with a Container widget that has a colored background and rounded borders on the appropriate corners based on the <code>isMe</code> value. The message text is displayed inside the Container widget with a Text widget. The width, padding, and margin properties of the Container widget are set to provide the appropriate spacing and layout for the message bubble.</p>
<h3>Message text box</h3>
<pre><code class="hljs language-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MesageInputBox</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">Function</span>(<span class="hljs-built_in">String</span>) onSend;

  <span class="hljs-keyword">const</span> MesageInputBox({Key? key, <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.onSend}) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-meta">@override</span>
  State&#x3C;MesageInputBox> createState() => _MesageInputBoxState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_MesageInputBoxState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&#x3C;<span class="hljs-title">MesageInputBox</span>> </span>{
  <span class="hljs-keyword">final</span> TextEditingController _textController = TextEditingController();

  <span class="hljs-built_in">bool</span> _isComposing = <span class="hljs-keyword">false</span>;

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Container(
      padding: <span class="hljs-keyword">const</span> EdgeInsets.symmetric(horizontal: <span class="hljs-number">8.0</span>),
      child: Row(
        children: [
          Expanded(
            child: TextField(
              controller: _textController,
              decoration: <span class="hljs-keyword">const</span> InputDecoration.collapsed(
                hintText: <span class="hljs-string">'Type a message'</span>,
              ),
              onChanged: (<span class="hljs-built_in">String</span> text) {
                setState(() {
                  _isComposing = text.isNotEmpty;
                });
              },
              onSubmitted: _handleSubmit,
            ),
          ),
          IconButton(
            icon: <span class="hljs-keyword">const</span> Icon(Icons.send),
            onPressed:
            _isComposing ? () => _handleSubmit(_textController.text) : <span class="hljs-keyword">null</span>,
          ),
        ],
      ),
    );
  }

  <span class="hljs-keyword">void</span> _handleSubmit(<span class="hljs-built_in">String</span> text) {
    <span class="hljs-keyword">if</span> (text.trim().isEmpty) <span class="hljs-keyword">return</span>;

    widget.onSend(text);
    _textController.clear();

    setState(() {
      _isComposing = <span class="hljs-keyword">false</span>;
    });
  }
}
</code></pre>
<p>In this widget, the <code>sendMessage</code> argument is a function that takes a string as input and sends a message to the chat. The <code>_textController</code> is used to control the state of the text input field.
The message composer is displayed as a Container widget with a padding of 8 on all sides. Inside the Container, there is a Row widget that contains an Expanded widget, which is used to take up all the available space for the text input field. The TextField widget is used for the text input field and has a controller that is set to <code>_textController</code>. The TextField also has a label and border decoration. The <code>textCapitalization</code> property is set to <code>TextCapitalization.sentences</code> to capitalize the first letter of each sentence automatically. The <code>onSubmitted</code> property is set to <code>_sendMessage</code>, which is called when the user presses the enter key or submits the text input.
Next to the text input field, there is an IconButton widget that displays a send icon. When the user taps on this button, the <code>_sendMessage</code> function is called, which sends the text input to the chat and clears the text input field.</p>
<h3>Chat Page</h3>
<pre><code class="hljs language-dart"><span class="hljs-comment">// chat_page.dart</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">'package:cgp/repository/chat_repository.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:cgp/ui/chat_page_view_model.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:cgp/widgets/message_bubble.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter_riverpod/flutter_riverpod.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:get_it/get_it.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../widgets/message_composer.dart'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'chat_page_state.dart'</span>;

GetIt injector = GetIt.instance;
<span class="hljs-keyword">final</span> chatProvider =
StateNotifierProvider.autoDispose&#x3C;ChatPageViewModel, ChatPageState>(
        (ref) => ChatPageViewModel(
      chatRepository: injector.<span class="hljs-keyword">get</span>&#x3C;ChatRepository>(),
    ));

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ChatPage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ConsumerStatefulWidget</span> </span>{
  <span class="hljs-keyword">const</span> ChatPage({Key? key}) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-meta">@override</span>
  ConsumerState&#x3C;ChatPage> createState() => _ChatPageState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_ChatPageState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ConsumerState</span>&#x3C;<span class="hljs-title">ChatPage</span>> </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">final</span> state = ref.watch(chatProvider);
    <span class="hljs-keyword">final</span> viewModel = ref.read(chatProvider.notifier);

    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(
        title: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'ChatGPT Flutter App'</span>),
      ),
      body: Padding(
        padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">16</span>),
        child: Column(
          children: [
            Container(),
            Expanded(
              child: ListView(
                children: [
                  ...state.messages.map(
                        (msg) => ChatBubble(
                      message: msg.text,
                      isSentByMe: msg.isUserText,
                    ),
                  ),
                ],
              ),
            ),
            MessageInputBox(
              onSend: (question) <span class="hljs-keyword">async</span> {
                <span class="hljs-keyword">await</span> viewModel.askQuestion(question: question);
              },
            ),
          ],
        ),
      ),
    );
  }
}
</code></pre>
<p>This is a <code>StatefulWidget</code> class named <code>ChatPage</code> that builds a chat interface for the user.
This code block returns a Scaffold widget that contains the chat interface. The <code>AppBar</code> displays the title of the app. The body contains a Column widget with two children:</p>
<ul>
<li>An <code>Expanded</code> widget containing a <code>ListView</code> with children generated from the messages in the <code>ChatPageState</code>. Each message is displayed using a <code>ChatBubble</code> widget, which takes the message text and whether the message was sent by the user as arguments.</li>
<li>A <code>MessageInput</code> widget with an <code>onSend</code> callback function that calls the <code>askQuestion</code> method of the <code>ChatPageViewModel</code>.</li>
</ul>
<p>Overall, this <code>ChatPage</code> class creates a chat interface that displays messages and sends user questions to the <code>ChatPageViewModel</code> for processing.</p>
<p>Run the app, it would finally look like the below:
<img src="/assets/img/articles/2023-05-13-ChatGPT-meets-Flutter-A-step-by-step-integration-guide/chat_page.webp" alt=""></p>
<p>The entire code can be found in the below repository:
<a href="https://github.com/huma11farheen/chatgpt_flutter_app">GithubRepo</a></p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/Fc1GBkmV-Dw">D koi</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Monstarhacks 2023 - An amazing remote hackathon experience]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/05/12/An-amazing-remote-hackathon-experience</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/05/12/An-amazing-remote-hackathon-experience</guid>
            <pubDate>Fri, 12 May 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Participating in <a href="https://www.linkedin.com/showcase/monstarhacks">MonstarHacks</a>, Monstarlab's internal hackathon, was truly an amazing experience. The event brought together a diverse group of individuals, each with their own unique set of skills and backgrounds from different countries. For my team, all of us are first-time hackathon participants and our goal was to develop innovative solutions within a tight timeframe which can help and contribute to the society. In this article, I'll share my insights and takeaways from my time at MonstarHacks and how my team worked together to tackle the unique challenges we faced and the innovative solutions we developed as a team.</p>
<h2>Our amazing team</h2>
<p>![team4-monstarhack](/assets/img/articles/2023-05-12-An-amazing-remote-hackathon-experience/team.webp</p>
<p>My talented members of our hackathon team, HackWarriors consists of five members, all with unique skills and experiences.</p>
<ul>
<li>Balakumar, our Senior Mobile Developer, had an extensive background in mobile app development. Thanks to his expertise, the prototype was completed within the designated timeline.</li>
<li>Vinoth, our Tech Lead, contributed a wealth of technical knowledge to the team. We knew we could depend on him to address any technical challenges that arose.</li>
<li>Jean, our Project Manager, showcased exceptional organizational skills, instilling confidence that we could achieve our goals and meet deadlines.</li>
<li>Ritche, our UX/UI Designer, was based in the Philippines but remained a vital team member. He offered a fresh perspective and valuable design experience.</li>
<li>Lastly, I, Swee Kwang, served as the team's iOS Developer. Leveraging my experience in iOS app development, I was eager to contribute to the team and help create a successful project.</li>
</ul>
<p>Each team member was located in Singapore, with the exception of Ritche, who joined us from the Philippines. We were excited to work together as a team and to tackle the challenges ahead of us.</p>
<h2>Brainstorming and topic selection</h2>
<p>During our morning call on Google Meet, my team and I discussed potential topics for the four challenges. Following extensive brainstorming, we decided to focus on the challenge presented by the partnering NGO <a href="https://elgatio.org/">El Gatio</a>, of simplifying the cat adoption process and facilitating adoptions - <strong>Match Made in HEAVEN; Streamlining Cat Foster and Adoption Matching</strong>.</p>
<p>We used Miro to create quick idea flow and identify the necessary features for our app, which Ritche then used as a foundation for the design.</p>
<p>Additionally, we have scheduled a call with our mentor, Takuya Yamaguchi, to discuss our idea and receive guidance on any additional considerations we should take into account.</p>
<p>![brainstorming-with-miro](/assets/img/articles/2023-05-12-An-amazing-remote-hackathon-experience/miro.webp</p>
<p>![team-mentor](/assets/img/articles/2023-05-12-An-amazing-remote-hackathon-experience/team-mentor.webp</p>
<h2>Our working process</h2>
<p>Our diverse team had only three days to create a prototype, design the app, and prepare presentation materials. To efficiently manage our time, we split tasks into four areas: designing the prototype, creating the prototype, preparing presentation slides, and preparing the app demo video.</p>
<p>As soon as several screen designs were finished, the developers — Balakumar, Vinoth, and myself — started working on the prototype to ensure we stayed within our time constraints. We decided to use Flutter to create our prototype so that we could create both iOS and Android platforms simultaneously. If there were any uncertainties during the development process, we used Slack for communication.</p>
<p>Our project manager, Jean, took charge of creating the presentation slides and a document to showcase our app to the judges. She searched and selected relevant statistics and information to explain our app's features and benefits. Our technical lead, Vinoth, worked closely with her to ensure that the presentation was informative and effective.</p>
<p>Creating an engaging app demo video was also a crucial part of our presentation strategy. To achieve the desired effect, we decided to follow the style of Apple Support's YouTube videos as a form of inspiration for keeping it simple yet effective. Working alongside the project manager, who developed the flow of the app demo, I recorded the app and took care of the video editing.</p>
<p>We also received valuable feedback on our ideas from Andrea Giraldo, one of the volunteers for the El Gatio organization.</p>
<h3>Management</h3>
<p>Throughout the hackathon, we had a project manager who was responsible for overseeing the project and ensuring that everyone was working towards our common goal. The project manager conducted daily stand-up meetings to check in on the team's progress.</p>
<p>![team-mentor](/assets/img/articles/2023-05-12-An-amazing-remote-hackathon-experience/design.webp</p>
<p>Working together in a remote hackathon was both a challenging and rewarding experience, but by splitting our tasks into design, prototype, slides, documentation, and management, we were able to build a meaningful yet innovative cat fostering app in just one weekend, which was our greatest accomplishment till date.</p>
<p>Each team member had a specific role to play, and we all worked together towards our common goal. Despite the distance between us, we were able to collaborate effectively and deliver a great product.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Full stack training for frontend engineers with Serverless Framework]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/05/08/From-frontend-to-fullstack</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/05/08/From-frontend-to-fullstack</guid>
            <pubDate>Mon, 08 May 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<!-- summary -->
<p>As a frontend engineer, learning to become a full stack engineer can seem like a daunting task. However, with the right tools and knowledge, it's possible to build scalable, production-ready web applications that incorporate both frontend and backend logic and functionality. This article will explore how to use Universal JavaScript tech stacks, with the Serverless Framework for Infrastructure and API Functions, and GitHub Actions for CI/CD to build full stack applications.</p>
<hr>
<h2>Introduction</h2>
<p>This article is based on an actual case of a successfully released project. From personal experience, that project was a great training for a frontend engineer. It provides the opportunity to expand their skills from developing single-page applications to developing APIs, cloud infrastructure, and DevOps, among other areas. Let's look at the key aspects that a frontend engineer could start with for the path of the full stack.</p>
<h2>Learn Universal JavaScript/Typescript tech stacks</h2>
<p>![Javascript](/assets/img/articles/2023-05-15-From-frontend-to-fullstack/javascript.webp</p>
<p>One of the keys to building full stack applications as a frontend engineer is to use a Universal JavaScript or Typescript tech stack. Universal JavaScript is a paradigm that enables developers to write JavaScript code that can run on both the client and the server side. This approach allows frontend engineers to begin writing server-side code without having to learn a new language. Moreover, since both sides are written by the same team, which can reduce many unnecessary communications and make development faster and more efficient. Some popular Universal JavaScript tech stacks include React, Node.js, and OpenAPI.</p>
<h2>Use Serverless Framework for Infrastructure and API Functions</h2>
<p>![Serverless](/assets/img/articles/2023-05-15-From-frontend-to-fullstack/serverless-title.webp</p>
<p>Once you have applied Universal JavaScript, the next step is to set up the infrastructure and incorporate server-side functionality into your application. A popular way to do this is to use Serverless Framework.</p>
<h3>About Serverless Framework</h3>
<p>The Serverless Framework is an open-source, developer-friendly framework for building and deploying serverless applications. It provides an easy-to-use, consistent, and vendor-agnostic way to define, deploy, and manage serverless functions and resources, making it easy for developers to build and run cloud-native applications.</p>
<p>Serverless Framework is based on the <strong>Infrastructure as Code (IaC)</strong> concept. Comparing it with other IaC services can help to understand its strengths.</p>



































<table><thead><tr><th></th><th>![Serverless](/assets/img/articles/2023-05-15-From-frontend-to-fullstack/serverless.webp</th><th>![Terraform](/assets/img/articles/2023-05-15-From-frontend-to-fullstack/terraform.webp</th><th>![CloudFormation](/assets/img/articles/2023-05-15-From-frontend-to-fullstack/cloudformation.webp</th></tr></thead><tbody><tr><td></td><td>Serverless</td><td>Terraform</td><td>CloudFormation</td></tr><tr><td>Infrastructure as Code</td><td>YAML/JSON config files</td><td>HCL/JSON config files</td><td>YAML/JSON config files</td></tr><tr><td>Platforms</td><td>AWS, Azure, Google Cloud, etc</td><td>AWS, Azure, Google Cloud, etc</td><td>AWS only</td></tr><tr><td>Focus</td><td><strong>All-in-one development solution</strong> for cloud-native applications</td><td>Provision, change, and version resources on <strong>ANY environment</strong>.</td><td>Provision, manage AWS and third-party resources</td></tr></tbody></table>
<h3>A Quick Sample of Serverless Framework</h3>
<p>Let’s take a look at a sample Lambda-based API created using the Serverless Framework. You can find more sample codes <a href="https://github.com/serverless/examples">here</a></p>
<p>As you can see from the sample code folder structure, a serverless project consists of two main parts: the application code and the configuration file for the infrastructure.
![Folder Structure](/assets/img/articles/2023-05-15-From-frontend-to-fullstack/sample-folder.webp</p>
<p>The <strong>handler.ts</strong> file creates an API that responds a simple message.</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">APIGatewayProxyEvent</span>, <span class="hljs-title class_">APIGatewayProxyResult</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"aws-lambda"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> hello = <span class="hljs-keyword">async</span> (
 <span class="hljs-attr">event</span>: <span class="hljs-title class_">APIGatewayProxyEvent</span>
): <span class="hljs-title class_">Promise</span>&#x3C;<span class="hljs-title class_">APIGatewayProxyResult</span>> => {
 <span class="hljs-keyword">return</span> {
   <span class="hljs-attr">statusCode</span>: <span class="hljs-number">200</span>,
   <span class="hljs-attr">body</span>: <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>(
     {
       <span class="hljs-attr">message</span>: <span class="hljs-string">"Hello Serverless!"</span>,
       <span class="hljs-attr">input</span>: event,
     },
     <span class="hljs-literal">null</span>,
     <span class="hljs-number">2</span>
   ),
 };
};
</code></pre>
<p>In the <strong>serverless.yml</strong> file, infrastructure can be defined simply by specifying the function and its related handler file. Other resources such as the API Gateway will be created automatically.</p>
<pre><code class="hljs language-yaml"><span class="hljs-attr">service:</span> <span class="hljs-string">serverless-http-api-typescript</span>
<span class="hljs-attr">frameworkVersion:</span> <span class="hljs-string">'3'</span>

<span class="hljs-attr">provider:</span>
 <span class="hljs-attr">name:</span> <span class="hljs-string">aws</span>
 <span class="hljs-attr">runtime:</span> <span class="hljs-string">nodejs14.x</span>

<span class="hljs-attr">functions:</span>
 <span class="hljs-attr">hello:</span>
   <span class="hljs-attr">handler:</span> <span class="hljs-string">handler.hello</span>
   <span class="hljs-attr">events:</span>
     <span class="hljs-bullet">-</span> <span class="hljs-attr">httpApi:</span>
         <span class="hljs-attr">path:</span> <span class="hljs-string">/</span>
         <span class="hljs-attr">method:</span> <span class="hljs-string">get</span>

<span class="hljs-attr">plugins:</span>
 <span class="hljs-bullet">-</span> <span class="hljs-string">serverless-plugin-typescript</span>
</code></pre>
<p>Then by a single magic command <strong><code>$serverless deploy</code></strong>, your API will be ready to use.</p>
<p>Plugins can also be used to enable additional cloud resources and functionality. In the above case, typescript plugin was added. In our real project, we added plugins for backend to use RDS, Data API, SQS, etc. For the frontend, since it's a <code>Sveltkit</code> based SPA, another plugin was used to deploy the built files to S3 and create a related Cloudfront distribution.</p>
<p>These features are extremely helpful to frontend engineers who may have limited knowledge of infrastructure and backend development, allowing us to begin full stack development with minimal prerequisites.</p>
<h3>Summary of Serverless Framework</h3>
<p>To summarize, these are the main features of Serverless Framework:</p>
<ul>
<li>
<p>Infrastructure as Code: The framework allows developers to define their infrastructure and resources as code, using a configuration file, which can be version controlled, tested, and easily shared among team members.</p>
</li>
<li>
<p>Easy Deployment: The framework makes it easy to deploy your serverless application with a single command, and automates the packaging, deployment, and resource management tasks.</p>
</li>
<li>
<p>Plugin Architecture: The framework provides a plugin architecture that allows developers to extend and customize the functionality of the framework, by adding new plugins, or creating their own.</p>
</li>
<li>
<p>Local Development: The framework enables developers to test and debug their serverless applications locally, using the Serverless Offline plugin.</p>
</li>
</ul>
<p>Overall, the Serverless Framework provides a great set of tools and features that make it easier for developers to build and deploy serverless applications, by providing a consistent and unified way to manage and deploy cloud resources.</p>
<h2>Set up GitHub Actions with Serverless Framework for CI/CD</h2>
<p>To ensure that your full stack applications are reliable and stable, it's important to use continuous integration and continuous deployment (CI/CD) practices. GitHub Actions is a popular CI/CD tool that integrates well with the Serverless Framework. With GitHub Actions and the Serverless Framework, you can set up automated testing, linting, and deployment processes that ensure your code is always up-to-date and running smoothly.</p>
<h3>Workflow for CI</h3>
<p>First, once a new PR was created, it will run jobs for lint and test. A <strong>static code analysis</strong> scan was also conducted in the end. Developers can then fix any detected issues even without requiring a manual code review, as all test and scan results will be updated in the PR conversation.</p>
<p><em>check.yml:</em></p>
<pre><code class="hljs language-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">status</span> <span class="hljs-string">check</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">types:</span> [<span class="hljs-string">opened</span>, <span class="hljs-string">synchronize</span>, <span class="hljs-string">reopened</span>]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">check:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">lint</span> <span class="hljs-string">and</span> <span class="hljs-string">check</span> <span class="hljs-string">App</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">environment:</span> <span class="hljs-string">develop</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">fetch-depth:</span> <span class="hljs-number">0</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">node-version:</span> <span class="hljs-number">16.</span><span class="hljs-string">x</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">Dependencies</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Make</span> <span class="hljs-string">envfile</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">SpicyPizza/create-envfile@v1.3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">envkey_VITE_EXAMPLE:</span> <span class="hljs-string">test</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">build</span>
        <span class="hljs-attr">env:</span>
          <span class="hljs-attr">CI:</span> <span class="hljs-literal">true</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">check</span> <span class="hljs-string">format</span> <span class="hljs-string">&#x26;</span> <span class="hljs-string">lint</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npx</span> <span class="hljs-string">lint-staged</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">test</span> <span class="hljs-string">and</span> <span class="hljs-string">coverage</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">coverage</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">SonarCloud</span> <span class="hljs-string">Scan</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">sonarsource/sonarcloud-github-action@master</span>
        <span class="hljs-attr">env:</span>
          <span class="hljs-attr">GITHUB_TOKEN:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.GITHUB_TOKEN</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">SONAR_TOKEN:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.SONAR_TOKEN</span> <span class="hljs-string">}}</span>
</code></pre>
<h3>Workflow for CD</h3>
<p>The deploy jobs run when a PR is merged to a target branch. Before and after the deployment, notifications are sent via Slack to inform about the status of the deployment. Environment variables, such as AWS and Slack keys, can be configured in the <strong>Environments</strong> of the GitHub Repo settings. Behind the deploy command <em><code>npm run deploy:staging</code></em> is the script defined in <strong>package.json</strong> :</p>
<pre><code class="hljs language-json"><span class="hljs-attr">"deploy:staging"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"serverless deploy --stage staging"</span>
</code></pre>
<p><em>deploy-staging.yml:</em></p>
<pre><code class="hljs language-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Staging</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">develop</span>

<span class="hljs-attr">concurrency:</span>
  <span class="hljs-attr">group:</span> <span class="hljs-string">staging</span>
  <span class="hljs-attr">cancel-in-progress:</span> <span class="hljs-literal">false</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">deploy:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">App</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">environment:</span> <span class="hljs-string">develop</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Post</span> <span class="hljs-string">to</span> <span class="hljs-string">a</span> <span class="hljs-string">Slack</span> <span class="hljs-string">channel</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">slack</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">slackapi/slack-github-action@v1.21.0</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">channel-id:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.SLACK_BOT_POST_CHANNEL</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">slack-message:</span> <span class="hljs-string">'FRONTEND STAGING RELEASE started'</span>
        <span class="hljs-attr">env:</span>
          <span class="hljs-attr">SLACK_BOT_TOKEN:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.SLACK_BOT_OAUTH_TOKEN</span> <span class="hljs-string">}}</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">node-version:</span> <span class="hljs-number">16.</span><span class="hljs-string">x</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">aws-actions/configure-aws-credentials@v1</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">aws-access-key-id:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.AWS_ACCESS_KEY_ID</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">aws-secret-access-key:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.AWS_SECRET_ACCESS_KEY</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">aws-region:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.AWS_REGION</span> <span class="hljs-string">}}</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">install</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Make</span> <span class="hljs-string">envfile</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">SpicyPizza/create-envfile@v1.3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">envkey_VITE_ENVIRONMENT:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.VITE_ENVIRONMENT</span> <span class="hljs-string">}}</span>
          <span class="hljs-string">...</span>
           <span class="hljs-comment"># .env variables here - name: deploy</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">deploy</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">deploy:staging</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Post</span> <span class="hljs-string">to</span> <span class="hljs-string">a</span> <span class="hljs-string">Slack</span> <span class="hljs-string">channel</span> <span class="hljs-bullet">-</span> <span class="hljs-string">success</span>
        <span class="hljs-attr">if:</span> <span class="hljs-string">success()</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">slack-success</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">slackapi/slack-github-action@v1.21.0</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">channel-id:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.SLACK_BOT_POST_CHANNEL</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">slack-message:</span> <span class="hljs-string">'FRONTEND STAGING RELEASE finished'</span>
        <span class="hljs-attr">env:</span>
          <span class="hljs-attr">SLACK_BOT_TOKEN:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.SLACK_BOT_OAUTH_TOKEN</span> <span class="hljs-string">}}</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Post</span> <span class="hljs-string">to</span> <span class="hljs-string">a</span> <span class="hljs-string">Slack</span> <span class="hljs-string">channel</span> <span class="hljs-bullet">-</span> <span class="hljs-string">failure</span>
        <span class="hljs-attr">if:</span> <span class="hljs-string">failure()</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">slack-failure</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">slackapi/slack-github-action@v1.21.0</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">channel-id:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.SLACK_BOT_POST_CHANNEL</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">slack-message:</span> <span class="hljs-string">'FRONTEND STAGING RELEASE failed'</span>
        <span class="hljs-attr">env:</span>
          <span class="hljs-attr">SLACK_BOT_TOKEN:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.SLACK_BOT_OAUTH_TOKEN</span> <span class="hljs-string">}}</span>

</code></pre>
<h2>Keep learning and practicing</h2>
<p>Becoming a full stack engineer is an ongoing process of learning and practice. Keep up with the latest trends and technologies in both front-end and back-end development, and always be looking for ways to improve your skills. Join online communities, attend conferences and meetups, and collaborate with other team members to stay up-to-date and continue to grow as a full stack engineer.</p>
<p>To truly master full stack development, you'll need to build more full stack projects. Start by creating small projects that allow you to practice integrating front-end and back-end code. As you gain confidence, you can move on to larger, more complex projects that require you to use a wider range of tools and skills.</p>
<p>In conclusion, applying Universal JavaScript, the Serverless Framework, and GitHub Actions can help to build scalable, cloud-native applications effectively. By mastering these tools and building full stack projects, you can become a more valuable and versatile member of any development team.</p>
<p><em>Article Photo by <a href="https://www.midjourney.com/">Midjourney</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to build your first Decentraland project]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/05/02/Decentraland-Building-your-first-Decentraland-project</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/05/02/Decentraland-Building-your-first-Decentraland-project</guid>
            <pubDate>Tue, 02 May 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>As the metaverse continues to gain popularity, Decentraland has emerged as a powerful platform and is an excellent tool to learn. This tutorial will guide you through the process of creating your first project on Decentraland.</p>
<p>Using Decentraland's Builder, it's simple for anyone to create scenes. However, if you’d like to add more complex interactions, you would need to use the Decentraland SDK. The Decentraland SDK is a powerful tool that lets you create scenes by writing in Typescript. First, we can start by positioning our 3D models in the builder, and then export it to the SDK to add further interactivity.</p>
<p>Let’s dive into creating a  scene in Decentraland with an easy interaction.</p>
<h3>Before we get started:</h3>
<ul>
<li>Install <a href="https://code.visualstudio.com/">Visual Studio Code</a></li>
<li>Install the Decentraland SDK 6 extension for Visual Studio Code</li>
<li>Create an account for <a href="https://decentraland.org/">Decentraland</a></li>
<li>Download <a href="https://sketchfab.com/3d-models/plush-unicorn-fd6f044ab8164a7e947f69fbe58435ee">this unicorn 3D model </a> (as a .glb file)</li>
<li>Download <a href="https://sketchfab.com/3d-models/cookie-1bbcdb1d6dd34bfe8c9176ccfb7d5c3c">this cookie 3D model</a> (as a .glb file)</li>
</ul>
<h2>Step One - Using the Decentraland Builder</h2>
<p>Go to the <a href="https://builder.decentraland.org">Decentraland Builder</a>, and click on <strong>Build Scenes</strong>. Give your scene a name and description and build it with 2 rows and 2 columns.
You should now see an empty scene with only grass. You can orbit and pan around the scene by holding and dragging the left or right mouse buttons respectively.</p>
<p>On the top right, you’ll see <strong>Asset packs</strong>, click the + sign near it to be able to upload your own models. Upload and import the plush unicorn model. Give your asset pack a name and create it. You should now be able to see the asset pack you created on the list on the right panel, select it and click <strong>“edit”</strong>. From here you can click on <strong>“Add Assets”</strong> and import more models to the same pack. Go ahead and upload the low poly cookie model too.</p>
<p>![Asset Pack](/assets/img/articles/2023-05-02-Decentraland-Building-your-first-Decentraland-project/1.webp</p>
<p>Back in the scene, click on the unicorn from the asset pack and it should show up in the scene on the grass. Select your unicorn in the scene and type in <strong>“R”</strong> on your keyboard. This will bring up the scaling tool. Click on the center of the gizmo and hold it. Dragging this to the right and left will scale the model up or down respectively. Scale the unicorn to your liking. Click <strong>‘R’</strong> again to remove the scaling gizmo. Drag the unicorn model to move it around the map or use <strong>‘W’</strong> to bring up the transform gizmo.</p>
<p>![Scaling the unicorn](/assets/img/articles/2023-05-02-Decentraland-Building-your-first-Decentraland-project/2.webp</p>
<p>Once you’re happy with the position and scale of the unicorn, click on the cookie from the asset pack to import it into the scene. Scale it to your liking. The position of the cookie will not matter for now, as we will be spawning it into the scene with code later.</p>
<p>![Unicorn and cookie](/assets/img/articles/2023-05-02-Decentraland-Building-your-first-Decentraland-project/3.webp</p>
<h2>Step Two - Exporting the scene to use the Decentraland SDK</h2>
<p>Download your scene from the button on the top right corner.</p>
<p>![Download Scene](/assets/img/articles/2023-05-02-Decentraland-Building-your-first-Decentraland-project/4.webp</p>
<p>Unzip the downloaded folder, and open it in Visual Studio Code. You should see your Decentraland SDK 6 extension on the left sidebar.</p>
<p>![Decentraland SDK 6 extension](/assets/img/articles/2023-05-02-Decentraland-Building-your-first-Decentraland-project/5.webp</p>
<p>Click “Run Scene” to preview what you’ve built so far.</p>
<h2>Step Three - Adding interactions</h2>
<p>Now, let’s add some functionality to the scene. Let’s start by giving the unicorn a “Pet” option. Open the game.ts file inside the src folder. This is where all the game logic for our scene sits, which has been automatically generated based on the models and its positions we previously gave the Decentraland builder.</p>
<p>Scroll down till you find the plushUnicorn variable.</p>
<pre><code class="hljs language-ini">    const <span class="hljs-attr">plushUnicorn</span> = new Entity(<span class="hljs-string">'plushUnicorn'</span>)

    engine.addEntity(plushUnicorn)
    plushUnicorn.setParent(_scene)
    const <span class="hljs-attr">transform6</span> = new Transform({
      position: new Vector3(17, 0, 15.5),
      rotation: new Quaternion(0, 0, 0, 1),
      scale: new Vector3(12.5, 12.5, 12.5)
    })

    plushUnicorn.addComponentOrReplace(transform6)

    const <span class="hljs-attr">gltfShape2</span> = new GLTFShape(<span class="hljs-string">"a0c1fd58-702d-4955-8d9f-82fea766c3b8/plush_unicorn.glb"</span>)
    <span class="hljs-attr">gltfShape2.withCollisions</span> = <span class="hljs-literal">true</span>
    <span class="hljs-attr">gltfShape2.isPointerBlocker</span> = <span class="hljs-literal">true</span>
    <span class="hljs-attr">gltfShape2.visible</span> = <span class="hljs-literal">true</span>

    plushUnicorn.addComponentOrReplace(gltfShape2)
</code></pre>
<p>A few things are happening here. A new variable is created called <strong>plushUnicorn</strong>. It’s then added to the Decentraland engine, so it can be displayed. Its position, rotation and scale are set in the <strong>Transform</strong> component. A <strong>gltfShape</strong> variable holding the path to the unicorn’s model is also created, and then added as a component to plushUnicorn.</p>
<p>Let’s add the following code right after:</p>
<pre><code class="hljs language-javascript">    plushUnicorn.<span class="hljs-title function_">addComponent</span>(
      <span class="hljs-keyword">new</span> <span class="hljs-title class_">OnPointerDown</span>(
        <span class="hljs-function">(<span class="hljs-params">e</span>) =></span> {

        },
        { <span class="hljs-attr">button</span>: <span class="hljs-title class_">ActionButton</span>.<span class="hljs-property">POINTER</span>, <span class="hljs-attr">hoverText</span>: <span class="hljs-string">'Pet'</span> }
        )
    )
</code></pre>
<p>We add a new component to the plushUnicorn called <strong>OnPointerDown</strong>. This creates a button called “Pet” which will appear when you hover over the unicorn. Save the file and go back to the Decentraland SDK 6 extension and run the scene to see it.</p>
<p>!['Pet' the Unicorn](/assets/img/articles/2023-05-02-Decentraland-Building-your-first-Decentraland-project/6.webp</p>
<p>Clicking this does nothing currently, so let’s change that.</p>
<p>Back in the game.ts script, find all the code related to creating the <strong>cookie</strong> variable and encapsulate it into a new function called <strong>newCookie()</strong>.</p>
<pre><code class="hljs language-ini">    function newCookie() {

      const <span class="hljs-attr">cookie</span> = new Entity(<span class="hljs-string">'cookie'</span>)
      engine.addEntity(cookie)
      cookie.setParent(_scene)
      const <span class="hljs-attr">transform7</span> = new Transform({
        position: new Vector3(21, 0, 16),
        rotation: new Quaternion(0, 0, 0, 1),
        scale: new Vector3(6.488997459411621, 6.488997459411621, 6.488997459411621)
      })

      cookie.addComponentOrReplace(transform7)
      const <span class="hljs-attr">gltfShape3</span> = new GLTFShape(<span class="hljs-string">"9e14fd6f-7e06-4fe5-9114-5c898f99bbc5/cookie.glb"</span>)
      <span class="hljs-attr">gltfShape3.withCollisions</span> = <span class="hljs-literal">true</span>
      <span class="hljs-attr">gltfShape3.isPointerBlocker</span> = <span class="hljs-literal">true</span>
      <span class="hljs-attr">gltfShape3.visible</span> = <span class="hljs-literal">true</span>
      cookie.addComponentOrReplace(gltfShape3)
    }
</code></pre>
<p>If you run the scene again, you’ll notice that the cookie is gone. That’s because we’ve put the instantiation of the cookie in a function, but then never call it.</p>
<p>Let’s go back to the <strong>OnPointerDown</strong> component on the <strong>plushUnicorn</strong>, and let’s call the <strong>newCookie</strong> function from there.</p>
<pre><code class="hljs language-scss">    plushUnicorn<span class="hljs-selector-class">.addComponent</span>(
      new OnPointerDown(
        (e) => {
             <span class="hljs-built_in">newCookie</span>()
        },
        { <span class="hljs-selector-tag">button</span>: ActionButton.POINTER, hoverText: <span class="hljs-string">'Pet'</span> }
        )
    )
</code></pre>
<p>Now if you run the scene again, you’ll see that when you click <em>"Pet"</em> on the unicorn, the cookie appears.</p>
<p>Currently, a new cookie is spawned every time the unicorn is pet, but you cannot see them because they're being spawned in the exact same position.</p>
<p>Let’s change this by adding a random position every time the function is called.</p>
<pre><code class="hljs language-ini">    function newCookie() {
      const <span class="hljs-attr">cookie</span> = new Entity(<span class="hljs-string">'cookie'</span>)
      engine.addEntity(cookie)
      cookie.setParent(_scene)

      const <span class="hljs-attr">newPosition</span> = new Vector3(
        Math.random() * 4 + 18,
        -0.35,
        Math.random() * 4 + 15
      )

      const <span class="hljs-attr">transform7</span> = new Transform({
        position: newPosition,
        rotation: new Quaternion(0, 0, 0, 1),
        scale: new Vector3(6.488997459411621, 6.488997459411621, 6.488997459411621)
      })
      cookie.addComponentOrReplace(transform7)
      const <span class="hljs-attr">gltfShape3</span> = new GLTFShape(<span class="hljs-string">"9e14fd6f-7e06-4fe5-9114-5c898f99bbc5/cookie.glb"</span>)
      <span class="hljs-attr">gltfShape3.withCollisions</span> = <span class="hljs-literal">true</span>
      <span class="hljs-attr">gltfShape3.isPointerBlocker</span> = <span class="hljs-literal">true</span>
      <span class="hljs-attr">gltfShape3.visible</span> = <span class="hljs-literal">true</span>
      cookie.addComponentOrReplace(gltfShape3)
    }
</code></pre>
<p>Here, we’ve created a variable named <strong>newPosition</strong> where the x and z coordinates are randomly chosen. The y coordinate is also set to be below 0 so the cookies aren’t floating in the air. This variable is then assigned to the position parameter in the <strong>Transform</strong> component.</p>
<p><em>You can play around with the numbers to suit your scene. For easy testing, you can keep the Decentraland scene tab open near your game.ts script. Every time you save the file, the scene will re-compile.</em></p>
<p>Now everytime you pet the unicorn, cookies will spawn around it!</p>
<p>![Unicorn and cookies!](/assets/img/articles/2023-05-02-Decentraland-Building-your-first-Decentraland-project/7.webp</p>
<h2>Step Four - Adding the Decentraland ECS Utils library</h2>
<p>Let’s go on and add a bit more functionality to make it more engaging.</p>
<pre><code class="hljs language-ini">    const <span class="hljs-attr">startPosition</span> =  new Vector3(<span class="hljs-number">17</span>, <span class="hljs-number">4</span>, <span class="hljs-number">15.5</span>)
    function newCookie() {
      const <span class="hljs-attr">cookie</span> = new Entity(<span class="hljs-string">'cookie'</span>)
      engine.addEntity(cookie)
      cookie.setParent(_scene)

      const <span class="hljs-attr">newPosition</span> = new Vector3(
        Math.random() * 4 + 18,
        -0.35,
        Math.random() * 4 + 15
      )

      const <span class="hljs-attr">transform7</span> = new Transform({
        position: newPosition,
        rotation: new Quaternion(0, 0, 0, 1),
        scale: new Vector3(6.488997459411621, 6.488997459411621, 6.488997459411621)
      })
      cookie.addComponentOrReplace(transform7)
      const <span class="hljs-attr">gltfShape3</span> = new GLTFShape(<span class="hljs-string">"9e14fd6f-7e06-4fe5-9114-5c898f99bbc5/cookie.glb"</span>)
      <span class="hljs-attr">gltfShape3.withCollisions</span> = <span class="hljs-literal">true</span>
      <span class="hljs-attr">gltfShape3.isPointerBlocker</span> = <span class="hljs-literal">true</span>
      <span class="hljs-attr">gltfShape3.visible</span> = <span class="hljs-literal">true</span>
      cookie.addComponentOrReplace(gltfShape3)

      cookie.addComponent(new utils.MoveTransformComponent(startPosition, newPosition, 1))
    }
</code></pre>
<p>Here, we’ve added the <strong>startPosition</strong> variable. This should be the same vector3 as the position of your <strong>plushUnicorn</strong> entity, except the y coordinate where I’ve added +4. This is so the cookies still spawn from “inside” the unicorn but closer to its head.</p>
<p>At the end of the function, we’ve added a new component <strong>MoveTransformComponent</strong> that will move the cookie from the <strong>startPosition</strong> to <strong>newPosition</strong> in 1 second.</p>
<p>However, Visual Studio will not find the “utils” library, so let’s add that. From the left sidebar, click on the + sign near the <em>dependencies</em> under the Decentraland SDK 6 extension.</p>
<p>![Installing Decentraland SDK dependencies](/assets/img/articles/2023-05-02-Decentraland-Building-your-first-Decentraland-project/8.webp</p>
<p>When prompted for the package name, type in <strong>“@dcl/ecs-scene-utils”</strong> and press enter to confirm.
Now at the very top of the script, add the following:</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> utils <span class="hljs-keyword">from</span> <span class="hljs-string">'@dcl/ecs-scene-utils'</span>
</code></pre>
<p>And there you go! Run the scene again, and when you “pet” the unicorn, he should spit out cookies 🙂</p>
<h3>Resources</h3>
<ul>
<li><a href="https://github.com/decentraland-scenes/Awesome-Repository#Examples">Decentraland Scenes - Awesome Repository on Github</a></li>
<li><a href="https://docs.decentraland.org/creator/development-guide/utils/">Decentraland Development Guide - Utils</a></li>
</ul>
<p><em>Article Photo by <a href="https://decentraland.org/blog/announcements/genesis-plaza-relaunched">Decentraland</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[An Introduction to WeatherKit and Alternative Weather Service APIs for iOS Developers]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/05/01/iOS-WeatherKit</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/05/01/iOS-WeatherKit</guid>
            <pubDate>Mon, 01 May 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Nowadays weather APIs are used in a wide range of applications from travel sites that help people plan their daily outdoor activities to distribution logistics where severe weather warnings can help companies plan for supply problems. Many mobile developers could also benefit from these weather services to provide useful information to complement the core functionality of their own apps as well. Adding a daily weather forecast to a calendar app could help in choosing which day to go hiking. Knowing today's weather could help a restaurant app make suitable suggestions for meals and drinks. Whilst a Japanese cherry blossom viewing app could make use of historic weather data to help predict the best place to go on a certain day.</p>
<h2>WeatherKit</h2>
<p>Up to now, iOS developers have had to rely on a mixed variety of third-party weather services and contend with numerous APIs, complex pricing plans and varying features. However, with the release of iOS 16, Apple now provides its own fully integrated weather service for iOS and all its other platforms (iPadOS 16, macOS 13, tvOS 16 and watchOS 9). In addition to a Swift API for Apple's own platforms called WeatherKit Apple also provides a REST API for other platforms, such as Android and websites, as well but I won't be covering this aspect in this article.</p>
<p>Apple weather was originally a weather service offered by Dark Sky, a popular app and weather service that Apple purchased in March 2022. Although Apple will be shutting this service down in March 2023 Apple has taken the core weather forecasting functionality and updated the service with improvements and overall performance and stability enhancements.</p>
<p>Note that although many of Dark Sky's features and capabilities have been integrated into Apple's new weather service, for those who are already familiar with Dark Sky you would be advised to compare service details for any possible changes.</p>
<p>WeatherKit is powered by this new weather service and provides apps and services with a comprehensive range of past, present, and future weather data that can be used to help you keep your users up to date with weather in their location.</p>
<p>WeatherKit is a welcome, if not late, addition to a growing number of weather services available and although there are several competing services already available there are a few specific advantages to Apple's own weather service.</p>
<p>Firstly, WeatherKit uses their all-new global weather service that provides a vast amount of up-to-date hyperlocal weather information. This offers both hourly forecasts for the next 10 days and minute-by-minute forecasts for the next hour as well as severe weather alerts and historical weather data.</p>
<p>Next, WeatherKit includes a Swift API utilizing a modern Swift syntax with Swift concurrency so it’s easy to get weather data with only a few lines of code. In addition, as the service is built on the mature Foundation and CoreLocation APIs, formatting measurements and converting units couldn't be easier.</p>
<p>Finally, as you would expect from Apple, WeatherKit is designed to give hyperlocal forecasts without compromising your user data. The location information you provide for the API is only used to provide weather forecasts. It is not stored on Apple's servers and the user is not tracked between requests. In addition, only the location data is used without any association to user information that is personally identifiable.</p>
<h3>Data types</h3>
<p>WeatherKit provides a variety of data types for current weather information, including:</p>
<ol>
<li><strong>Temperature</strong>: The current temperature, in both Celsius and Fahrenheit.</li>
<li><strong>Weather Condition</strong>: The current weather condition, such as clear, cloudy, rainy, or snowy.</li>
<li><strong>Weather Icon</strong>: An icon representing the current weather condition.</li>
<li><strong>Humidity</strong>: The current humidity level, expressed as a percentage.</li>
<li><strong>Wind Speed</strong>: The current wind speed, in both meters per second and miles per hour.</li>
<li><strong>Wind Direction</strong>: The current wind direction, in degrees.</li>
<li><strong>Pressure</strong>: The current atmospheric pressure, in both hPa and inHg.</li>
<li><strong>UV Index</strong>: The current ultraviolet (UV) index, which indicates the level of UV radiation.</li>
<li><strong>Visibility</strong>: The current visibility, in both kilometers and miles.</li>
<li><strong>Cloud Cover</strong>: The current cloud cover, expressed as a percentage.</li>
<li><strong>Precipitation</strong>: The current precipitation, including the type of precipitation (rain, snow, sleet, etc.) and the intensity.</li>
<li><strong>Sunrise/Sunset</strong>: The time of sunrise and sunset for the current location.
These are the main data types that can be retrieved for current weather information in WeatherKit.
Note that the specific data types available vary based on the weather you are.</li>
</ol>
<h3>Requirements</h3>
<p>There are number of requirements that you need to be aware of when considering Apple's weather service.</p>
<p>Firstly, you will obviously need to have a valid Apple Developer Program membership. Access to WeatherKit is specifically included in the Apple Developer Program. Not only does that provide you with all the tools and resources you need to develop and distribute apps but also gives you access to usage analytics to monitor API access and manage subscription levels.</p>
<p>Secondly, WeatherKit requires iOS 16, iPadOS 16, macOS 13, tvOS 16, or watchOS 9. Unfortunately, Apple couldn’t offer this service to older platforms so for those who need to handle apps supporting a previous OS version then you will need to either integrate the weather service as an option for the latest version only or wait a few years until WeatherKit is available on every OS version you need to support.</p>
<p>Thirdly, WeatherKit only provides 500,000 API calls a month for free (as part of your Apple Developer Program membership). If you need additional API calls, then you can choose to pay for additional API usage by subscribing to one of the other paid weather data tiers. This is easily done via the Account tab of the Apple Developer app, and you can upgrade or downgrade a subscription at any time.</p>

















































<table><thead><tr><th>Calls per month</th><th>Cost</th></tr></thead><tbody><tr><td>500,000</td><td>free (Included with membership)</td></tr><tr><td>1 million</td><td>US$49.99</td></tr><tr><td>2 million</td><td>US$99.99</td></tr><tr><td>5 million</td><td>US$249.99</td></tr><tr><td>10 million</td><td>US$499.99</td></tr><tr><td>20 million</td><td>US$999.99</td></tr><tr><td>50 million</td><td>US$2,499.99</td></tr><tr><td>100 million</td><td>US$4,999.99</td></tr><tr><td>150 million</td><td>US$7,499.99</td></tr><tr><td>200 million</td><td>US$9,999.99</td></tr></tbody></table>
<p>Finally, you will need to be aware of attribution requirements for Apple's weather service. Regardless of whether you use WeatherKit in your apps, web apps, or websites, you will have to follow Apple's strict guidelines and requirements for attributing weather data from Apple.</p>
<h2>Alternative free weather API services</h2>
<p>In addition to the WeatherKit free tier there are alternative free weather API services available for obtaining weather data for mobile applications which may offer you a better service depending on your needs. In deciding which service is best for you there are several possible differences you may need to consider.</p>
<p>A detailed comparison of weather services is difficult to make because service options, pricing tiers and API functionality varies widely. Since every app will has its own specific weather requirements every weather service will have its own merits and demerits you will have to consider.</p>
<p>Remember that although with Apple's weather service only the number of available API calls changes between free and paid plans, other weather services may also limit the weather information you can access as well when using their free tier.</p>
<p>Here are some points you should consider:</p>
<ol>
<li>
<p><strong>Features</strong>: The WeatherKit free tier provides weather data for cities around the world, including current weather conditions, hourly and daily forecasts, as well as historical weather data and severe weather alerts. Free weather API services may have limited features compared to WeatherKit, such as limited API calls, fewer types of weather information or less accurate weather data.</p>
</li>
<li>
<p><strong>Accuracy and Coverage</strong>: The WeatherKit free tier uses multiple sources, including weather stations and weather models, to ensure the accuracy of its forecasts. Free weather API services may not have as many data sources or may use less reliable data, which could result in less accurate weather information. Also be aware that WeatherKit is a global weather service so your app or service should work worldwide whilst other free weather API services be either be region specific or be unable to provide hyper-local weather data for every country.</p>
</li>
<li>
<p><strong>Service Limitations</strong>: The WeatherKit free tier has limitations on the number of API calls that can be made, which may not be sufficient for some mobile applications. Free weather API services may also have similar limitations (with more or fewer free API calls available) and may additionally also limit the kinds of weather data that can be obtained as well.</p>
</li>
<li>
<p><strong>Support</strong>: The WeatherKit free tier provides basic support for developers, including comprehensive documentation, sample code and a community forum. Free weather API services may have limited support and/or may not have dedicated support teams.</p>
</li>
<li>
<p><strong>Privacy</strong>: With WeatherKit you can be sure that the user is not being tracked between requests and user location data is not being used for other purposes. Free weather API services may be harvesting and selling your user location information as well as using it to identify the user for targeted advertising.</p>
</li>
</ol>
<p>Overall, both Apple's weather service and other free weather API services can be good options for obtaining weather data for mobile applications, depending on the specific needs and budget of your app. However, the WeatherKit free tier will usually provide more reliable and accurate weather data, with limitations only on API calls, whilst free weather API services may have similar limitations and less available data but may be more cost-effective if they offer more free API usage.</p>
<p>Bearing the above in mind let’s look at some popular weather service APIs for mobile apps. Now, this is not a comprehensive list nor a top ten ranking of weather services. There are far too many weather services available to list them all and it's difficult to provide an exact order of the most popular weather APIs since popularity can depend on various factors such as pricing, features, accuracy, and ease of use. Nevertheless, here are a few of the most popular weather APIs, in no particular order:</p>
<h3>OpenWeather API:</h3>
<p>This API provides weather information for any location in the world, including current weather conditions, forecasts, and historical data.</p>
<h3>Weather Underground API:</h3>
<p>This API provides weather data for over 250,000 locations worldwide, including real-time observations, forecasts, and severe weather alerts.</p>
<h3>AccuWeather API:</h3>
<p>This API provides accurate weather data for any location in the world, including current conditions, hourly and daily forecasts, and severe weather alerts.</p>
<h3>Weather API:</h3>
<p>This API provides weather data for over 200,000 locations worldwide, including current conditions, forecasts, and historical data.</p>
<h3>Weatherstack API:</h3>
<p>This API provides current weather data and forecasts for over 200,000 locations worldwide, with support for multiple languages and units of measurement.</p>
<h3>National Weather Service API:</h3>
<p>This API provides weather data for the United States, including current conditions, forecasts, and severe weather alerts.</p>
<p>Please note that while these APIs all have a free tier, some may have limitations on the number of requests you can make per day or require attribution to the provider. Be sure to review the terms and conditions of each service when comparing API functionality.</p>
<h2>How to pick a service?</h2>
<p>Each weather service has its own strengths and weaknesses, and the best choice for a particular mobile application will depend on the specific needs of the app, the target audience, and other such factors. To highlight the differences that such services may have let’s take a closer look at one such service and compare it to WeatherKit.</p>
<p>OpenWeather and Apple Weather are both weather data providers that offer APIs for developers to integrate into their mobile apps. Here are some advantages/disadvantages of OpenWeather compared to WeatherKit:</p>
<h3>Advantages:</h3>
<ol>
<li>
<p><strong>Coverage</strong>: OpenWeather provides weather data for over 200,000 locations worldwide, while WeatherKit's coverage is mainly limited to the United States, Canada, and a few other countries such as Germany, India, Japan, China, Brazil etc. If you need specific global coverage then OpenWeather may be a better choice, depending on the country, although I would expect Apple to expand its use of third-party meteorological data providers in the future.</p>
</li>
<li>
<p><strong>Range of data</strong>: OpenWeather provides a wider range of weather data, including current conditions, forecasts, historical data, and satellite imagery, while WeatherKit mainly focuses on current conditions and forecasts. If you need access to a wider variety of weather data, OpenWeather could be a better choice although you may need to use a paid plan to access some it. Although WeatherKit does offer historical data it only goes back a few years compared to more than 40 years back for OpenWeather. However, historical data is included in the WeatherKit free tier without restriction whilst OpenWeather limits your access to historical data in its free tier.</p>
</li>
<li>
<p><strong>Pricing</strong>: OpenWeather offers several free plans with limited requests per day, while WeatherKit offers one free plan with limited requests per month. Overall, OpenWeather offers cheaper plans with more API calls but restricts available weather data depending on the tier chosen. If cost is a concern, OpenWeather's free plan may be more suitable but check to ensure that it will provide the weather data you require.</p>
</li>
<li>
<p><strong>Customization</strong>: OpenWeather allows for more customization options, such as choosing the language, units of measurement, and data format, while WeatherKit has fewer customization options. If you need more flexibility in how the weather data is presented in your app, OpenWeather may be a better choice.</p>
</li>
</ol>
<h3>Disadvantages:</h3>
<ol>
<li>
<p><strong>Accuracy</strong>: WeatherKit uses a proprietary forecasting algorithm that considers a wide range of weather data sources, including radar and satellite imagery, to provide highly accurate and up-to-date weather data. This can be especially useful for developers who require highly accurate weather data, but availability may be limited to certain regions.</p>
</li>
<li>
<p><strong>Simplicity</strong>: WeatherKit has a simpler API structure with a modern Swift syntax and comprehensive documentation and sample code, making it easier for developers to integrate into their mobile apps. This can be useful for developers who are new to weather APIs or who need to quickly add weather data to their app.</p>
</li>
<li>
<p><strong>Support</strong>: WeatherKit offers dedicated support for its customers, including a help center, documentation, and technical support. This can be especially useful for developers who need assistance with integrating the API into their app or resolving issues with the weather data. Long term, developers can expect new functionality to be added and coverage to be expanded. Unlike a third-party API provider it is unlikely that this service will suddenly be stopped forcing you to switch to another weather service.</p>
</li>
<li>
<p><strong>Data quality</strong>: WeatherKit focuses on providing high-quality weather data for the areas it covers, rather than trying to cover a large number of locations worldwide. This can be beneficial for developers who need to ensure the accuracy and reliability of their weather data for a specific region or location but may be a problem if you are not yet in one of these areas.</p>
</li>
<li>
<p><strong>Privacy</strong>: As mentioned before you can trust Apple to treat your users’ location information with respect. Data is not collected, and users are not tracked. It should be noted that OpenWeather also claims not to collect or store any parameters from your API requests nor share any data with third-party companies and services.</p>
</li>
</ol>
<p>Ultimately, the choice between OpenWeather (or any similar weather service) and WeatherKit will depend on your specific needs and requirements for your mobile app. As a relatively new weather service Apple Weather has a smaller set of weather data available with only a 10-day forecast and a few years of historic data. If your app specifically makes use of historic weather data or requires a forecast for a whole month then currently you will need to consider another service. Although, such long-range forecasts and detailed historical data are rarely available at the free tier.</p>
<h2>Conclusion</h2>
<p>Overall, WeatherKit would seem to be most suitable for apps that use weather data for detailed local weather forecasts for the next week or so. For those apps that aren’t specifically weather related then the simple API integration and low-cost maintenance will be appealing to developers who would like to use weather data but don’t wish to spend the time or effort for a feature that maybe only has limited use within existing functionality. In addition, having the Weather Service account as part of your Apple Developer Program membership makes billing and account management easy as well.</p>
<p>On the other hand, if weather data is a core part of your app functionality and you require specific information not supported in WeatherKit then you should be looking at one of the other weather services available. Although, at the free tier level WeatherKit usually offers much of the weather data that you can only access with paid tiers in other weather services.</p>
<hr>
<h3>Resources</h3>
<ul>
<li><a href="https://developer.apple.com/weatherkit/">WeatherKit</a></li>
<li><a href="https://openweathermap.org">OpenWeather</a></li>
<li><a href="https://www.weatherapi.com">Weather API</a></li>
<li><a href="https://www.wunderground.com">Weather Underground</a></li>
<li><a href="https://www.accuweather.com">AccuWeather</a></li>
<li><a href="https://weatherstack.com">Weatherstack</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Accelerate Your Android App Testing with Appium and Java- A Beginner’s Guide (Part 1)]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/04/26/Accelerate-Your-Android-App-Testing-with-Appium-and-Java-A-Beginner’s-Guide-(Part-1)</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/04/26/Accelerate-Your-Android-App-Testing-with-Appium-and-Java-A-Beginner’s-Guide-(Part-1)</guid>
            <pubDate>Wed, 26 Apr 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Manual testing is an essential part of Android application testing that involves a tester manually going through an application to ensure that it meets the desired specifications and quality standards. However, this process can become monotonous and repetitive, leading to a decrease in the tester's attention span and motivation. As a result, the testing process can become less effective, and the quality of the application can suffer. This is where Appium comes in, offering a solution to this problem by handling repetitive tasks and freeing up the tester's time and energy to focus on more complex and creative aspects of testing.</p>
<p>Appium is an open-source tool for automating native, mobile web, and hybrid applications. It allows you to write tests in multiple programming languages such as Java, Ruby, Python, etc. With Appium, you can write tests that simulate user interactions on an Android device, which can help test the functionality of your application.</p>
<p>Furthermore, Appium uses the standard WebDriver API, so you can leverage the wide range of WebDriver-based tools and libraries available in the market. One of the major advantages of Appium is it can be integrated easily with other testing frameworks, such as Selenium, allowing testers to leverage existing test scripts and frameworks for mobile app testing.</p>
<p>By the end of this article, you will have a thorough understanding of how to get started with Android test automation using Appium and Java. Furthermore, you will be able to automate a feature of the Calculator app, putting into practice what you've learned. Before we get started the reader should have a basic knowledge of <a href="https://www.javatpoint.com/java-tutorial">Java</a> programming language and <a href="https://www.javatpoint.com/selenium-tutorial">Selenium</a>. We’ll cover the following topics:</p>
<ul>
<li>Setting up Appium for Android test automation</li>
<li>Getting appPackage and appActivity</li>
<li>Writing your first Appium test</li>
</ul>
<h2>1. Setting up Appium for Android test automation</h2>
<p>Setting up Appium for Android test automation is a straightforward process. I will be showing the setup in macOS for this article. You can check the installation in Windows from <a href="https://support.smartbear.com/testcomplete/docs/app-testing/mobile/device-cloud/configure-appium/android-on-windows.html">here</a>. Here’s how you can get started:</p>
<p>Install Homebrew: Homebrew is a package manager for Mac OS X that makes it easy to install and manage open-source software. You can install Homebrew by following the instructions on the <a href="https://brew.sh/">Homebrew</a> website.</p>
<p>Install Node.js: Appium is built on Node.js, so you’ll need to install Node.js on your Macbook. You can install Node.js by running the following command in your terminal:</p>
<ul>
<li><code>brew install node</code></li>
</ul>
<p>Install Appium: Once you have installed Node.js, you can install Appium by running the following command in your terminal:</p>
<ul>
<li><code>npm install -g appium</code></li>
</ul>
<p>Install Android Studio: You’ll also need to install Android Studio on your Macbook. The Android Studio provides the tools and APIs required to develop, test, and debug Android applications. You can download the <a href="https://developer.android.com/studio">Android Studio</a> from the Android developer website.</p>
<p>Setup SDK Platforms and Tools: Once downloaded and installed, open Android Studio, click on Configure, and then SDK Manager.</p>
<p><img src="/assets/img/articles/2023-04-26-Accelerate-Your-Android-App-Testing-with-Appium-and-Java-A-Beginner%E2%80%99s-Guide-(Part-1)/Android_studio_setup_1.webp" alt=""></p>
<p>You can download any desired SDK Tools from here. Also can upgrade any required updates from Android SDK settings. We’ll select the defaults for now.</p>
<p><img src="/assets/img/articles/2023-04-26-Accelerate-Your-Android-App-Testing-with-Appium-and-Java-A-Beginner%E2%80%99s-Guide-(Part-1)/Android_studio_setup_2.webp" alt=""></p>
<p>Set up Environment Variable: To run Appium tests on an Android device, you need to set up environment variables that tell the Appium server where to find the Android SDK and other tools like the emulator and platform tools. This is necessary to ensure that Appium can access these tools and run tests correctly.</p>
<p>To accomplish this, we can include the following variables in the system environment.</p>
<p>On MacOS-</p>
<ul>
<li>To add environment variables for Android Studio, you need to edit the shell profile file for your chosen shell. For Bash, this is usually the .bash_profile file, while for Zsh it's the .zshrc file.</li>
</ul>
<p><img src="/assets/img/articles/2023-04-26-Accelerate-Your-Android-App-Testing-with-Appium-and-Java-A-Beginner%E2%80%99s-Guide-(Part-1)/Android_home_setup.webp" alt=""></p>
<p>Set up the Android emulator: Once you have installed the Android SDK, you can set up an Android emulator to test your application. The Android emulator is a software tool that allows you to run a virtual Android device on your computer. This virtual device can be used to test your applications without the need for a physical Android device.</p>
<ul>
<li>Open Android Studio</li>
<li>Click Configure</li>
<li>Click on AVD Manager</li>
</ul>
<p><img src="/assets/img/articles/2023-04-26-Accelerate-Your-Android-App-Testing-with-Appium-and-Java-A-Beginner%E2%80%99s-Guide-(Part-1)/Android_studio_AVDmanager.webp" alt=""></p>
<p>To create an Android emulator, you need to launch the Virtual Device Configuration flow, which is accessible from within Android Studio.</p>
<p><img src="/assets/img/articles/2023-04-26-Accelerate-Your-Android-App-Testing-with-Appium-and-Java-A-Beginner%E2%80%99s-Guide-(Part-1)/Virtual_device_setup_1.webp" alt=""></p>
<p>When creating an Android emulator, it's important to consider which Play Store services are available, as certain apps may require the latest version of these services to be installed. Select the operating system and all the necessary stuff and complete creating the virtual device.</p>
<p><img src="/assets/img/articles/2023-04-26-Accelerate-Your-Android-App-Testing-with-Appium-and-Java-A-Beginner%E2%80%99s-Guide-(Part-1)/Virtual_device_setup_2.webp" alt=""></p>
<p>After creating our emulator, we can start it up by tapping the Play icon under the Actions section.</p>
<p><img src="/assets/img/articles/2023-04-26-Accelerate-Your-Android-App-Testing-with-Appium-and-Java-A-Beginner%E2%80%99s-Guide-(Part-1)/Virtual_device_setup_3.webp" alt=""></p>
<p>Start the Appium server: Once you have completed all of the above steps, you can start the Appium server by running the following command in your terminal:</p>
<ul>
<li>appium</li>
</ul>
<p><img src="/assets/img/articles/2023-04-26-Accelerate-Your-Android-App-Testing-with-Appium-and-Java-A-Beginner%E2%80%99s-Guide-(Part-1)/Start_appium_server.webp" alt=""></p>
<p>That’s it! You have successfully set up Appium for Android test automation on your Macbook. You can now write and run Appium tests for your Android application.</p>
<h2>2. Getting appPackage and appActivity</h2>
<p>We can find the App details using the <a href="https://developer.android.com/studio/command-line/adb">Android Debugging Bridge (ADB)</a> interface in a Terminal.</p>
<ol>
<li>Open your android emulator and open the app in which you want to find the appPackage and appActivity. I am using the android Calculator Application here.</li>
</ol>
<p><img src="/assets/img/articles/2023-04-26-Accelerate-Your-Android-App-Testing-with-Appium-and-Java-A-Beginner%E2%80%99s-Guide-(Part-1)/Calculator.webp" alt=""></p>
<ol start="2">
<li>
<p>Open a Terminal window and use the below command to see the appPackage and appActivity.</p>
<ul>
<li>adb shell dumpsys window | grep -E ‘mCurrentFocus’</li>
</ul>
</li>
</ol>
<p><img src="/assets/img/articles/2023-04-26-Accelerate-Your-Android-App-Testing-with-Appium-and-Java-A-Beginner%E2%80%99s-Guide-(Part-1)/AppPackage_and_activity.webp" alt=""></p>
<h2>3. Writing your first Appium test</h2>
<ol>
<li>
<p>Choose a programming language and Appium client library: Appium supports various programming languages such as Java, Python, Ruby, etc., and client libraries for each language are available.</p>
</li>
<li>
<p>Create a new test project: Set up a new project in your preferred IDE and include the Appium client library in your project’s build path.</p>
<ul>
<li>
<p>Open your IDE, say for example you are using Intellij Idea, start creating a new Java Project by following the below path,</p>
<p>Go to File -> New -> Project -> Select Maven Project -> Click Next</p>
</li>
</ul>
</li>
</ol>
<p><img src="/assets/img/articles/2023-04-26-Accelerate-Your-Android-App-Testing-with-Appium-and-Java-A-Beginner%E2%80%99s-Guide-(Part-1)/Project_setup_1.webp" alt=""></p>
<p>Once you are done with the above step, you will land on the project page,</p>
<ul>
<li>You need to add an Appium library that would help our test framework to execute the test cases and the same can be done with the help of the Maven Project’s pom.xml file.</li>
</ul>
<p><img src="/assets/img/articles/2023-04-26-Accelerate-Your-Android-App-Testing-with-Appium-and-Java-A-Beginner%E2%80%99s-Guide-(Part-1)/Project_setup_2.webp" alt=""></p>
<ol start="3">
<li>Write your first Appium test: Here’s an example of a Java test to launch the Calculator app on an Android emulator, input some numbers, and verify the result. Find the project <a href="https://github.com/Eftiar/Calculatortest.git">here</a>.</li>
</ol>
<pre><code class="hljs language-java"><span class="hljs-keyword">import</span> io.appium.java_client.android.AndroidDriver;
<span class="hljs-keyword">import</span> io.appium.java_client.remote.MobileCapabilityType;
<span class="hljs-keyword">import</span> org.openqa.selenium.remote.DesiredCapabilities;
<span class="hljs-keyword">import</span> java.net.URL;


<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">CalculatorTest</span> {
   <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception {
       <span class="hljs-type">DesiredCapabilities</span> <span class="hljs-variable">capabilities</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">DesiredCapabilities</span>();
       capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, <span class="hljs-string">"emulator-5554"</span>);
       capabilities.setCapability(<span class="hljs-string">"appPackage"</span>, <span class="hljs-string">"com.google.android.calculator"</span>);
       capabilities.setCapability(<span class="hljs-string">"appActivity"</span>, <span class="hljs-string">"com.android.calculator2.Calculator"</span>);
       <span class="hljs-type">AndroidDriver</span> <span class="hljs-variable">driver</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">AndroidDriver</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">URL</span>(<span class="hljs-string">"http://0.0.0.0:4723/wd/hub"</span>), capabilities);
       driver.findElementById(<span class="hljs-string">"digit_2"</span>).click();
       driver.findElementById(<span class="hljs-string">"op_add"</span>).click();
       driver.findElementById(<span class="hljs-string">"digit_2"</span>).click();
       driver.findElementById(<span class="hljs-string">"eq"</span>).click();
       <span class="hljs-type">String</span> <span class="hljs-variable">result</span> <span class="hljs-operator">=</span> driver.findElementById(<span class="hljs-string">"result"</span>).getText();
       System.out.println(<span class="hljs-string">"Result is: "</span> + result);
       driver.quit();
   }
}
</code></pre>
<p>This test uses the Appium Java client library and launches the Calculator app on an Android device, performs some basic calculations, and verifies the result. It uses the AndroidDriver class to interact with the mobile device, and DesiredCapabilities to set the device's capabilities.</p>
<ol start="4">
<li>Run the test: Run the test from your IDE, and it will execute the test on the connected mobile device or emulator.</li>
</ol>
<p>Note: Ensure the Appium server is running and listening to the correct port before executing the test.</p>
<h3>Here are some Top Appium Installation Hacks that's can make your Appium installation easier:</h3>
<ul>
<li>Use the Appium GUI installer: The Appium GUI installer is a graphical user interface that guides you through the installation process and can be helpful for beginners. <a href="https://github.com/appium/appium-desktop/releases/tag/v1.22.3-4">https://github.com/appium/appium-desktop/releases/tag/v1.22.3-4</a></li>
<li>Install required dependencies separately: Appium requires several dependencies, such as Node.js and the Android SDK. Installing these dependencies separately can make the Appium installation process smoother.</li>
<li>Use Appium doctor: Appium doctor is a command-line tool that can help diagnose and resolve common installation issues. Running Appium doctor before installing Appium can help identify any missing dependencies or configuration issues. <a href="https://github.com/appium/appium-doctor">https://github.com/appium/appium-doctor</a></li>
<li>Use a package manager: Using a package manager like Homebrew (for macOS) can simplify the installation process by automatically resolving dependencies. <a href="https://brew.sh/">https://brew.sh/</a></li>
</ul>
<p>Thank you for reading this blog post. I hope you found the information useful and informative. If you enjoyed this post, be sure to check out our next blog post, which will focus on locating elements on Appium, POM and running &#x26; debugging Appium tests on Android.</p>
<p><a href="https://www.linkedin.com/in/s-m-eftiar-hossain-a94808174/">https://www.linkedin.com/in/s-m-eftiar-hossain-a94808174/</a></p>
<p><em>Article Photo by <a href="https://unsplash.com/@agk42">Alex Knight</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to create a simple app with Flutter on the Web]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/04/18/Creating-a-simple-app-with-Flutter-on-the-Web</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/04/18/Creating-a-simple-app-with-Flutter-on-the-Web</guid>
            <pubDate>Tue, 18 Apr 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Recently, Monstarlab has been receiving an increasing number of app development projects using Flutter. In this article, we will have a look at how you can easily create a simple web app using Flutter on the web.</p>
<h2>How to create a simple sample app</h2>
<p>For this project, I have developed an application that showcases movie details.
The main points of the development are as follows:</p>
<ul>
<li>Use <a href="https://developers.themoviedb.org/">The Movie Database API</a> to fetch movie information via a Web API.</li>
<li>Use <a href="https://docs.getwidget.dev/">Get Widget</a> to customize the appearance.</li>
<li>Use <a href="https://github.com/lukepighetti/fluro">Fluro</a> to display parameterized URLs even with direct access.
<ul>
<li>By default, Flutter cannot interpret dynamic paths like <code>/movies/:movieId</code>.</li>
</ul>
</li>
</ul>
<p>※Due to copyright reasons, the movie image is replaced with a sample image.</p>













<table><thead><tr><th>Movie list</th><th>Movie details</th></tr></thead><tbody><tr><td><img src="/assets/img/articles/2023-04-18-How-to-create-a-simple-app-with-Flutter-on-the-Web/flutter-movie-list.webp" alt=""></td><td><img src="/assets/img/articles/2023-04-18-How-to-create-a-simple-app-with-Flutter-on-the-Web/flutter-movie-detail.webp" alt=""></td></tr></tbody></table>
<h2>Steps to create the web app</h2>
<h3>1. Set up the development environment for Flutter</h3>
<p>Install Flutter using Homebrew</p>
<pre><code class="hljs language-shell">brew install --cask flutter
</code></pre>
<p>Create a Flutter project in your working directory</p>
<pre><code class="hljs language-shell">flutter create my_app
cd my_app
</code></pre>
<p>Run the application</p>
<pre><code class="hljs language-shell">flutter run -d web-server
</code></pre>
<h4>How the app looks like after initial set up</h4>
<p><img src="/assets/img/articles/2023-04-18-How-to-create-a-simple-app-with-Flutter-on-the-Web/flutter-setup-result.webp" alt=""></p>
<h3>2. Install the <code>http</code> package</h3>
<p>Install the <code>http</code> package to fetch data from the Internet.</p>
<pre><code class="hljs language-shell">flutter pub add http
</code></pre>
<h3>3. Direct access to URLs with parameters</h3>
<p>Now, let's implement a way to directly access URLs with parameters.</p>
<p>The following article might help you understand how to set up a router with Fluro: <a href="https://medium.com/flutter-community/routing-in-flutter-using-fluro-bedb564cb737">Routing in Flutter using Fluro</a></p>
<h4>Install <code>Fluro</code></h4>
<pre><code class="hljs language-shell">flutter pub add fluro
</code></pre>
<p>Modify <code>main.dart</code> as following:</p>
<pre><code class="hljs language-dart"><span class="hljs-comment">// lib/main.dart</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:fluro/fluro.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter_dotenv/flutter_dotenv.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter_web_plugins/flutter_web_plugins.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:my_app/router/routes.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-keyword">static</span> FluroRouter? router;
  <span class="hljs-keyword">const</span> MyApp({Key? key}) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> MaterialApp(
        title: <span class="hljs-string">'Movie'</span>,
        initialRoute: <span class="hljs-string">'/'</span>,
        onGenerateRoute: (setting) {
          <span class="hljs-keyword">return</span> MyApp.router?.generator(setting);
        });
  }
}

Future main() <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">await</span> dotenv.load(fileName: <span class="hljs-string">'.env'</span>);
  setUrlStrategy(PathUrlStrategy());
  <span class="hljs-keyword">final</span> router = FluroRouter();
  Routes.configureRoutes(router);
  MyApp.router = router;
  runApp(<span class="hljs-keyword">const</span> MyApp());
}

</code></pre>
<p>Create <code>routes.dart</code> to create routes:</p>
<pre><code class="hljs language-dart"><span class="hljs-comment">// lib/router/routes.dart</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:fluro/fluro.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:my_app/pages/movie_detail.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:my_app/pages/movie_list.dart'</span>;

Handler createBasicHandler(Widget targetWidget) {
  <span class="hljs-keyword">return</span> Handler(
      handlerFunc: (BuildContext? context, <span class="hljs-built_in">Map</span>&#x3C;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">List</span>&#x3C;<span class="hljs-built_in">String</span>>> params) {
    <span class="hljs-keyword">return</span> targetWidget;
  });
}

Handler movieDetailPageHandler = Handler(
    handlerFunc: (BuildContext? context, <span class="hljs-built_in">Map</span>&#x3C;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">List</span>&#x3C;<span class="hljs-built_in">String</span>>> params) {
  <span class="hljs-keyword">return</span> MovieDetailPage(movieId: params[<span class="hljs-string">'movieId'</span>]!.first);
});

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Routes</span> </span>{
  <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> configureRoutes(FluroRouter router) {
    router.notFoundHandler = Handler(
        handlerFunc: (BuildContext? context, <span class="hljs-built_in">Map</span>&#x3C;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">List</span>&#x3C;<span class="hljs-built_in">String</span>>> params) {
      <span class="hljs-built_in">print</span>(<span class="hljs-string">"ROUTE WAS NOT FOUND!!!"</span>);
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
    });
    router
      ..define(<span class="hljs-string">'/'</span>, handler: createBasicHandler(<span class="hljs-keyword">const</span> MovieListPage()))
      ..define(<span class="hljs-string">'/movies'</span>, handler: createBasicHandler(<span class="hljs-keyword">const</span> MovieListPage()))
      ..define(<span class="hljs-string">'/movies/:movieId'</span>, handler: movieDetailPageHandler);
  }
}

</code></pre>
<h3>4. Network requests</h3>
<p>In order to hide the API key value, I created an API key environment variable by using <a href="https://pub.dev/packages/flutter_dotenv">flutter_dotenv</a>.</p>
<pre><code class="hljs language-shell">flutter pub add flutter_dotenv
</code></pre>
<p>The following code is to fetch data from <a href="https://developers.themoviedb.org/">The Movie Database API</a>:</p>
<pre><code class="hljs language-dart"><span class="hljs-comment">// lib/apis/movie.dart</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'dart:convert'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter_dotenv/flutter_dotenv.dart'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'package:http/http.dart'</span> <span class="hljs-keyword">as</span> http;

<span class="hljs-keyword">final</span> API_KEY = dotenv.<span class="hljs-keyword">get</span>(<span class="hljs-string">'API_KEY'</span>);

Future&#x3C;http.Response> fetchMovieList() <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">return</span> http.<span class="hljs-keyword">get</span>(
      <span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'https://api.themoviedb.org/3/movie/popular?api_key=<span class="hljs-subst">$API_KEY</span>'</span>));
}

Future&#x3C;http.Response> fetchMovieDetail(<span class="hljs-built_in">dynamic</span> movieId) <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">return</span> http.<span class="hljs-keyword">get</span>(<span class="hljs-built_in">Uri</span>.parse(
      <span class="hljs-string">'https://api.themoviedb.org/3/movie/<span class="hljs-subst">$movieId</span>?api_key=<span class="hljs-subst">$API_KEY</span>'</span>));
}
</code></pre>
<h3>5. Convert the responses data type</h3>
<p>If the return type of the data-fetching function is non-typed, it can be challenging to manage it.
Therefore, it is necessary to create a class that can convert the response data into a custom Dart object.</p>
<p>API schema is provided by <a href="https://developers.themoviedb.org/">The Movie Database API</a>.</p>
<ul>
<li><a href="https://developers.themoviedb.org/3/movies/get-popular-movies">Movie List API schema</a></li>
<li><a href="https://developers.themoviedb.org/3/movies/get-movie-details">Movie Detail API schema</a></li>
</ul>
<pre><code class="hljs language-dart"><span class="hljs-comment">// lib/models/movie.dart</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Movie</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String?</span> posterPath;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">bool</span> adult;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> overview;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> releaseDate;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&#x3C;<span class="hljs-built_in">int</span>> genreIds;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> id;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> originalTitle;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> originalLanguage;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> title;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String?</span> backdropPath;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">num</span> popularity;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> voteCount;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">bool</span> video;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">num</span> voteAverage;

  <span class="hljs-keyword">const</span> Movie(
      {<span class="hljs-keyword">this</span>.posterPath,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.adult,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.overview,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.releaseDate,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.genreIds,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.id,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.originalTitle,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.originalLanguage,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.title,
      <span class="hljs-keyword">this</span>.backdropPath,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.popularity,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.voteCount,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.video,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.voteAverage});

  <span class="hljs-keyword">factory</span> Movie.fromJson(<span class="hljs-built_in">Map</span>&#x3C;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>> json) {
    <span class="hljs-keyword">return</span> Movie(
        posterPath: json[<span class="hljs-string">'poster_path'</span>],
        adult: json[<span class="hljs-string">'adult'</span>],
        overview: json[<span class="hljs-string">'overview'</span>],
        releaseDate: json[<span class="hljs-string">'release_date'</span>],
        genreIds: <span class="hljs-built_in">List</span>&#x3C;<span class="hljs-built_in">int</span>>.from(json[<span class="hljs-string">'genre_ids'</span>]),
        id: json[<span class="hljs-string">'id'</span>],
        originalTitle: json[<span class="hljs-string">'original_title'</span>],
        originalLanguage: json[<span class="hljs-string">'original_language'</span>],
        title: json[<span class="hljs-string">'title'</span>],
        backdropPath: json[<span class="hljs-string">'backdrop_path'</span>],
        popularity: json[<span class="hljs-string">'popularity'</span>],
        voteCount: json[<span class="hljs-string">'vote_count'</span>],
        video: json[<span class="hljs-string">'video'</span>],
        voteAverage: json[<span class="hljs-string">'vote_average'</span>]);
  }
}

</code></pre>
<pre><code class="hljs language-dart"><span class="hljs-comment">// lib/models/movie_list.dart</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'movie.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MovieList</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> page;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&#x3C;Movie> results;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> totalResults;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> totalPages;

  <span class="hljs-keyword">const</span> MovieList(
      {<span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.page,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.results,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.totalResults,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.totalPages});

  <span class="hljs-keyword">factory</span> MovieList.fromJson(<span class="hljs-built_in">Map</span>&#x3C;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>> json) {
    <span class="hljs-keyword">return</span> MovieList(
        page: json[<span class="hljs-string">'page'</span>],
        results: (json[<span class="hljs-string">'results'</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">List</span>&#x3C;<span class="hljs-built_in">dynamic</span>>)
            .map((e) => Movie.fromJson(e))
            .toList(),
        totalResults: json[<span class="hljs-string">'total_results'</span>],
        totalPages: json[<span class="hljs-string">'total_pages'</span>]);
  }
}

</code></pre>
<pre><code class="hljs language-dart"><span class="hljs-comment">// lib/models/movie_detail.dart</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MovieDetail</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">bool</span> adult;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String?</span> backdropPath;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">Map</span>&#x3C;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>>? belongsToCollection;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int?</span> budget;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&#x3C;<span class="hljs-built_in">dynamic</span>> genres;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String?</span> homePage;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> id;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String?</span> imdbId;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> originalLanguage;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> originalTitle;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String?</span> overview;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">num</span> popularity;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String?</span> posterPath;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&#x3C;<span class="hljs-built_in">dynamic</span>> productionCompanies;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&#x3C;<span class="hljs-built_in">dynamic</span>> productionCountries;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">DateTime</span> releaseDate;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int?</span> revenue;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int?</span> runtime;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&#x3C;<span class="hljs-built_in">dynamic</span>> spokenLanguages;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> status;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String?</span> tagline;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> title;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">bool</span> video;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">num</span> voteAverage;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">dynamic</span> voteCount;

  <span class="hljs-keyword">const</span> MovieDetail(
      {<span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.adult,
      <span class="hljs-keyword">this</span>.backdropPath,
      <span class="hljs-keyword">this</span>.belongsToCollection,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.budget,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.genres,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.homePage,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.id,
      <span class="hljs-keyword">this</span>.imdbId,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.originalLanguage,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.originalTitle,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.overview,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.popularity,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.posterPath,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.productionCompanies,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.productionCountries,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.releaseDate,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.revenue,
      <span class="hljs-keyword">this</span>.runtime,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.spokenLanguages,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.status,
      <span class="hljs-keyword">this</span>.tagline,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.title,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.video,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.voteAverage,
      <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.voteCount});

  <span class="hljs-keyword">factory</span> MovieDetail.fromJson(<span class="hljs-built_in">Map</span>&#x3C;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>> json) {
    <span class="hljs-keyword">return</span> MovieDetail(
        adult: json[<span class="hljs-string">'adult'</span>],
        backdropPath: json[<span class="hljs-string">'backdrop_path'</span>],
        belongsToCollection: json[<span class="hljs-string">'belongs_to_collection'</span>],
        budget: json[<span class="hljs-string">'budget'</span>],
        genres: <span class="hljs-built_in">List</span>&#x3C;<span class="hljs-built_in">dynamic</span>>.from(json[<span class="hljs-string">'genres'</span>]),
        homePage: json[<span class="hljs-string">'home_page'</span>],
        id: json[<span class="hljs-string">'id'</span>],
        imdbId: json[<span class="hljs-string">'imdb_id'</span>],
        originalLanguage: json[<span class="hljs-string">'original_language'</span>],
        originalTitle: json[<span class="hljs-string">'original_title'</span>],
        overview: json[<span class="hljs-string">'overview'</span>],
        popularity: json[<span class="hljs-string">'popularity'</span>],
        posterPath: json[<span class="hljs-string">'poster_path'</span>],
        productionCompanies: json[<span class="hljs-string">'production_companies'</span>],
        productionCountries: json[<span class="hljs-string">'production_countries'</span>],
        releaseDate: <span class="hljs-built_in">DateTime</span>.parse(json[<span class="hljs-string">'release_date'</span>]),
        revenue: json[<span class="hljs-string">'revenuew'</span>],
        runtime: json[<span class="hljs-string">'runtime'</span>],
        spokenLanguages: json[<span class="hljs-string">'spoken_languages'</span>],
        status: json[<span class="hljs-string">'status'</span>],
        tagline: json[<span class="hljs-string">'tagline'</span>],
        title: json[<span class="hljs-string">'title'</span>],
        video: json[<span class="hljs-string">'video'</span>],
        voteAverage: json[<span class="hljs-string">'vote_average'</span>],
        voteCount: json[<span class="hljs-string">'vote_count'</span>]);
  }
}

</code></pre>
<h3>6. Modify the return type of the fetch functions</h3>
<p>How to modify the return type of the fetch functions has been covered in step 3.</p>
<pre><code class="hljs language-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:convert'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter_dotenv/flutter_dotenv.dart'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'package:http/http.dart'</span> <span class="hljs-keyword">as</span> http;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:my_app/models/movie_detail.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:my_app/models/movie_list.dart'</span>;

<span class="hljs-keyword">final</span> API_KEY = dotenv.<span class="hljs-keyword">get</span>(<span class="hljs-string">'API_KEY'</span>);

<span class="hljs-comment">// Future&#x3C;http.Response> fetchMovieList() async {</span>
<span class="hljs-comment">//   return http.get(</span>
<span class="hljs-comment">//       Uri.parse('https://api.themoviedb.org/3/movie/popular?api_key=$API_KEY'));</span>
<span class="hljs-comment">// }</span>

<span class="hljs-comment">// Future&#x3C;http.Response> fetchMovieDetail(dynamic movieId) async {</span>
<span class="hljs-comment">//   return http.get(Uri.parse(</span>
<span class="hljs-comment">//       'https://api.themoviedb.org/3/movie/$movieId?api_key=$API_KEY'));</span>
<span class="hljs-comment">// }</span>

<span class="hljs-comment">// Modify code created in the step 4 to the code below.</span>

Future&#x3C;MovieList> fetchMovieList() <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.<span class="hljs-keyword">get</span>(
      <span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'https://api.themoviedb.org/3/movie/popular?api_key=<span class="hljs-subst">$API_KEY</span>'</span>));

  <span class="hljs-keyword">if</span> (response.statusCode == <span class="hljs-number">200</span>) {
    <span class="hljs-keyword">return</span> MovieList.fromJson(jsonDecode(response.body));
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">throw</span> Exception(<span class="hljs-string">'Failed to load movies'</span>);
  }
}

Future&#x3C;MovieDetail> fetchMovieDetail(<span class="hljs-built_in">dynamic</span> movieId) <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.<span class="hljs-keyword">get</span>(<span class="hljs-built_in">Uri</span>.parse(
      <span class="hljs-string">'https://api.themoviedb.org/3/movie/<span class="hljs-subst">$movieId</span>?api_key=<span class="hljs-subst">$API_KEY</span>'</span>));

  <span class="hljs-keyword">if</span> (response.statusCode == <span class="hljs-number">200</span>) {
    <span class="hljs-keyword">return</span> MovieDetail.fromJson(jsonDecode(response.body));
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">throw</span> Exception(<span class="hljs-string">'Failed to load the movie detail.'</span>);
  }
}

</code></pre>
<h3>7. Create Pages</h3>
<p>In this step we will create pages that display a list of movie information and their individual details.
We also install <a href="https://docs.getwidget.dev/">Get Widget</a> in this step.</p>
<h4>Install <code>Get Widget</code></h4>
<pre><code class="hljs language-shell">flutter pub get getwidget
</code></pre>
<h4>Movie list Page</h4>
<p>To keep the movie list data in a widget, create it as a Stateful Widget.
In order to transition to the movie detail screen when tapped, the transition process should be executed on the <code>onTap</code> event.</p>
<pre><code class="hljs language-dart"><span class="hljs-comment">// lib/pages/movie_list.dart</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:getwidget/getwidget.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:my_app/main.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:my_app/apis/movie.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:my_app/models/movie_list.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MovieListPage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-keyword">const</span> MovieListPage({<span class="hljs-keyword">super</span>.key});

  <span class="hljs-meta">@override</span>
  State&#x3C;MovieListPage> createState() => _MovieListPageState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_MovieListPageState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&#x3C;<span class="hljs-title">MovieListPage</span>> </span>{
  <span class="hljs-keyword">late</span> Future&#x3C;MovieList> futureMovies;

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> initState() {
    <span class="hljs-keyword">super</span>.initState();
    futureMovies = fetchMovieList();
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
        backgroundColor: Colors.grey[<span class="hljs-number">800</span>],
        appBar: AppBar(
          title: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Movie List'</span>),
          backgroundColor: Colors.grey[<span class="hljs-number">900</span>],
        ),
        body: Center(
            child: FutureBuilder&#x3C;MovieList>(
          future: futureMovies,
          builder: (context, snapshot) {
            <span class="hljs-keyword">if</span> (snapshot.hasData) {
              <span class="hljs-keyword">return</span> GridView.count(
                  crossAxisCount: <span class="hljs-number">2</span>,
                  children: snapshot.data!.results
                      .map((e) => GestureDetector(
                          onTap: () => {
                                MyApp.router!
                                    .navigateTo((context), <span class="hljs-string">'/movies/<span class="hljs-subst">${e.id}</span>'</span>)
                              },
                          child: GFCard(
                            color: Colors.white,
                            boxFit: BoxFit.cover,
                            showOverlayImage: <span class="hljs-keyword">true</span>,
                            imageOverlay: NetworkImage(
                                <span class="hljs-string">'https://image.tmdb.org/t/p/w500<span class="hljs-subst">${e.posterPath}</span>'</span>),
                            title: GFListTile(
                              title: Text(
                                e.title,
                                style: <span class="hljs-keyword">const</span> TextStyle(
                                  color: Colors.white,
                                  fontWeight: FontWeight.bold,
                                ),
                              ),
                            ),
                          )))
                      .toList());
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (snapshot.hasError) {
              <span class="hljs-keyword">return</span> Text(<span class="hljs-string">'<span class="hljs-subst">${snapshot.error}</span>'</span>);
            }
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">const</span> CircularProgressIndicator();
          },
        )));
  }
}

</code></pre>
<h4>Movie detail page</h4>
<pre><code class="hljs language-dart"><span class="hljs-comment">// lib/pages/movie_detail.dart</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:getwidget/getwidget.dart'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'package:my_app/apis/movie.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:my_app/models/movie_detail.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MovieDetailPage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-keyword">const</span> MovieDetailPage({<span class="hljs-keyword">super</span>.key, <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.movieId});

  <span class="hljs-keyword">final</span> <span class="hljs-built_in">dynamic</span> movieId;

  <span class="hljs-meta">@override</span>
  State&#x3C;MovieDetailPage> createState() => _MovieDetailPageState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_MovieDetailPageState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&#x3C;<span class="hljs-title">MovieDetailPage</span>> </span>{
  <span class="hljs-keyword">late</span> Future&#x3C;MovieDetail> futureMovieDetail;

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> initState() {
    <span class="hljs-keyword">super</span>.initState();
    futureMovieDetail = fetchMovieDetail(widget.movieId);
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
        backgroundColor: Colors.grey[<span class="hljs-number">800</span>],
        appBar: AppBar(
          title: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Movie Detail'</span>),
          backgroundColor: Colors.grey[<span class="hljs-number">900</span>],
        ),
        body: Center(
            child: FutureBuilder&#x3C;MovieDetail>(
                future: futureMovieDetail,
                builder: ((context, snapshot) {
                  <span class="hljs-keyword">if</span> (snapshot.hasData) {
                    <span class="hljs-keyword">return</span> Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        GFImageOverlay(
                          image: NetworkImage(
                            <span class="hljs-string">'https://image.tmdb.org/t/p/w500<span class="hljs-subst">${snapshot.data!.posterPath!}</span>'</span>,
                          ),
                          height: <span class="hljs-number">200.0</span>,
                          boxFit: BoxFit.cover,
                        ),
                        Padding(
                            padding:
                                <span class="hljs-keyword">const</span> EdgeInsets.only(left: <span class="hljs-number">16.0</span>, right: <span class="hljs-number">16.0</span>),
                            child: Column(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: [
                                Padding(
                                    padding: <span class="hljs-keyword">const</span> EdgeInsets.only(top: <span class="hljs-number">16.0</span>),
                                    child: Text(snapshot.data!.title,
                                        style: <span class="hljs-keyword">const</span> TextStyle(
                                            fontSize: <span class="hljs-number">24.0</span>,
                                            color: Colors.white))),
                                GFRating(
                                  onChanged: (value) {},
                                  value:
                                      snapshot.data!.voteAverage.toDouble() / <span class="hljs-number">2</span>,
                                  size: <span class="hljs-number">16.0</span>,
                                ),
                                Padding(
                                    padding: <span class="hljs-keyword">const</span> EdgeInsets.only(top: <span class="hljs-number">16.0</span>),
                                    child: Text(snapshot.data!.overview!,
                                        style: <span class="hljs-keyword">const</span> TextStyle(
                                            color: Colors.white))),
                                Padding(
                                    padding: <span class="hljs-keyword">const</span> EdgeInsets.only(top: <span class="hljs-number">16.0</span>),
                                    child: Text(
                                      <span class="hljs-string">'Genres: <span class="hljs-subst">${snapshot.data!.genres.map((e) {
                                        return e[<span class="hljs-string">'name'</span>]!;
                                      }</span>).join('</span>, <span class="hljs-string">')}'</span>,
                                      style:
                                          <span class="hljs-keyword">const</span> TextStyle(color: Colors.white),
                                    ))
                              ],
                            ))
                      ],
                    );
                  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (snapshot.hasError) {
                    <span class="hljs-keyword">return</span> Text(<span class="hljs-string">'<span class="hljs-subst">${snapshot.error}</span>'</span>);
                  }
                  <span class="hljs-keyword">return</span> <span class="hljs-keyword">const</span> CircularProgressIndicator();
                }))));
  }
}

</code></pre>
<p>This is the end of the guide for the sample application.</p>
<h2>About SEO</h2>
<p>SEO performance is often required in web application development.
I investigated how SEO can be configured in Flutter.
I found the following information in the official documentation:</p>
<blockquote>
<p>In general, Flutter is geared towards dynamic application experiences. Flutter’s web support is no exception. Flutter web prioritizes performance, fidelity, and consistency. This means application output does not align with what search engines need to properly index. For web content that is static or document-like, we recommend using HTML—just like we do on flutter.dev, dart.dev, and pub.dev. You should also consider separating your primary application experience—created in Flutter—from your landing page, marketing content, and help content—created using search-engine optimized HTML.</p>
</blockquote>
<p>When considering SEO in your Flutter application, it is recommended that you use HTML.
Depending on your SEO requirements, you may want to avoid using Flutter if you want to improve the SEO performance of your site itself.</p>
<h2>Wrap up</h2>
<p>After creating an example app and researching Flutter on the Web, I felt that it is still less mature compared to the web-front competitors, such as React and Vue.
However, when the main development technology stack of an organization is Flutter/Dart, it is attractive to develop applications with it for better costs considerations.
I'll be keeping an eye on how Flutter continues to boost web development!</p>
<h2>Resources</h2>
<ul>
<li><a href="https://docs.flutter.dev/development/platform-integration/web/faq#search-engine-optimization-seo">Search Engine Optimization (SEO)</a></li>
<li><a href="https://docs.flutter.dev/cookbook/networking/fetch-data">Fetch data from the internet</a></li>
<li><a href="https://pub.dev/packages/flutter_dotenv">flutter_dotenv</a></li>
<li><a href="https://developers.themoviedb.org/">The Movie Database API</a></li>
<li><a href="https://docs.getwidget.dev/">Get Widget</a></li>
<li><a href="https://github.com/lukepighetti/fluro">Fluro</a></li>
<li><a href="https://medium.com/flutter-community/routing-in-flutter-using-fluro-bedb564cb737">Routing in Flutter using Fluro</a></li>
</ul>
<p>Article Photo by <a href="https://flutter.dev/multi-platform/web">Flutter Web</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[SQL wildcard operators and how to escape them]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/04/06/SQL-wildcard-operators-and-how-to-escape-them</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/04/06/SQL-wildcard-operators-and-how-to-escape-them</guid>
            <pubDate>Thu, 06 Apr 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>One of the most powerful features of SQL (Structured Query Language) is its ability to search and retrieve data from databases using wildcard operators. A wildcard character in SQL is used to replace a single or set of characters in any string, which can be very useful for comparing string data precisely. These operators are mostly used with the SQL LIKE operators. The LIKE operator and the WHERE clause can be used together to search data throughout the database.</p>
<h2>Content</h2>
<ul>
<li>SQL wildcard operators</li>
<li>An in-depth discussion on various wildcard operators with examples</li>
<li>Escaping wildcard operators</li>
</ul>
<h2>Introduction</h2>
<p>As a programmer working on a real-time application, we often need to search for data that needs to be manipulated in more ways than some simple exact matching query. The reason for this complex manipulation is usually to retrieve desired information with added filters.</p>
<p>Let's assume we have a user base integrated into our application and we need a suggestion list of those users whose username starts with ‘Ta’ or ends with ‘H’. In this kind of scenario, we can use wildcard operators to resolve complex queries. It is similar to Regular Expressions (Regex) where we search for patterns.</p>
<p>SQL Wildcard characters are special characters that represent any other character or a range of characters in a query. While these characters are useful for complex searches, they can also cause problems if not used correctly. This article will cover the different SQL wildcard characters and how to escape them when needed.</p>
<h2>SQL Wildcard Operators</h2>
<p>Below are the wildcard operators of SQL with examples</p>
<p><strong>% operator</strong></p>
<p>The % operator represents zero or more characters</p>
<p>For example the below query,</p>
<pre><code class="hljs language-swift"><span class="hljs-type">SELECT</span> <span class="hljs-operator">*</span> <span class="hljs-type">FROM</span> <span class="hljs-type">Users</span> <span class="hljs-type">WHERE</span> username <span class="hljs-type">LIKE</span> '<span class="hljs-type">Ta</span><span class="hljs-operator">%</span>' ;
</code></pre>
<p>Will find users whose username starts with <strong>Ta</strong> followed by zero or more characters after it, so it will find usernames like <strong>‘Tanvir’</strong> <strong>‘Tak’</strong> <strong>‘Ta’</strong></p>
<p><strong>_ operator</strong></p>
<p>The _ or underscore operator is used to represent exactly one character in a string.</p>
<p>For example,</p>
<pre><code class="hljs language-swift"><span class="hljs-type">SELECT</span> <span class="hljs-operator">*</span> <span class="hljs-type">FROM</span> <span class="hljs-type">Users</span> <span class="hljs-type">WHERE</span> username <span class="hljs-type">LIKE</span> '<span class="hljs-type">A_</span>' ;
</code></pre>
<p>The above query will find usernames that start with <strong>A</strong> and are followed by only one single character, so it will find usernames like <strong>‘An’</strong> <strong>‘Ab’</strong> and so on.</p>
<p><strong>[] operator</strong></p>
<p>The [ ] operator is used to represent any single character within the brackets</p>
<p>So the query</p>
<pre><code class="hljs language-swift"><span class="hljs-type">SELECT</span> <span class="hljs-operator">*</span> <span class="hljs-type">FROM</span> <span class="hljs-type">Users</span> <span class="hljs-type">WHERE</span> username <span class="hljs-type">LIKE</span> 't[ao]n' ;
</code></pre>
<p>Will find usernames like <strong>‘tan’</strong> and <strong>‘ton’</strong></p>
<p><strong>^ operator</strong></p>
<p>The ^ operator is used to represent any single character that is not within the brackets</p>
<p>So the query</p>
<pre><code class="hljs language-swift"> <span class="hljs-type">SELECT</span> <span class="hljs-operator">*</span> <span class="hljs-type">FROM</span> <span class="hljs-type">Users</span> <span class="hljs-type">WHERE</span> username <span class="hljs-type">LIKE</span> 't[<span class="hljs-operator">^</span>oe]n' ;
</code></pre>
<p>will find username <strong>‘tan’</strong> and not <strong>‘ton’</strong> or <strong>‘ten’</strong></p>
<p><strong>- operator</strong></p>
<p>The - operator will represent any single character that is within the range, below query</p>
<pre><code class="hljs language-swift"><span class="hljs-type">SELECT</span> <span class="hljs-operator">*</span> <span class="hljs-type">FROM</span> <span class="hljs-type">Users</span> <span class="hljs-type">WHERE</span> username <span class="hljs-type">LIKE</span> 't[a<span class="hljs-operator">-</span>c]n' ;
</code></pre>
<p>will find a user with the username <strong>‘tan’</strong>, <strong>‘tbn’</strong>, <strong>‘tcn’</strong></p>
<h2>Some other wildcard operators from different database</h2>
<p><strong>PostgreSQL and MySQL</strong></p>
<ul>
<li>% - zero or more characters</li>
<li>_ - single character</li>
</ul>
<p><strong>Oracle</strong></p>
<ul>
<li>% - zero or more characters</li>
<li>_ - means a single character</li>
<li>[] - single character within the brackets</li>
<li>{} - escaped character</li>
</ul>
<h2>Need of wildcard operators</h2>
<p>As we have seen from the above examples, SQL wildcard operators are extremely useful to search for complex data whether it's a simple string or a string of alphanumeric characters.</p>
<p>They play a vital role in speeding up queries and obtaining quick results.</p>
<p>For a real-time example let's look at the table below</p>





























































<table><thead><tr><th>id</th><th>First Name</th><th>Last Name</th><th>Username</th><th>Department</th></tr></thead><tbody><tr><td>1</td><td>Aamir</td><td>Hamza</td><td>aamir</td><td>Accounts</td></tr><tr><td>2</td><td>Ayesha</td><td>Naazier</td><td>ayesha</td><td>Accounts</td></tr><tr><td>3</td><td>Tanvir</td><td>Ahmed</td><td>tanvir</td><td>Engineering</td></tr><tr><td>4</td><td>Taskin</td><td>Rehman</td><td>taaskeen</td><td>Engineering</td></tr><tr><td>5</td><td>Arturo</td><td>Zehan</td><td>zehan</td><td>Engineering</td></tr><tr><td>6</td><td>Aamir</td><td>Waaleed</td><td>amir1</td><td>Engineering</td></tr><tr><td>7</td><td>Tanveer</td><td>Al-Rasheed</td><td>tanveer</td><td>Engineering</td></tr></tbody></table>
<p>Now, if we look at the table we will see, multiple names are duplicates by the first name but have key differences in username.</p>
<p>So if we have a search bar on our application where we want to see a list of <strong>Aamir’s</strong> by searching with their usernames,</p>
<p>We can simply execute a query</p>
<pre><code class="hljs language-swift"><span class="hljs-type">SELECT</span> <span class="hljs-operator">*</span> <span class="hljs-type">FROM</span> <span class="hljs-type">Users</span> <span class="hljs-type">WHERE</span> username <span class="hljs-type">LIKE</span> '<span class="hljs-type">A</span><span class="hljs-operator">%</span>' ;
</code></pre>
<p>and this query will make a list of users who have usernames starting with <strong>‘A’</strong> and will follow up with names like <strong>‘aamir’</strong>, <strong>‘amir’</strong>, <strong>‘amdad’</strong> etc.</p>
<p>Without using <strong>LIKE</strong> clause and wildcard operator pairing we could’ve gotten a <strong>“no user found”</strong> message from our DB.</p>
<h2>Why do we need to escape wildcard operators?</h2>
<p>Now so far we know, having wildcard operators in our search operation is a good thing, then why would there be a need to escape them?</p>
<p>Let's look at the above user table a bit differently now</p>





























































<table><thead><tr><th>id</th><th>First Name</th><th>Last Name</th><th>Username</th><th>Department</th></tr></thead><tbody><tr><td>1</td><td>Aamir</td><td>Hamza</td><td>aamir</td><td>Accounts</td></tr><tr><td>2</td><td>Ayesha</td><td>Naazier</td><td>ayesha</td><td>Accounts</td></tr><tr><td>3</td><td>Tanvir</td><td>Ahmed</td><td>tanvir</td><td>Engineering</td></tr><tr><td>4</td><td>Taskin</td><td>Rehman</td><td>taaskeen</td><td>Engineering</td></tr><tr><td>5</td><td>Arturo</td><td>Zehan</td><td>zeh%an</td><td>Engineering</td></tr><tr><td>6</td><td>Aamir</td><td>Waaleed</td><td>a-amir</td><td>Engineering</td></tr><tr><td>7</td><td>Tanveer</td><td>Al-Rasheed</td><td>t^nvir</td><td>Engineering</td></tr></tbody></table>
<p>If you look at the table closely, specially at the username column you will notice special characters which matches with some of the wildcard operators we were discussing earlier.</p>
<p>Now, what's wrong with those usernames? seems perfectly fine if we allow special characters in our DB for usernames right? But no, In a real-time situation when you are querying usernames similar to the table above using the LIKE clause, you might get a similar error like this.</p>
<pre><code class="hljs language-swift"> <span class="hljs-type">ERR_PARSE_ERROR</span>: <span class="hljs-type">You</span> have an error <span class="hljs-keyword">in</span> your <span class="hljs-type">SQL</span> syntax; check <span class="hljs-keyword">for</span> the right syntax to use near '\<span class="hljs-string">"%<span class="hljs-subst">\'</span>
</span></code></pre>
<p>So to escape this we can use a simple solution.</p>
<h2>Escaping wildcard operators</h2>
<p>In SQL, escaping a wildcard character is used to indicate that a wildcard character should be treated as a literal character rather than as a wildcard one. To escape a character we usually use the backslash ' \ ', but it may be different depending on the specific SQL database you are using.</p>
<p>Here's an example of how to use the escape character in a SQL find query:</p>
<pre><code class="hljs language-swift"><span class="hljs-type">SELECT</span> username
<span class="hljs-type">FROM</span> users
<span class="hljs-type">WHERE</span> username <span class="hljs-type">LIKE</span> '\<span class="hljs-operator">%</span>a<span class="hljs-operator">%</span>';
</code></pre>
<p>In this example, the escape character ' \ ' is used before the percent sign ' % ' in the pattern to indicate that the % symbol should be treated as a literal character and not a wildcard. This query will match usernames containing the string '%a' anywhere in the username, regardless of whether the username starts with a or % symbol.</p>
<p>Another solution if you are working with <strong>Typeorm</strong> and <strong>Nestjs</strong> (like I do) and taking dynamic input:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> { EntityManager, getConnection } from 'typeorm';

<span class="hljs-keyword">async</span> function findUsernamesWithSpecialCharacters(username: string) {
  const connection <span class="hljs-operator">=</span> <span class="hljs-keyword">await</span> getConnection();
  const entityManager <span class="hljs-operator">=</span> connection.createEntityManager();

  const usernames <span class="hljs-operator">=</span> <span class="hljs-keyword">await</span> entityManager
    .createQueryBuilder(<span class="hljs-type">User</span>, 'user')
    .where(<span class="hljs-string">"user.username LIKE :username"</span>, { username: `<span class="hljs-operator">%</span>${username}<span class="hljs-operator">%</span>` })
    .getMany();

  <span class="hljs-keyword">return</span> usernames;
}
</code></pre>
<p>Or another solution below where you can specify which wildcard operators you want to escape specifically:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> { EntityManager, getConnection } from 'typeorm';

<span class="hljs-keyword">async</span> function findUsernamesWithSpecialCharacters(username: string) {
  const connection <span class="hljs-operator">=</span> <span class="hljs-keyword">await</span> getConnection();
  const entityManager <span class="hljs-operator">=</span> connection.createEntityManager();
  <span class="hljs-keyword">let</span> escapedUsername <span class="hljs-operator">=</span> username.replace(<span class="hljs-operator">/</span>[<span class="hljs-operator">%</span><span class="hljs-keyword">_</span><span class="hljs-operator">^</span>]<span class="hljs-operator">/</span>g, '\\$<span class="hljs-operator">&#x26;</span>');
  escapedUsername <span class="hljs-operator">=</span> `<span class="hljs-operator">%</span>${escapedUsername}<span class="hljs-operator">%</span>`;

  const usernames <span class="hljs-operator">=</span> <span class="hljs-keyword">await</span> entityManager
    .createQueryBuilder(<span class="hljs-type">User</span>, 'user')
    .where(<span class="hljs-string">"user.username LIKE :username"</span>, { username: escapedUsername })
    .getMany();

  <span class="hljs-keyword">return</span> usernames;
}
</code></pre>
<p>Here in this part of the code <em>username.replace(/[%</em>^]/g, '\$&#x26;') specifically ‘/[%_^]/g’\ this part is giving you the customization you need for the wildcards.</p>
<p>One last solution if you follow a DTO-based standardized practice to code as we do at Monstarlab for all our Nodejs projects.</p>
<p>You simply put this solution in your input DTO and the wildcard operators will be taken care of:</p>
<pre><code class="hljs language-swift">  <span class="hljs-meta">@IsString</span>()
  <span class="hljs-meta">@IsNotEmpty</span>()
  <span class="hljs-meta">@MaxLength</span>(<span class="hljs-number">200</span>)
  <span class="hljs-meta">@Transform</span>(
    ({ value }: <span class="hljs-type">TransformFnParams</span>) <span class="hljs-operator">=></span>
      isString(value) <span class="hljs-operator">&#x26;&#x26;</span> value<span class="hljs-operator">?</span>.trim() <span class="hljs-operator">&#x26;&#x26;</span> value.replace(<span class="hljs-operator">/</span>[<span class="hljs-operator">%</span><span class="hljs-keyword">_</span><span class="hljs-operator">^</span>]<span class="hljs-operator">/</span>g, '\\\\$<span class="hljs-operator">&#x26;</span>'),
  )
  username: string;
</code></pre>
<p>Thank you for reading my article. I hope it will help you write sophisticated SQL queries.</p>
<h2>Resources</h2>
<ul>
<li><a href="https://www.tutorialspoint.com/sql/sql-like-clause.htm">SQL LIKE Operator</a></li>
<li><a href="https://www.tutorialspoint.com/sql/sql-wildcards.htm">SQL Wildcards</a></li>
<li><a href="https://typeorm.io/select-query-builder">Typeorm query builder</a></li>
<li><a href="https://www.thisdot.co/blog/combining-validators-and-transformers-in-nestjs">Class Transformer</a>_</li>
</ul>
<p><em>Article Photo by <a href="https://unsplash.com/photos/j5gCOKZdm6I">Ricardo Gomez Angel</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Migrating Obj-C/C++ module to SPM]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/03/29/Migrating-objc-module-to-spm</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/03/29/Migrating-objc-module-to-spm</guid>
            <pubDate>Wed, 29 Mar 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Swift Package Manager(SPM) provides a powerful system of modularizing Swift projects. With SPM, you can create separate modules for different parts of your codebase and manage their dependencies in a clean and efficient way. It could be done using the old way: adding new targets (modules) but that comes with a lot of project configuration which generally increases the project complexity.</p>
<p>SPM's brilliance lies in its simple and transparent Package.swift file that configures all modules, making it easy for developers to understand and manage dependencies, whether at the module-specific or overall level.</p>
<p>If you're working on a project that has been modularized the old way, you might be tasked with migrating previous static or target frameworks to Swift Package Manager (SPM). While SPM modularization is generally straightforward, restructuring your module is necessary in the case of a mixed language codebase, such as Swift/Obj-C/C++. To do this, you'll need to create individual targets for each programming language.</p>
<p>As our example for today, we will use a static framework, namely the PolygonKitStatic module. Its structure is ilustrated in the following figure.</p>
<figure>
<img src="/assets/img/articles/2023-03-29-Migrating-objc-module-to-spm/polygon-kit-static.webp">
<figcaption>Figure 1. PolygonKitStatic framework structure</figcaption>
</figure>
<h2>Step 1: Create the first Model SPM module</h2>
<p>Now, let's start with this example where you want to create your first Model module:</p>
<figure>
<img src="/assets/img/articles/2023-03-29-Migrating-objc-module-to-spm/model-framework.webp">
<figcaption>Figure 2. Model target</figcaption>
</figure>
<p>To start, we'll create a new SPM Model package by adding a library product, and a target creating a new folder under the Sources directory named Model.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> package <span class="hljs-operator">=</span> <span class="hljs-type">Package</span>(
    name: <span class="hljs-string">"Packages"</span>,
    products: [
        .library(
            name: <span class="hljs-string">"Model"</span>,
            targets: [<span class="hljs-string">"Model"</span>]),
    ],
    dependencies: [],
    targets: [
        .target(
            name: <span class="hljs-string">"Model"</span>),
        .testTarget(
            name: <span class="hljs-string">"ModelTests"</span>,
            dependencies: [<span class="hljs-string">"Model"</span>])
    ],
)
</code></pre>
<p>Then, we'll add the first file to our new SPM Model module - <code>PolygonPoint.swift</code> - by copying the existing class from the old PolygonPoint in the new Model SPM module.</p>
<p>However, the SPM won't compile just yet because we need the <code>PolygonKitStatic</code> library. To resolve this, we'll need to migrate this framework to an SPM module and import it into our new SPM Model.</p>
<figure>
<img src="/assets/img/articles/2023-03-29-Migrating-objc-module-to-spm/PolygonPoint.webp">
<figcaption>Figure 3. Adding PolygonPoint.swift file to SPM Model Package</figcaption>
</figure>
<h2>Step 2: Design mixed languages modules</h2>
<p>Before we proceed, let's analyze the contents of our <code>PolygonKitStatic</code> library represented in <strong>Figure 1. PolygonKitStatic framework structure</strong>
and consider how we can restructure it to fit within our new SPM module(s).</p>
<p>You’ve probably  noticed that our framework contains both Obj-C and C++ codebase, which means we need to restructure our PolygonKit module and separate it into at least two targets with their respective folders, as mixed languages cannot be contained in the same SPM target.</p>
<p>Adding to the complexity, the <code>PolygonKitStatic</code> framework directory also has a git submodule repo - <code>Polyutils</code> - which is written in C++ and imported into Obj-C interfaces, bridging its functionality to our <code>PolygonPoint</code> and <code>Polygon</code> Swift classes. This means that we will need to move our submodule from the PolygonKitStatic path to the new SPM module path. However, for now, let's set this aside and address it later in the post.</p>
<p>Given this context, it's clear that we'll need to define a separate module for the Polyutils repo, which contains only C++ codebase. We can then import it into our PolygonKit Obj-C target, which provides the interface for the PolygonKit Swift target.</p>
<p>Here is our new structure of SPM modules and their targets:</p>
<pre><code class="hljs language-swift">
<span class="hljs-operator">|---------</span><span class="hljs-type">Packages</span>
	<span class="hljs-operator">|------------</span><span class="hljs-type">README</span>
	<span class="hljs-operator">|------------</span><span class="hljs-type">Package</span>.swift
	<span class="hljs-operator">|------------</span><span class="hljs-type">Sources</span>
		<span class="hljs-operator">|------------</span><span class="hljs-type">Model</span>
			<span class="hljs-operator">…</span>
			<span class="hljs-operator">…</span>
			<span class="hljs-operator">…</span>
		<span class="hljs-operator">|------------</span><span class="hljs-type">PolyutilsPackage</span>
			<span class="hljs-operator">…</span>
			<span class="hljs-operator">…</span>
			<span class="hljs-operator">…</span>
		<span class="hljs-operator">|------------</span><span class="hljs-type">PolygonKitObjC</span>
			<span class="hljs-operator">…</span>
			<span class="hljs-operator">…</span>
			<span class="hljs-operator">…</span>
		<span class="hljs-operator">|------------</span><span class="hljs-type">PolygonKitSwift</span>
			<span class="hljs-operator">…</span>
			<span class="hljs-operator">…</span>
			<span class="hljs-operator">…</span>
        <span class="hljs-operator">|------------</span><span class="hljs-type">Tests</span>
</code></pre>
<h2>Step 3: Implement SPM modules</h2>
<p>Let's begin with the <code>PolyutilsPackage</code> module by adding its product, target to <code>Package.swift</code>  and folder in the Sources directory.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> package <span class="hljs-operator">=</span> <span class="hljs-type">Package</span>(
    name: <span class="hljs-string">"Packages"</span>,
    products: [
        .library(
            name: <span class="hljs-string">"Model"</span>,
            targets: [<span class="hljs-string">"Model"</span>]),
        .library(
            name: <span class="hljs-string">"PolyutilsPackage"</span>,
            targets: [<span class="hljs-string">"PolyutilsPackage"</span>]
        ),
    ],
    dependencies: [],
    targets: [
        .target(
            name: <span class="hljs-string">"Model"</span>),
        .target(
            name: <span class="hljs-string">"PolyutilsPackage"</span>,
            path: <span class="hljs-string">"Sources/PolyutilsPackage"</span>,
            exclude: [
                <span class="hljs-string">"polyutils/poly_jni.cpp"</span>,
                <span class="hljs-string">"polyutils/poly_jni.h"</span>
            ],
            sources: [
                <span class="hljs-string">"polyutils/"</span>,
                <span class="hljs-string">"include/"</span>
            ],
            publicHeadersPath: <span class="hljs-string">"include"</span>,
            cxxSettings: [
                .headerSearchPath(<span class="hljs-string">"polyutils"</span>),
            ]
        )
        .testTarget(
            name: <span class="hljs-string">"ModelTests"</span>,
            dependencies: [<span class="hljs-string">"Model"</span>])
    ],
)
</code></pre>
<p>Let’s address the git submodule issue now as we need its files for our <code>PolyutilsPackage</code> module. For the newer git version( > 1.8.5) there’s a git command (<code>git mv</code>) that does everything for you, we just need to specify the old path and the new path. Let’s execute it for our project submodule.</p>
<pre><code class="hljs language-Shell">git mv modern-swiftui/PolygonKitStatic/Native\ submodule/polyutils Sources/PolyutilsPackage 
</code></pre>
<p>After moving our submodule into place, it's apparent that our module is still not compiling. However, there are a couple of things we can do to rectify this.</p>
<p>Firstly, we need to create an "include" folder where we can add public headers that we want to expose to other modules. We can then create its header and implementation files: <code>PolyutilsKit.h</code> and <code>PolyutilsKit.m</code>.</p>
<p>Secondly, we must exclude a couple of unnecessary Java files from the Polyutils repo and set the publicHeadersPath to reference the "include" directory. Additionally, we need to set the C++ HeaderSearchPath to the <code>Polyutils</code> directory, as this is required for the <code>Polyutils</code> repo to compile.</p>
<p>It's important to note the significance of the <code>publicHeadersPath</code> - it specifies the directory that contains the public interface with the functionality we need to make public.</p>
<pre><code class="hljs language-swift">.target(
            name: <span class="hljs-string">"PolyutilsPackage"</span>,
            path: <span class="hljs-string">"Sources/PolyutilsPackage"</span>,
            exclude: [
                <span class="hljs-string">"polyutils/poly_jni.cpp"</span>,
                <span class="hljs-string">"polyutils/poly_jni.h"</span>
            ],
            sources: [
                <span class="hljs-string">"polyutils/"</span>,
                <span class="hljs-string">"include/"</span>
            ],
            publicHeadersPath: <span class="hljs-string">"include"</span>,
            cxxSettings: [
                .headerSearchPath(<span class="hljs-string">"polyutils"</span>),
            ]
        )
</code></pre>
<p>Voila! Our very first C++ SPM module is now successfully compiled and ready to be imported into other modules.</p>
<p>Next, let's move on to creating the <code>PolygonKitObjC</code> and <code>PolygonKitSwift</code> modules. Since Obj-C interfaces are only used to bridge C++ to Swift, we can create a single SPM library but with two separate targets - <code>PolygonKitObjC</code> and <code>PolygonKitSwift</code>.</p>
<p>Firstly, let's focus on <code>PolygonKitObjC</code>. Similar to C++, we need to make the interfaces public by creating an "include" directory. We should then specify this directory in the target's configuration by setting the <code>publicHeadersPath</code> property.
Additionally, we need to add our PolyutilsPackage as a dependency so that we can use it in the Obj-C implementation. Let's add it to the Package.swift manifest file and include the PolygonKitObjC directory, along with its necessary files, in the Sources directory.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> package <span class="hljs-operator">=</span> <span class="hljs-type">Package</span>(
    name: <span class="hljs-string">"Packages"</span>,
    products: [
        .library(
            name: <span class="hljs-string">"Model"</span>,
            targets: [<span class="hljs-string">"Model"</span>]),
        .library(
            name: <span class="hljs-string">"PolygonKitPackage"</span>,
            targets: [<span class="hljs-string">"PolygonKitObjC"</span>, <span class="hljs-string">"PolygonKitSwift"</span>]),
        .library(
            name: <span class="hljs-string">"PolyutilsPackage"</span>,
            targets: [<span class="hljs-string">"PolyutilsPackage"</span>]
        ),
    ],
    dependencies: [],
    targets: [
        .target(
            name: <span class="hljs-string">"Model"</span>),
        .target(
            name: <span class="hljs-string">"PolygonKitObjC"</span>,
            dependencies: [
                <span class="hljs-string">"PolyutilsPackage"</span>,
            ],
            path: <span class="hljs-string">"Sources/PolygonKitObjC"</span>,
            sources: [
                <span class="hljs-string">"Models/"</span>,
                <span class="hljs-string">"Private/"</span>,
                <span class="hljs-string">"include/"</span>
            ],
            publicHeadersPath: <span class="hljs-string">"include"</span>
        ),
        .target(
            name: <span class="hljs-string">"PolygonKitSwift"</span>,
            dependencies: [
                <span class="hljs-string">"PolygonKitObjC"</span>,
                <span class="hljs-string">"PolyutilsPackage"</span>,
            ]
        ),
        .target(
            name: <span class="hljs-string">"PolyutilsPackage"</span>,
            path: <span class="hljs-string">"Sources/PolyutilsPackage"</span>,
            exclude: [
                <span class="hljs-string">"polyutils/poly_jni.cpp"</span>,
                <span class="hljs-string">"polyutils/poly_jni.h"</span>
            ],
            sources: [
                <span class="hljs-string">"polyutils/"</span>,
                <span class="hljs-string">"include/"</span>
            ],
            publicHeadersPath: <span class="hljs-string">"include"</span>,
            cxxSettings: [
                .headerSearchPath(<span class="hljs-string">"polyutils"</span>),
            ]
        )
        .testTarget(
            name: <span class="hljs-string">"ModelTests"</span>,
            dependencies: [<span class="hljs-string">"Model"</span>])
    ],
)
</code></pre>
<p>To complete our task, we need to add the last piece of the puzzle: <code>PolygonKitSwift</code>. Since it is a Swift-only module, configuring its target is relatively straightforward - we just need to set the target properties for its name and dependencies.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> package <span class="hljs-operator">=</span> <span class="hljs-type">Package</span>(
    name: <span class="hljs-string">"Packages"</span>,
    products: [
        .library(
            name: <span class="hljs-string">"Model"</span>,
            targets: [<span class="hljs-string">"Model"</span>]),
        .library(
            name: <span class="hljs-string">"PolygonKitPackage"</span>,
            targets: [<span class="hljs-string">"PolygonKitObjC"</span>, <span class="hljs-string">"PolygonKitSwift"</span>]),
        .library(
            name: <span class="hljs-string">"PolyutilsPackage"</span>,
            targets: [<span class="hljs-string">"PolyutilsPackage"</span>]
        ),
    ],
    dependencies: [],
    targets: [
        .target(
            name: <span class="hljs-string">"Model"</span>),
        .target(
            name: <span class="hljs-string">"PolygonKitObjC"</span>,
            dependencies: [
                <span class="hljs-string">"PolyutilsPackage"</span>,
            ],
            path: <span class="hljs-string">"Sources/PolygonKitObjC"</span>,
            sources: [
                <span class="hljs-string">"Models/"</span>,
                <span class="hljs-string">"Private/"</span>,
                <span class="hljs-string">"include/"</span>
            ],
            publicHeadersPath: <span class="hljs-string">"include"</span>
        ),
        .target(
            name: <span class="hljs-string">"PolygonKitSwift"</span>,
            dependencies: [
                <span class="hljs-string">"PolygonKitObjC"</span>,
                <span class="hljs-string">"PolyutilsPackage"</span>,
            ]
        ),
        .target(
            name: <span class="hljs-string">"PolyutilsPackage"</span>,
            path: <span class="hljs-string">"Sources/PolyutilsPackage"</span>,
            exclude: [
                <span class="hljs-string">"polyutils/poly_jni.cpp"</span>,
                <span class="hljs-string">"polyutils/poly_jni.h"</span>
            ],
            sources: [
                <span class="hljs-string">"polyutils/"</span>,
                <span class="hljs-string">"include/"</span>
            ],
            publicHeadersPath: <span class="hljs-string">"include"</span>,
            cxxSettings: [
                .headerSearchPath(<span class="hljs-string">"polyutils"</span>),
            ]
        )
        .testTarget(
            name: <span class="hljs-string">"ModelTests"</span>,
            dependencies: [<span class="hljs-string">"Model"</span>])
    ],
)
</code></pre>
<p>We've completed the setup for all of our SPM modules. The final step is to move the <code>Polygon.swift</code> and <code>PolygonPoint.swift</code> files to the <code>PolygonKitSwift</code> directory, and create the classes where we can import <code>PolyutilsPackage</code> and <code>PolygonKitObjC</code>. This allows us to use the functionality from both packages in our Swift module. With everything set up correctly, we can now build and run our project to test the new SPM modules.</p>
<p>And there you have it! Say goodbye to hidden configuration files for project modules. With everything set up in the Package.swift manifest file, our libraries can now be easily tested and compiled in isolation.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to set up Firebase dSYM upload for an iOS project with multiple environments (Xcode 14)]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/03/01/How-to-set-up-Firebase-dSYM-files-upload-for-a-multiple-environment-project</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/03/01/How-to-set-up-Firebase-dSYM-files-upload-for-a-multiple-environment-project</guid>
            <pubDate>Wed, 01 Mar 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>If you ever used Firebase Crashlytics for an iOS app's crash reports, chances are you also encountered the “missing dSYM files” warning in the Firebase console. The crash reports are basically useless without these files.</p>
<p>The Firebase Crashlytics documentation only guides you on how to set up Firebase and the dSYM upload for a single environment project. Real-life projects are usually deployed to two environments at a minimum though. And you really want to assign two different Firebase apps to these environments not to pollute your production data. It took me some time to figure out how to do this properly. Because even if you use the official Firebase configuration APIs, you can still mess up.</p>
<p>In this article, I will explain how to properly set up a multi-environment Xcode project for Firebase and correctly upload the dSYM files to their Firebase apps counterparts.</p>
<p>I will also give a brief overview of what dSYM files are, how Xcode 14 changed where we find the dSYMs and how to debug the dSYM upload.</p>
<p><img src="/assets/img/articles/2023-03-01-How-to-set-up-Firebase-dSYM-upload-for-an-iOS-project/missing_dsym.webp" alt=""></p>
<h2>What are dSYM files?</h2>
<p>dSYM files, or debug symbol files are files generated during the code compilation process. You can think of them as a map for the compiled binary. If a crash occurs in the app, the dSYMs will tell you in which part of the code the crash occurred. Crashlytics needs them to display a human-readable crash report. A crash report is not of much use if you do not know where and why the app crashed.
You can find the dSYM files in the dSYM folder inside the <code>xcarchive</code> file generated by Xcode during archiving.</p>
<p><img src="/assets/img/articles/2023-03-01-How-to-set-up-Firebase-dSYM-upload-for-an-iOS-project/archive_structure.webp" alt=""></p>
<h2>How to upload the dSYMs to Firebase</h2>
<p>You need to find a way to upload these files to Crashlytics. You can either do that manually, using a build phase script or some custom script solution (in your CI/CD preferably). I will explain how to do this with a build phase script which is probably the best method.
Using the build phase script, you are sure that every time you compile your code, the generated dSYMs are uploaded to Crashlytics (if you have an internet connection working).</p>
<p>The build phase script makes use of the <code>run</code> and <code>upload-symbols</code> scripts inside the Crashlytics package. You can check those for some additional information on how the upload works.</p>
<p><img src="/assets/img/articles/2023-03-01-How-to-set-up-Firebase-dSYM-upload-for-an-iOS-project/module_script.webp" alt=""></p>
<p>Using a build phase script is also recommended in the <a href="https://firebase.google.com/docs/crashlytics/get-started?platform=ios">Crashlytics setup docs</a>.</p>
<h2>What changed with Xcode 14?</h2>
<p>Xcode 14 release <a href="https://developer.apple.com/documentation/xcode-release-notes/xcode-14-release-notes#Deprecations">deprecated bitcode</a>. Bitcode was a big deal for dSym files. If your app used bitcode, the locally generated dSym files were basically useless. Once the bitcode-enabled app was uploaded to TestFlight, Apple recompiled the .ipa file and thus created brand new dSYM files. If you wanted to get crash reports from TestFlight and App Store, you needed to find a way to download these dSYM files from App Store Connect and upload them to Crashlytics. Usually, you would set up a CI/CD step to do that or make use of something like <a href="https://docs.fastlane.tools/actions/download_dsyms/">fastlane download action</a>.</p>
<p>All of these solutions are now failing because there are no dSYM files to be found on App Store Connect anymore.</p>
<p>On the brighter side, bitcode deprecation actually makes the dSYM upload problem easier now. You can always just upload the locally created dSYM files and be sure you are not missing anything.</p>
<h2>How to set up Xcode to use multiple Firebase apps</h2>
<p>Let's see how to set up your project for multiple Firebase apps now. I will show you the wrong way first.</p>
<h3>The wrong way</h3>
<p>The official <a href="https://firebase.google.com/docs/ios/setup">Firebase docs</a> only mention how to set up the project for one Firebase app.
To add more Firebase apps, you need to add more <code>GoogleService-Info.plist</code> config files to the project and find a way to provide the right plist during a particular Xcode configuration.
The logical solution here would be to use the official Firebase APIs and configure Firebase in code similar to this (pardon the force unwrapping):</p>
<p><img src="/assets/img/articles/2023-03-01-How-to-set-up-Firebase-dSYM-upload-for-an-iOS-project/multiple_plists.webp" alt=""></p>
<pre><code class="hljs language-swift"># <span class="hljs-type">DO</span> <span class="hljs-type">NOT</span> <span class="hljs-type">DO</span> <span class="hljs-type">THIS</span>. <span class="hljs-type">THIS</span> <span class="hljs-type">IS</span> <span class="hljs-type">WRONG</span> :)

<span class="hljs-keyword">var</span> plistPath <span class="hljs-operator">=</span> <span class="hljs-type">Bundle</span>.main.path(forResource: <span class="hljs-string">"GoogleService-Info-Production"</span>, ofType: <span class="hljs-string">"plist"</span>)

<span class="hljs-keyword">#if</span> <span class="hljs-type">STAGING</span>
plistPath <span class="hljs-operator">=</span> <span class="hljs-type">Bundle</span>.main.path(forResource: <span class="hljs-string">"GoogleService-Info-Staging"</span>, ofType: <span class="hljs-string">"plist"</span>)
<span class="hljs-keyword">#elseif</span> <span class="hljs-type">DEBUG</span>
plistPath <span class="hljs-operator">=</span> <span class="hljs-type">Bundle</span>.main.path(forResource: <span class="hljs-string">"GoogleService-Info-Debug"</span>, ofType: <span class="hljs-string">"plist"</span>)
<span class="hljs-keyword">#endif</span>
        
<span class="hljs-type">FirebaseApp</span>.configure(options: .<span class="hljs-keyword">init</span>(contentsOfFile: plistPath<span class="hljs-operator">!</span>)<span class="hljs-operator">!</span>)
</code></pre>
<p>All should be fine, right? <strong>WRONG!</strong> :)</p>
<p>I also made the mistake of configuring several iOS apps this way and then wondering where my dSYMs are when I started to get the warning in the Firebase console.</p>
<p>The problem with the configuration in code is that the Firebase app is configured with the correct <code>GoogleService-Info.plist</code> during the app <strong>run time</strong>. But the build phase script to upload the dSYMs is run during the <strong>compilation time</strong>. During the compilation time, Firebase has no idea which configuration to use and it usually defaults to the production <code>GoogleService-Info.plist</code> file if your app bundle contains it or it fails if you renamed the file.</p>
<p>As a consequence, all of your dSYM files are always uploaded only to one Firebase app and the other ones completely miss all the dSYMs.
On top of that, all the analytics events that are triggered before the <code>FirebaseApp.configure()</code> is run, are sent to the wrong (or none) Firebase app too.</p>
<h3>The right way</h3>
<p>The solution to this problem is to provide the right <code>GoogleService-Info.plist</code> file <strong>during the app compilation</strong> before the dSYM upload script is executed.
There are three steps to this:</p>
<ol>
<li>Take out all the plists from the app target so they are not included in the app bundle after archiving.</li>
</ol>
<p><img src="/assets/img/articles/2023-03-01-How-to-set-up-Firebase-dSYM-upload-for-an-iOS-project/plist_no_target.webp" alt=""></p>
<ol start="2">
<li><a href="https://developer.apple.com/documentation/xcode/running-custom-scripts-during-a-build?changes=_8">Make a separate build phase script</a> to copy the correct plist file to the root of your app’s bundle.</li>
</ol>
<p>Put this build phase script before the dSYM upload build phase script. You will need to change the parts that are labeled with CHANGE.</p>
<pre><code class="hljs language-bash">
<span class="hljs-comment"># CHANGE</span>
<span class="hljs-comment"># The folder where you store the GoogleService-Info.plists for different app configurations</span>
PLISTS_FOLDER_PATH=<span class="hljs-string">"<span class="hljs-variable">${PROJECT_DIR}</span>/App/Resources/Firebase"</span>

<span class="hljs-comment"># CHANGE</span>
<span class="hljs-comment"># The default plist to use - probably the production plist</span>
PLIST_TO_COPY=<span class="hljs-string">"<span class="hljs-variable">${PLISTS_FOLDER_PATH}</span>/GoogleService-Info.plist"</span>

<span class="hljs-comment"># Switch the app configurations and decide which plist to copy</span>
<span class="hljs-keyword">case</span> <span class="hljs-string">"<span class="hljs-variable">${CONFIGURATION}</span>"</span> <span class="hljs-keyword">in</span>
    <span class="hljs-comment"># CHANGE the configuration and plist names</span>
   <span class="hljs-string">"Debug Development"</span> | <span class="hljs-string">"Release Development"</span> )
        PLIST_TO_COPY=<span class="hljs-string">"<span class="hljs-variable">${PLISTS_FOLDER_PATH}</span>/GoogleService-Info-Debug.plist"</span>
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"🔥 Firebase configured for the DEVELOPMENT configuration"</span>
        ;;
         
    <span class="hljs-comment"># CHANGE the configuration and plist names</span>
   <span class="hljs-string">"Debug Staging"</span> | <span class="hljs-string">"Release Staging"</span> )
        PLIST_TO_COPY=<span class="hljs-string">"<span class="hljs-variable">${PLISTS_FOLDER_PATH}</span>/GoogleService-Info-Staging.plist"</span>
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"🔥 Firebase configured for STAGING environment"</span>
        ;;

    <span class="hljs-comment"># Default = production         </span>
    *)
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"🔥 Firebase configured for PRODUCTION environment"</span>
        ;;
<span class="hljs-keyword">esac</span>

<span class="hljs-built_in">echo</span> <span class="hljs-string">"🔥 FIREBASE CONFIG FILE COPIED TO THE APP BUNDLE: <span class="hljs-variable">$PLIST_TO_COPY</span>"</span>
<span class="hljs-comment"># The plist is copied straight to the root folder and renamed to the default name</span>
<span class="hljs-comment"># Having only this one plist in the app bundle - Firebase will use this plist for its config</span>
cp -r <span class="hljs-variable">$PLIST_TO_COPY</span> <span class="hljs-string">"<span class="hljs-variable">${BUILT_PRODUCTS_DIR}</span>/<span class="hljs-variable">${PRODUCT_NAME}</span>.app/GoogleService-Info.plist"</span>
</code></pre>
<p><img src="/assets/img/articles/2023-03-01-How-to-set-up-Firebase-dSYM-upload-for-an-iOS-project/build_phase_xcode.webp" alt=""></p>
<p>You can find the configuration names in your project's build settings.</p>
<p><img src="/assets/img/articles/2023-03-01-How-to-set-up-Firebase-dSYM-upload-for-an-iOS-project/build_configs.webp" alt=""></p>
<ol start="3">
<li>Just configure the app with <code>FirebaseApp.configure()</code> as usual.
There will only be one <code>GoogleService-Info.plist</code> file in the bundle, so you do not need to specify it anymore.</li>
</ol>
<p>You can spin up your proxy now and check the right dSYMs are uploaded to the right Firebase apps for different build configurations. This approach works for your CI/CD as well. You do not need to set a custom workflow to upload the dSYMs from there.</p>
<h2>How to debug the dSYM upload</h2>
<p>If you want to know that the dSYM build phase upload works fine, you can try to build your app and check the build log in the Report Navigator. The build log should give you a short success message.</p>
<p><img src="/assets/img/articles/2023-03-01-How-to-set-up-Firebase-dSYM-upload-for-an-iOS-project/build_phase_log.webp" alt=""></p>
<p>This message tells you that the build script successfully found some Firebase configuration, the generated dSYM files, and started a background task that should upload them.
Things can still go south from here though. If you want to know if and to which app the dSYMs were uploaded, you can start a proxy (like Charles or Proxyman) and check the upload request.</p>
<p><img src="/assets/img/articles/2023-03-01-How-to-set-up-Firebase-dSYM-upload-for-an-iOS-project/charles_upload.webp" alt=""></p>
<p>You should see the dSYMs uploaded a few seconds after the app compiles. The request query contains the Firebase app ID which you can compare with what you see in the Firebase Console.
By checking all of these, you can be sure things are configured correctly on your side.</p>
<p><img src="/assets/img/articles/2023-03-01-How-to-set-up-Firebase-dSYM-upload-for-an-iOS-project/firebase_app.webp" alt=""></p>
<h2>Key takeaways</h2>
<ul>
<li>dSYM files are no longer to be found on App Store Connect.</li>
<li>All dSYM files necessary for Crashlytics are now produced during the local (your machine or CI/CD) code compilation.</li>
<li>If you want to make sure your app is configured properly, rely on copying the correct GoogleService-Info.plist into the app bundle during the compilation.</li>
<li>If you configure the app with the correct plist during run time, you will lose some dSYMs and certain early analytics.</li>
</ul>
<p><em>Article Photo by <a href="https://unsplash.com/@timothycdykes">Timothy Dykes</a> on <a href="https://unsplash.com/">Unsplash</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introduction to Kotlin Coroutines for Android]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/01/06/Introduction-to-Kotlin-Coroutines-for-Android</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/01/06/Introduction-to-Kotlin-Coroutines-for-Android</guid>
            <pubDate>Fri, 06 Jan 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Asynchronous programming is a technique used to perform long-running tasks without blocking the main thread of an application. This is important in Android, where the main thread is responsible for rendering the user interface and handling user input. Traditionally, asynchronous tasks have been implemented using callbacks, which can make the code complex and hard to understand.</p>
<p>Coroutines offer a simpler and more readable alternative to callbacks for asynchronous programming in Android Kotlin. By using coroutines, you can write asynchronous code that looks and behaves like synchronous code, giving you more control over the flow of your program.</p>
<p>In this article, we'll explore what coroutines are and how they work in Android Kotlin. We'll also look at some of the benefits of using coroutines and some best practices for implementing them in your projects. By the end of this article, you'll have a solid understanding of how to use coroutines to write clean and efficient asynchronous code in Android Kotlin.</p>
<p>Let's look at an example of implementing a small asynchronous task in an Android application using coroutines. This will help us understand how to use coroutines to write clean and efficient code for asynchronous tasks in Android.</p>
<h2>Quick start</h2>
<p>To use coroutines in your Android project, you will need to add the following dependency to your build.gradle file:</p>
<pre><code class="hljs language-Kotlin">implementation <span class="hljs-string">"org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"</span>
</code></pre>
<p>Once you have added the dependency, you can start using coroutines in your project by adding the suspend keyword to the function you want to make asynchronous.
For example, let's say you want to make a network call to fetch some data from a server.
With coroutines, you can do this as follows:</p>
<pre><code class="hljs language-Kotlin"><span class="hljs-keyword">suspend</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">fetchData</span><span class="hljs-params">()</span></span>: Data {
    <span class="hljs-keyword">return</span> withContext(Dispatchers.IO) {
        <span class="hljs-comment">// Make network call here</span>
        <span class="hljs-keyword">val</span> response = api.fetchData()
        <span class="hljs-comment">// Return the result</span>
        response.<span class="hljs-keyword">data</span>
    }
}
</code></pre>
<p>As you can see, the "fetchData" function is marked with the "suspend" keyword, which tells the compiler that this function can be suspended and resumed at certain
points. The "withContext" function is used to specify the context in which the code should be executed, in this case, the IO dispatcher, which is a thread pool
designed for blocking IO operations.</p>
<p>To use the fetchData function, you will need to call it from another suspend function or from a coroutine.
You can do this using the launch function from the CoroutineScope class:</p>
<pre><code class="hljs language-Kotlin"><span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">loadData</span><span class="hljs-params">()</span></span> {
    CoroutineScope(Dispatchers.Main).launch {
        <span class="hljs-keyword">val</span> <span class="hljs-keyword">data</span> = fetchData()
        <span class="hljs-comment">// Use the data</span>
    }
}
</code></pre>
<p>The launch function creates a new coroutine and starts it immediately.
The Dispatchers.Main parameter specifies that the code inside the coroutine should be executed on the main thread, which is necessary because you can't update the
UI from a background thread.</p>
<p>With these few lines of code, you can easily make asynchronous network calls and update your UI with the results, all without using any callbacks.
Coroutines make it much easier to write clean and readable code, and are a great tool to have in your Android development toolkit.</p>
<p>Now is the time to go beyond the surface level and really get to know Kotlin coroutines by examining them more closely.</p>
<h2>What do we need in order to use coroutines?</h2>
<p><img src="/assets/img/articles/2023-01-06-Introduction-to-Kotlin-Coroutines-for-Android/image01.webp" alt=""></p>
<ol>
<li>A <strong>dependency</strong> on the kotlinx.coroutines library. You need to add the following dependency to your build.gradle file to use coroutines in your Android project: <em>"implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'"</em></li>
<li>A <strong>scope</strong> where the coroutine is going to live. Scopes define the boundaries for the execution of a coroutine. When the object (e.g. an Activity) is destroyed, the coroutines tied to that object will be automatically cancelled.</li>
<li>A <strong>dispatcher</strong> (also known as a context) that decides on which thread the coroutine will execute. Dispatchers can be passed as a parameter to coroutine builders.</li>
<li><strong>Coroutine builders</strong> that are responsible for building and, in some cases, launching the coroutine.</li>
<li>A <strong>job</strong> that represents asynchronous work, such as fetching data.</li>
<li>Optionally, a <strong>structured concurrency</strong> design pattern, such as a supervisor job, to manage the lifetime of your coroutines.</li>
</ol>
<p>Let's start from scopes.</p>
<h2>Coroutine scopes</h2>
<p>There are several built-in scopes that you can use in Kotlin, such as:</p>
<ul>
<li><strong>Global scope</strong> is a scope that is not tied to the lifetime of any particular object. It is the default scope used when you launch a coroutine using the launch function from the kotlinx.coroutines package. But coroutines launched in the GlobalScope continue to run even after the completion of the coroutine builder's block, and they are not cancelled by the system unless you explicitly cancel them or terminate the entire coroutine context in which they are running. Here is an example of how you can launch a coroutine in the GlobalScope:</li>
</ul>
<pre><code class="hljs language-Kotlin"><span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    GlobalScope.launch {
        <span class="hljs-comment">// launch a new coroutine in background and continue</span>
        delay(<span class="hljs-number">1000L</span>) <span class="hljs-comment">// non-blocking delay for 1 second</span>
        println(<span class="hljs-string">"World!"</span>) <span class="hljs-comment">// print after delay</span>
    }
    println(<span class="hljs-string">"Hello,"</span>) <span class="hljs-comment">// main thread continues while coroutine is delayed</span>
    Thread.sleep(<span class="hljs-number">2000L</span>) <span class="hljs-comment">// block main thread for 2 seconds to keep JVM alive</span>
}
</code></pre>
<p>You could try it here: <a href="https://pl.kotl.in/Bbe35HhM8">https://pl.kotl.in/Bbe35HhM8</a></p>
<ul>
<li><strong>LifecycleScope</strong> is a scope that is tied to the lifecycle of any Lifecycle object, such as an Activity or a Fragment. Coroutines launched in this scope are automatically cancelled when the associated Lifecycle object is destroyed. This is useful for cancelling ongoing background tasks when the user navigates away from the associated UI. Here is example:</li>
</ul>
<pre><code class="hljs language-Kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyFragment</span> : <span class="hljs-type">Fragment</span></span>() {
    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onViewCreated</span><span class="hljs-params">(view: <span class="hljs-type">View</span>, savedInstanceState: <span class="hljs-type">Bundle</span>?)</span></span> {
        <span class="hljs-keyword">super</span>.onViewCreated(view, savedInstanceState)
        lifecycleScope.launch {
            <span class="hljs-comment">// launch a new coroutine in the scope of this fragment's view lifecycle</span>
            delay(<span class="hljs-number">1000L</span>) <span class="hljs-comment">// non-blocking delay for 1 second (default time unit is ms)</span>
            println(<span class="hljs-string">"Data loaded"</span>) <span class="hljs-comment">// print after delay</span>
        }
    }
}
</code></pre>
<ul>
<li><strong>ViewModelScope</strong> is a scope that is tied to the lifecycle of a ViewModel. Coroutines launched in this scope are automatically cancelled when the ViewModel is destroyed, which typically happens when the associated UI is no longer visible to the user. This is useful for cancelling ongoing background tasks when the user navigates away from the UI. For example:</li>
</ul>
<pre><code class="hljs language-Kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyViewModel</span> : <span class="hljs-type">ViewModel</span></span>() {
    <span class="hljs-keyword">init</span> {
        viewModelScope.launch {
            <span class="hljs-comment">// launch a new coroutine in the scope of this viewModel</span>
            delay(<span class="hljs-number">1000L</span>) <span class="hljs-comment">// non-blocking delay for 1 second</span>
            println(<span class="hljs-string">"Data loaded"</span>) <span class="hljs-comment">// print after delay</span>
        }
    }
}
</code></pre>
<p>Once you have decided on the scope in which you want to launch your coroutine, the next step is to choose the context or dispatcher that will determine on which thread the coroutine will execute.</p>
<h2>Dispatchers</h2>
<p>In Kotlin, dispatchers are represented by the CoroutineDispatcher interface, which defines a single dispatch function that is responsible for executing a given piece of code on a specific thread. There are several built-in dispatchers that you can use, such as:</p>
<ul>
<li>
<p><strong>Dispatchers.Main:</strong> This dispatcher is used to execute coroutines on the main thread of the app. It is typically used to update the UI or perform other tasks that need to run on the main thread.</p>
</li>
<li>
<p><strong>Dispatchers.Default:</strong> This dispatcher is used to execute coroutines on a shared background thread. It is suitable for tasks that perform blocking operations or computationally intensive work.</p>
</li>
<li>
<p><strong>Dispatchers.IO:</strong> This dispatcher is used to execute coroutines that perform blocking IO operations, such as reading from or writing to a file or network socket.</p>
</li>
<li>
<p><strong>Dispatchers.Unconfined:</strong> This dispatcher is used to execute a coroutine in the current thread, but only until the first suspension point. After the first suspension, the coroutine will resume in the thread that is used by the corresponding continuation.</p>
</li>
</ul>
<p>You can also create your own custom dispatchers by using the <strong>Dispatchers.newSingleThreadContext</strong> or <strong>Dispatchers.newFixedThreadPoolContext</strong> functions.</p>
<p>Which dispatcher you should use will depend on the specific needs of your app. In general, it is a good idea to use the most appropriate dispatcher for the type of work that you are doing, to ensure that your coroutines run efficiently and do not block the main thread.</p>
<p>After determining the scope and the dispatcher, you can proceed to creating and starting the coroutine.</p>
<h2>Coroutine builders</h2>
<p>In Kotlin coroutines, a <strong>"coroutine builder"</strong> is a function that creates and starts a new coroutine.
There are several coroutine builders provided by the Kotlin coroutines library, including:</p>
<ul>
<li><strong>launch:</strong> This function creates and starts a new coroutine. It returns a Job object that can be used to manage the lifecycle of the coroutine.</li>
</ul>
<pre><code class="hljs language-Kotlin"><span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> = runBlocking {
    <span class="hljs-keyword">val</span> job = launch {
        <span class="hljs-comment">// launch a new coroutine and keep a reference to its Job</span>
        delay(<span class="hljs-number">1000L</span>)
        println(<span class="hljs-string">"World!"</span>)
    }
    println(<span class="hljs-string">"Hello,"</span>)
    job.join() <span class="hljs-comment">// wait until the coroutine completes</span>
}
</code></pre>
<ul>
<li><strong>async:</strong> This function creates and starts a new coroutine that runs asynchronously. It returns a Deferred object that can be used to retrieve the result of the coroutine when it completes.</li>
</ul>
<pre><code class="hljs language-Kotlin"><span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> = runBlocking {
    <span class="hljs-keyword">val</span> result = async {
        <span class="hljs-comment">// create and start a new coroutine</span>
        delay(<span class="hljs-number">1000L</span>)
        <span class="hljs-string">"Hello, World!"</span>
    }
    println(result.await()) <span class="hljs-comment">// retrieve the result of the coroutine</span>
}
</code></pre>
<ul>
<li><strong>runBlocking:</strong> This function blocks the current thread and runs a new coroutine until it completes. It is typically used to test coroutines or to run a small amount of code synchronously.</li>
</ul>
<pre><code class="hljs language-Kotlin"><span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> = runBlocking {
    <span class="hljs-keyword">val</span> result = withContext(Dispatchers.IO) {
        <span class="hljs-comment">// run this block in the IO context</span>
        delay(<span class="hljs-number">1000L</span>)
        <span class="hljs-string">"Hello, World!"</span>
    }
    println(result)
}
</code></pre>
<ul>
<li><strong>produce:</strong> This function creates and starts a new coroutine that produces a stream of values. It returns a ReceiveChannel object that can be used to receive the values produced by the coroutine.</li>
</ul>
<p>Each of these coroutine builders has its own specific use cases and can be useful in different situations. For example, you might use <strong>launch</strong> to perform a long-running task in the background, <strong>async</strong> to perform a computation and retrieve the result asynchronously, or <strong>produce</strong> to create a stream of data that can be consumed by other parts of your code. For example:</p>
<h2>Suspending functions</h2>
<p>In Kotlin coroutines, a <strong>"suspending function"</strong> is a function that can be paused and resumed at a later time.
Suspending functions are used to perform long-running tasks, such as network requests or database access, in a non-blocking way.</p>
<p>Here is a list of some of the most common suspending functions provided by the Kotlin coroutines library:</p>
<ul>
<li>
<p><strong>delay:</strong> This function suspends the current coroutine for a specified period of time. It can be used to pause the execution of a coroutine for a fixed amount of time, or to implement a timeout. This function will be demonstrated in the following examples.</p>
</li>
<li>
<p><strong>yield:</strong> This function yields control to other coroutines that are waiting to be executed. It can be used to improve the performance of a coroutine by allowing other coroutines to run in its place when it is idle.</p>
</li>
</ul>
<pre><code class="hljs language-Kotlin"><span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> = runBlocking {
    launch {
        <span class="hljs-keyword">var</span> nextPrintTime = System.currentTimeMillis()
        <span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>
        <span class="hljs-keyword">while</span> (i&#x3C;<span class="hljs-number">10</span>) {
            <span class="hljs-comment">// print a message every second</span>
            <span class="hljs-keyword">if</span> (System.currentTimeMillis() >= nextPrintTime) {
                println(<span class="hljs-string">"X: I'm deciding whom to give the control <span class="hljs-subst">${i++}</span> ..."</span>)
                nextPrintTime += <span class="hljs-number">1000L</span>
            }
            yield() <span class="hljs-comment">// yield control to other coroutines</span>
        }
    }
    delay(<span class="hljs-number">1000L</span>) <span class="hljs-comment">// delay a bit</span>
    println(<span class="hljs-string">"A: I took the control first"</span>)

    delay(<span class="hljs-number">1000L</span>) <span class="hljs-comment">// delay a bit</span>
    println(<span class="hljs-string">"B: I took the control second"</span>)
}
</code></pre>
<p>In this example, we start a new coroutine called "X" and begin printing a message with an incrementing value. Then, we yield control to other coroutines using the yield function.</p>
<p>The coroutine "A" takes control and prints the message: "A: I took the control first". After that, the control is returned to "X", and it continues printing incrementing values.</p>
<p>Then, we yield control to the coroutine "B" and it prints the message: "B: I took the control second". After that, the control is returned to "X", and it continues printing incrementing values until the end. <strong>"yield"</strong> functions is still there, but there are no more coroutines left to yield control to. As a result, the control remains with the current coroutine and the loop continues to execute.</p>
<ul>
<li><strong>withContext:</strong> This function allows you to specify the context in which a block of code should run. It is often used to specify the dispatcher on which a coroutine should run, such as the main thread or a background thread. It suspends the current coroutine and resumes it with a new context when the block completes. Here is an example of how you can use <strong>withContext</strong> to run a block of code in a specific thread:</li>
</ul>
<pre><code class="hljs language-Kotlin"><span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> = runBlocking {
    <span class="hljs-keyword">val</span> result = withContext(Dispatchers.IO) {
        <span class="hljs-comment">// run this block in the IO context</span>
        delay(<span class="hljs-number">1000L</span>)
        <span class="hljs-string">"Hello, World!"</span>
    }
    println(result)
}
</code></pre>
<p>In this example, the withContext function suspends the current coroutine and runs the block of code in the IO context. The coroutine is resumed with the result of the block (i.e. the string "Hello, World!") once the block completes.</p>
<p>You can use <strong>withContext</strong> to perform blocking or I/O-bound operations in a specific thread, or to switch between threads to take advantage of the different characteristics of each thread. For example, you might use <strong>Dispatchers.Main</strong> to update the UI from a background thread.</p>
<ul>
<li><strong>await:</strong> This function is used to suspend the current coroutine and wait for the completion of a Deferred object. It can be used to retrieve the result of an asynchronous computation. For example:</li>
</ul>
<pre><code class="hljs language-Kotlin"><span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> = runBlocking {
    <span class="hljs-keyword">val</span> deferred = async {
        <span class="hljs-comment">// start a new coroutine and return a Deferred object</span>
        delay(<span class="hljs-number">1000L</span>)
        <span class="hljs-string">"Hello, World!"</span>
    }
    <span class="hljs-keyword">val</span> result = deferred.await() <span class="hljs-comment">// wait for the result of the coroutine</span>
    println(result)
}
</code></pre>
<p>There are many other suspending functions available in the Kotlin coroutines library, depending on the specific needs of your app.
For example, the <strong>kotlinx.coroutines.flow</strong> package provides functions for creating and manipulating streams of data (you can get to know them better here: <a href="https://engineering.monstar-lab.com/en/post/2022/09/30/Introduction-to-Kotlin-flows">Introduction to Kotlin Flows</a>, and the <strong>kotlinx.coroutines.channels</strong> package provides functions for creating and manipulating channels for message passing.</p>
<h2>Conclusion</h2>
<p>Kotlin coroutines are a useful tool for Android developers to write asynchronous, non-blocking code in a more readable and straightforward manner. They enable the suspension and resumption of code execution and provide various coroutine builders, such as "launch," "async," "runBlocking," and "produce," to create and manage coroutines. Suspending functions allow you to perform long-running tasks, such as network requests and database access, asynchronously. Kotlin coroutines also provide ways to handle errors, including try-catch blocks, the "throw" keyword, and the "supervisorScope" function. In summary, Kotlin coroutines are a valuable addition to any developer's toolkit for writing efficient asynchronous code in Android projects.</p>
<h2>Resources</h2>
<ul>
<li><a href="https://developer.android.com/kotlin/coroutines">Kotlin coroutines on Android</a></li>
<li><a href="https://kotlinlang.org/docs/coroutines-overview.html">Coroutines﻿</a></li>
<li><a href="https://kotlinlang.org/docs/coroutines-guide.html">Coroutines guide﻿</a></li>
<li><a href="https://developer.android.com/kotlin/coroutines/additional-resources">Additional resources for Kotlin coroutines and flow</a></li>
</ul>
<p><em>Article Photo created by Midjourney AI <a href="https://www.midjourney.com">Midjourney</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to setup CI/CD pipeline for WordPress with GitHub Actions and AWS (Part 1)]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/01/02/How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/01/02/How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS</guid>
            <pubDate>Mon, 02 Jan 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>GitHub Actions can be used to automate almost any type of project, but in this article, I will cover how can we automate deployment (CI/CD) for WordPress. I personally chose WordPress as an example as is the world’s most popular CMS, with over 455 million websites that use WordPress, a number which is increasing day by day. With this in mind, this tutorial will hopefully be useful for many. To know more benefits of using WordPress I would suggest reading <a href="https://www.dreamhost.com/blog/why-use-wordpress/">this article</a>.</p>
<h2>What are GitHub Actions?</h2>
<p>GitHub Actions are a convenient service where we can automate our builds, tests, and deployments instead of doing them manually. Every time we complete a task locally and want to deploy it on a server, we want to minimize redundant work especially when we want to deploy only minor fixes.
CI/CD (Continuous Integration and Continuous Delivery) helps us automate those processes and GitHub Actions do this for us with a couple of settings.
You can also check out the <a href="https://github.com/features/actions">GitHub Actions official documentation</a> for more detailed information.</p>
<h2>How do GitHub Actions work</h2>
<p>There are many events for GitHub Actions. Some handy and frequent-use events are pull_request, push, schedule, etc. You can find them all on <a href="https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows">GitHub's official documentation</a>.</p>
<p>Let's see our GitHub branches and how we would like to organize GitHub Actions on them.</p>
<p><strong><em>GitHub Branches:</em></strong>
![Github Branches](/assets/img/articles/2023-01-02-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS/github.webp</p>

























<table><thead><tr><th>Branch Name</th><th>Our Actions Based on it</th></tr></thead><tbody><tr><td><strong><em>master</em></strong></td><td>Here we keep all up-to-date codebase.</td></tr><tr><td><strong><em>prod-deploy</em></strong></td><td>If any code is merge in this branch, We run a GitHub Action for the production deployment.</td></tr><tr><td><strong><em>dev</em></strong></td><td>We work on this branch for fixing issues and building features.</td></tr><tr><td><strong><em>dev-deploy</em></strong></td><td>If any code is merge in this branch, We run a GitHub Action for development deployment.</td></tr></tbody></table>
<h3>Our servers architecture</h3>
<p>I made a minimal server architecture, I am not going to display a complex architecture but at the end, we will move a bit deeper.
![server architecture](/assets/img/articles/2023-01-02-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS/aws.webp</p>
<p>We have two server environment for one website. <br></p>
<ol>
<li>Development Server</li>
<li>Production Server</li>
</ol>
<p>After developing any new features or bug fixes we will send a Pull Request to the <strong><em>"dev"</em></strong> branch and after the code has been reviewed, if the <strong><em>"code reviewer"</em></strong> approves the code then s/he will merge and send another Pull Request to the <strong><em>"dev-deploy"</em></strong> branch.
The GitHub Action will run when the code is merged on <strong><em>"dev-deploy"</em></strong>.</p>
<p>![github workflow](/assets/img/articles/2023-01-02-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS/workflow.webp</p>
<p>Now, this is the time to create a GitHub Action for the <strong><em>"dev-deploy"</em></strong> branch. I will try to provide as many details as possible step by step. Let's create a <strong><em>"development"</em></strong> workflow in our repository.
Create a folder named <strong><code>".github"</code></strong> in the root of your project directory.
Create another folder named <strong><code>"workflows"</code></strong> inside the <strong><code>".github"</code></strong> directory and create a file named <strong><code>"dev_deploy.yml"</code></strong>, you can name it anything but the file extension should be <strong><code>".yml"</code></strong>. We will write all deployment code in this file.<br></p>
<h6>Step 1:</h6>
<p>Let's add a "name". You can name it whatever you want but meaningful name is better.
![provide a name](/assets/img/articles/2023-01-02-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS/step-one.webp</p>
<h6>Step 2:</h6>
<p>We want to run our GitHub Action when anything merges with the <strong><code>"dev-deploy"</code></strong> branch. We have to write the code on the <strong><code>"on step"</code></strong> how we would like to trigger the event. Here is an example <strong><code>"on"</code></strong> event that we are firing when the code merges with the <strong><code>"dev-deploy"</code></strong> git branch.
![github 'on' step](/assets/img/articles/2023-01-02-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS/step-two.webp</p>
<h6>Step 3:</h6>
<p>We can set a default directory before the <strong><code>"jobs"</code></strong> run. We know that our root directory has no WordPress theme, so we can set the directory in the "theme directory" so that we do not need to change the directory in every <strong><code>"steps"</code></strong>.
![github 'jobs' step](/assets/img/articles/2023-01-02-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS/step-three.webp</p>
<h6>Step 4:</h6>
<p><strong><code>"GitHub workflow"</code></strong> is a collection of <strong><code>"jobs,"</code></strong> and we write the <strong><code>"jobs"</code></strong> based on our needs. In this stage, we create a job that will run a virtual server based on our settings. We run <strong><code>"Ubuntu Linux"</code></strong> here but you can run <code>macOS or Microsoft Windows</code> if needed.
![GitHub workflow](/assets/img/articles/2023-01-02-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS/step-four.webp</p>
<h6>Step 5:</h6>
<p>Here we write some <strong><code>"steps"</code></strong> that will be executed sequentially. If we define a <strong><code>"step"</code></strong> with <strong><code>"uses"</code></strong> it will execute as an action and if <strong><code>"step"</code></strong> with <strong><code>"run"</code></strong> then it will execute as a shell script.
![github run and use](/assets/img/articles/2023-01-02-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS/step-five.webp</p>
<h5>We are already done! And it is as simple as I said it would be. Now let's see what it looks like when we write all the code together.</h5>
<script src="https://gist.github.com/Rasel-Mahmud/e5f3be5dabf866c87446f78e82a2deb9.js"></script>
<h5>Let's describe each step:</h5>
<ul>
<li>
<p><strong><code>checkout:</code></strong> We checkout with our files into the virtual server with the GitHub checkout action <a href="https://github.com/actions/checkout">https://github.com/actions/checkout</a></p>
</li>
<li>
<p><strong><code>cache:</code></strong> It keeps the cache so the theme build will be faster after the 1st time. (if the cache exists). More about the cache action here <a href="https://github.com/actions/cache">https://github.com/actions/cache</a></p>
</li>
<li>
<p><strong><code>Node JS:</code></strong> Our theme requires building the JavaScript packages and it has node version dependency v12.x.x so we just take the node version <strong><code>12.x</code></strong> on our virtual server. You can pick your own version from here: <a href="https://github.com/actions/setup-node">https://github.com/actions/setup-node</a></p>
</li>
<li>
<p><strong><code>PHP:</code></strong> Our theme requires building the PHP packages and it has PHP version dependency v7.4 so we just take the PHP version <strong><code>"7.4"</code></strong> on our virtual server. You can pick your own version from here: <a href="https://github.com/shivammathur/setup-php">https://github.com/shivammathur/setup-php</a></p>
</li>
<li>
<p><strong><code>Install Node dependency:</code></strong> Here we run an action to install our npm package dependencies.</p>
</li>
<li>
<p><strong><code>Install PHP dependency:</code></strong> Here we run an action to install the PHP package dependencies.</p>
</li>
<li>
<p><strong><code>Build The Theme:</code></strong> Here we run the internal package command to build our WordPress theme. We are using the "Tonic starter" theme for our WordPress Theme.</p>
</li>
<li>
<p><strong><code>Before Deployment:</code></strong> Before deployment, we have to do some tasks if required. In my case, I do not want to mess up the setting that our "infrastructure team" made and I need the right permission to move the files too. so here I do some tasks like removing the old theme and change the folder ownership to mine. <br>
To perform these actions we need to login/SSH to the server. For example, we need a DNS/IP, username, ssh key, port, etc to connect the AWS EC2 instance. I use here <strong><code>ssh-action</code></strong> to make my job easy. <a href="https://github.com/appleboy/ssh-action">https://github.com/appleboy/ssh-action</a></p>
</li>
</ul>
<p><code>To add these things just follow below steps:</code></p>
<ol>
<li>Go to the Repository and click on the "Settings" tab
![github Settings](/assets/img/articles/2023-01-02-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS/setting-one.webp</li>
<li>In the left side: menu, you will find <strong><code>"actions"</code></strong> under the <strong><code>"secrets"</code></strong> menu.
![github setting](/assets/img/articles/2023-01-02-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS/setting-two.webp</li>
<li>Click on <strong><code>"New repository secret"</code></strong>.</li>
<li>Provide a <strong><code>"name"</code></strong> on that specific <strong><code>"secret"</code></strong> and later you can access it by its name <strong><em><code>${{ secrets.PORT }}</code></em></strong> here <strong><code>"PORT"</code></strong> was the name. <br>
You might not want to write permission or ownership command openly in the .yml file that is kept in Github. It leads a security thread later on so we can add this command in the secret and later access it when needed.
![github setting](/assets/img/articles/2023-01-02-How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS/setting-three.webp</li>
</ol>
<ul>
<li>
<p><strong><code>Deploy to AWS Server:</code></strong> Here we just move the build theme into the right directory and here also I am using another pre-build GitHub Action named "ssh-deploy". More about the ssh deploy action here: <a href="https://github.com/easingthemes/ssh-deploy">https://github.com/easingthemes/ssh-deploy</a></p>
</li>
<li>
<p><strong><code>After Deployment:</code></strong> The changes we made before theme deployment, we do revert file permission and the ownership.</p>
</li>
</ul>
<p>Congratulations! You have done all the steps, now it's time to run the auto-deployment by merging code on <strong><code>"dev-deploy"</code></strong>.</p>
<p>Would you like to know a bit deeper about GitHub Actions? One of our colleagues named "Sabrina Rashid", wrote a stunning article regarding <a href="/en/post/2022/07/15/A-Guideline-to-GitHub-Actions-with-CI-Pipeline/">GitHub Actions</a>, you can check that too.</p>
<p><a href="/en/post/2023/08/07/How-to-setup-CI-CD-pipeline-for-WordPress-with-GitHub-Actions-and-AWS-part-2">In Part 2 of the article</a>, I have covered the setup and configuration of a GitHub self-hosted runner. If you are interested in using self-hosted runners for your deployment process, I highly recommend reading the section as it provides detailed steps and insights into the advantages of utilizing self-hosted runners.</p>
<h2>Resources</h2>
<ul>
<li><a href="https://www.envisagedigital.co.uk/wordpress-market-share/">WordPress Market Share Statistics</a></li>
</ul>
<p><em>Article Photo by <a href="https://unsplash.com/@synkevych">Roman Synkevych</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Open Source The Future of Software Development]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/01/02/Open-Source-The-Future-of-Software-Development</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/01/02/Open-Source-The-Future-of-Software-Development</guid>
            <pubDate>Mon, 02 Jan 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Are you looking to leverage the power of open source software for your business or project? Look no further!
In this comprehensive guide, we dive into the world of open source, exploring the different types of open source software and the benefits they offer.
From cost-saving options to increased flexibility and stronger security, open source has something to offer for every need.
But with so many options available, it can be overwhelming to navigate the landscape of open source licenses and communities.
Don't worry, we've got you covered. We'll demystify the world of open source, breaking down the various licenses and their associated requirements, and provide tips on how to make the most of the vast resources and support offered by the open source community.
Whether you're a seasoned developer or new to the open source world, this guide has something for you.
So let's get started on your journey towards open source success!</p>
<h2>Types of Open Source Software and Licenses</h2>
<p><img src="/assets/img/articles/2023-01-02-Open-Source-The-Future-of-Software-Development/image01.webp" alt="">
Open source software is freely available for anyone to use, modify, and distribute.
In this chapter, we will explore the various types of open source software and the licenses that govern their use.</p>
<p>There are many different types of software, each with its own unique characteristics and uses.In this article, we will focus on four main categories:</p>
<ul>
<li><strong>Free software</strong> - software that is available to users at no cost, regardless of whether the source code is open or closed.</li>
<li><strong>Commercial software</strong> - software that is sold to users for a fee, regardless of whether the source code is open or closed.</li>
<li><strong>Proprietary software</strong> - software with a closed source code that is owned and controlled by a particular company or individual.</li>
<li><strong>Open-source software</strong> - software with an open source code that is freely available for anyone to use, modify, and distribute.</li>
</ul>
<p>When it comes to open source software, the licenses that govern their use are an important consideration.
These licenses outline the terms under which the software can be used, modified, and distributed, and understanding the various licenses and their associated requirements is key to properly using and distributing open source software.</p>
<p>Some common types of open source licenses include:</p>
<ul>
<li><strong>MIT License</strong> - a permissive license that allows users to use, modify, and distribute the software without many restrictions. It does not require users to distribute the source code or make their own changes available to others.</li>
<li><strong>GNU General Public License (GPL)</strong> - a copyleft license that requires users to make the source code and any changes they make available to others. It also requires that any software distributed using GPL-licensed code must be distributed under the same license.</li>
<li><strong>Apache License</strong> - a permissive license that allows users to use, modify, and distribute the software without many restrictions. It requires users to include a copy of the license and give credit to the original authors.</li>
<li><strong>Creative Commons (CC)</strong> - a set of licenses that allow users to use, modify, and distribute creative works, such as images, videos, and music, under various levels of restriction.</li>
</ul>
<p>It is important to carefully review the terms of the specific license associated with the open source software you are using, as the requirements can vary significantly.
Failure to properly follow the terms of the license can result in legal consequences.
In addition to the above licenses, there are many other types of open source licenses available, each with its own unique features and requirements.
Understanding the various types of open source software and their associated licenses is key to making the most of the vast resources and community support that the open source movement has to offer.</p>
<p>Open source software has many advantages, including the ability to attract talent, build trust with users, and drive innovation.
It is an important part of the software ecosystem and has enormous potential for shaping the future of software development.</p>
<h2>Advantages of Open Source</h2>
<p><img src="/assets/img/articles/2023-01-02-Open-Source-The-Future-of-Software-Development/image03.webp" alt="">
There are many advantages to using open source software.
One of the biggest is the ability to attract talent through advertising.
Companies like Google and Red Hat are able to attract top developers by openly sharing their code and inviting contributions from the community.
In addition, open source software can be a great way to build trust with users, as the open nature of the code allows for transparency and honesty.</p>
<p>Other advantages of open source include:</p>
<ul>
<li><strong>Lower costs</strong> - since open source software is freely available, it can save organizations money on licensing fees.</li>
<li><strong>Greater flexibility</strong> - with open source software, users have the freedom to modify the code to fit their specific needs and can even hire developers to make customizations.</li>
<li><strong>Stronger security</strong> - the open nature of open source software allows for a larger community of developers to review and identify potential security vulnerabilities, leading to stronger security.</li>
<li><strong>Faster innovation</strong> - the ability to access and contribute to the source code allows for faster development and innovation.</li>
</ul>
<p>Overall, open source software offers a number of advantages that can help organizations save money, increase flexibility, improve security, and drive innovation.
It is an important part of the software ecosystem and has enormous potential for shaping the future of software development.</p>
<h2>Open Source Fears</h2>
<p><img src="/assets/img/articles/2023-01-02-Open-Source-The-Future-of-Software-Development/image04.webp" alt="">
Despite the many benefits of open source, there are still some common fears and misconceptions surrounding its use.
One common fear is that code will be stolen or used for nefarious purposes. However, there are numerous licenses available for open source software that can protect against this, such as the GNU General Public License (GPL), which requires that any modifications or derived works also be released under the GPL.</p>
<p>Another fear is that code will be "spied on" by competitors.
In reality, it would take a significant amount of time and resources for a company to parse through even a moderate-sized open source codebase, making this fear largely unfounded.
In addition, the open nature of open source software allows for greater transparency and accountability, making it less likely that malicious actors would be able to successfully hide their activities within the code.</p>
<p>Other common concerns about open source include:</p>
<ul>
<li><strong>Lack of support</strong> - some users may be concerned about the availability of support for open source software. However, many open source projects have active communities that provide support and documentation, and there are also companies that offer paid support for open source software.</li>
<li><strong>Lack of control</strong> - since open source software is freely available for anyone to use and modify, some users may be concerned about losing control over their code. However, careful selection of an appropriate license can help protect against this.</li>
<li><strong>Quality concerns</strong> - some users may be concerned about the quality of open source software, as it is developed by a community rather than a single company. However, the open nature of open source software allows for a larger number of developers to review and improve the code, leading to higher overall quality.</li>
</ul>
<p>While there are some valid concerns about open source software, the many benefits it offers often outweigh the potential risks. By understanding the different licenses available and carefully selecting the right one for your needs, you can effectively protect your code and take advantage of the benefits of open source.</p>
<h2>Examples</h2>
<p><img src="/assets/img/articles/2023-01-02-Open-Source-The-Future-of-Software-Development/image05.webp" alt="">
There are many successful companies that have embraced open source and are using it to drive innovation and growth.
Google has open sourced over 20 million lines of code in 900+ projects, while Red Hat has more than 100 open source projects and Facebook has around 300
repositories. Twitter also has a number of open source projects, showing that open source can be successful in a variety of industries.</p>
<p>All of these companies are successful, all profitable, and all of them are important contributors to open source.
I think they could be a great example for all of us.</p>
<h3>The Enormous Potential of Linux</h3>
<p>The platform itself changes 9 times an hour!
Ten thousand lines of code are added every day.
About five thousand lines are changed and about eight thousand lines are removed.
It’s by far the highest velocity, most effective software development process in the history of computing!
It’s all about open source. It’s all about Linux!</p>
<h2>Conclusion</h2>
<p><img src="/assets/img/articles/2023-01-02-Open-Source-The-Future-of-Software-Development/image06.webp" alt="">
Open source software has come a long way from its humble beginnings as a domain for hobbyists and tinkerers.
Today, it is an integral part of the software ecosystem, driving innovation and shaping the future of software development.
From widely-used platforms like Linux to specialized tools for data science and machine learning, there is an open source option for every need.</p>
<p>One of the defining characteristics of open source software is its open nature, which allows for greater transparency, honesty, and collaboration.
This makes it an excellent choice for organizations looking to build trust with users and attract top talent.
In addition, the ability to freely access and modify the source code gives users greater flexibility and can save organizations money on licensing fees.</p>
<p>Despite some common fears surrounding open source software, such as concerns about code theft and lack of support, the many benefits it offers often outweigh the potential risks.
By understanding the different types of open source software and selecting the right license for your needs, you can effectively protect your code and take advantage of the unique features of open source.
As the examples of successful companies like Google, Red Hat, Facebook, and Twitter demonstrate, open source can be a powerful tool for driving growth, building better products, and driving innovation.</p>
<h2>Resources</h2>
<ul>
<li><a href="http://bit.ly/FOOS2015">Results: 2015 Future of Open Source Study</a>\</li>
<li><a href="https://en.wikipedia.org/wiki/Proprietary_software">Proprietary software - Wikipedia</a>\</li>
<li><a href="https://en.wikipedia.org/wiki/Open-source_software">Open-source software - Wikipedia</a>\</li>
<li><a href="https://en.wikipedia.org/wiki/Free_software">Free software - Wikipedia</a>\</li>
<li><a href="https://en.wikipedia.org/wiki/Freeware">Freeware - Wikipedia</a>\</li>
<li><a href="https://www.youtube.com/watch?v=SpeDK1TPbew&#x26;ab_channel=CNBC">The Rise Of Open-Source Software - YouTube</a>\</li>
<li><a href="https://www.youtube.com/watch?v=ZCmqeOHhjzk&#x26;ab_channel=DWShift">The Future of Open Source? | When Open Source gets monetized - YouTube</a>\</li>
<li><a href="https://www.youtube.com/watch?v=Ag1AKIl_2GM&#x26;ab_channel=TEDxTalks">Free software, free society: Richard Stallman at TEDxGeneva 2014 - YouTube</a></li>
</ul>
<h3>Art</h3>
<ul>
<li>Article Photo by <a href="https://storyset.com/web">Storyset</a>\</li>
<li>Work illustrations by <a href="https://storyset.com/work">Storyset</a>\</li>
<li>People illustrations by <a href="https://storyset.com/people">Storyset</a>\</li>
<li>Technology illustrations by <a href="https://storyset.com/technology">Storyset</a>\</li>
<li>Business illustrations by <a href="https://storyset.com/business">Storyset</a>\</li>
<li>Team illustrations by <a href="https://storyset.com/team">Storyset</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to modify your environment using Postman API]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2023/01/02/Postman-API-environments</link>
            <guid>https://engineering.monstar-lab.com/en/post/2023/01/02/Postman-API-environments</guid>
            <pubDate>Mon, 02 Jan 2023 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Most of the projects we are working on at Monstarlab have frontend and backend parts. Business logic is usually on the backend, and the frontend communicates with the backend using an API.</p>
<p>For some projects we have automated UI tests, but automate API testing is more common:</p>
<ul>
<li>API auto tests are faster than UI auto tests</li>
<li>The most part of business logic can be tested on the API level</li>
<li>Creating and running API auto tests needs less effort than for the UI ones</li>
<li>API auto tests support the frontend team:
<ul>
<li>the tests give a better understanding on how to use the API</li>
<li>the tests support the frontend development (especially in the beginning of the project when the backend might often be changed)</li>
</ul>
</li>
</ul>
<p>My favorite tool for API testing is Postman. It’s really flexible and easy to use. In this article I will not focus on the Postman Tool, but on the <a href="https://www.postman.com/postman/workspace/postman-public-workspace/documentation/12959542-c8142d51-e97c-46b6-bd77-52bb66712c9a">Postman API</a>.</p>
<p>It gives us the ability to programmatically manipulate Postman data such as workspaces, collections, environments, mocks and more.</p>
<p>An API key is mandatory for sending requests to the Postman API. The key can be taken from <a href="https://web.postman.co/settings/me/api-keys">the integrations dashboard</a>.
It should be added into Headers named as: “X-API-Key”.</p>
<p>The value of the “X-API-Key” can be used directly in the header but it is better to save it in the environment variables (with variable type “secret”):</p>
<p><img src="/assets/img/articles/2023-01-02-Postman-API-environments/pic1.webp" alt=""></p>
<p>Here is an example of a GET /me request that returns information about the user the X-API-Key belongs to:</p>
<p><img src="/assets/img/articles/2023-01-02-Postman-API-environments/pic2.webp" alt=""></p>
<p>I would like to pay your attention to the operations limitation the user has.
It depends on your current Pricing Plan. For example if you are using a free Postman account you have only 1000 API calls.</p>
<p>Once your “usage” is equal to the “limit” you will receive an error with 429 response code and “Service limit exhausted. Please contact your team admin.” message.</p>
<p>Anyway, let’s take a look at a case when using the Postman API helped me a lot.</p>
<p>It was when I started to work on a project where API tests might have be taken and run by anyone from the team.</p>
<p>We noticed a problem. Each time when a collection was run locally it worked well. Once I exported my collection and environment and shared the json files with my colleagues the run didn’t work on their side.</p>
<p>I did an experiment.</p>
<p>I created the following environment:</p>






























<table><thead><tr><th>VARIABLE</th><th>INITIAL VALUE</th><th>CURRENT VALUE</th></tr></thead><tbody><tr><td>onlyInitialValues</td><td>one</td><td></td></tr><tr><td>onlyCurrentValue</td><td></td><td>two</td></tr><tr><td>noAnyValues</td><td></td><td></td></tr><tr><td>bothValues</td><td>three</td><td>four</td></tr></tbody></table>
<br>
<p>After that I copied the environment. It looked in the following way:</p>






























<table><thead><tr><th>VARIABLE</th><th>INITIAL VALUE</th><th>CURRENT VALUE</th></tr></thead><tbody><tr><td>onlyInitialValues</td><td>one</td><td>one</td></tr><tr><td>onlyCurrentValue</td><td></td><td></td></tr><tr><td>noAnyValues</td><td></td><td></td></tr><tr><td>bothValues</td><td>three</td><td>three</td></tr></tbody></table>
<br>
<p>As you can see, initial values are copied to the current ones.</p>
<p>The same was when the environment was exported to json and imported again.</p>
<p>But I counted to see fresh values there (that are taken from the Current Value column).</p>
<p>That is what I read in the <a href="https://learning.postman.com/docs/sending-requests/managing-environments/">Postman documentation</a>:</p>
<blockquote>
<p>Enter a name for your variable, and specify its <strong>Initial</strong> and <strong>Current</strong> values. By default the current value will copy the initial value.</p>
<ul>
<li>The <strong>Initial Value</strong> is synced to your account using the Postman servers. It’s shared with any collaborators who have access to the environment.</li>
<li>The <strong>Current Value</strong> is used in your local instance of Postman, and is never synced to your account or shared with your team unless you choose to persist it.</li>
</ul>
<p>To update the synced variable with your local value, set the initial value to the current value by selecting the more actions icon and choosing <strong>Persist</strong>. To reset your local (current) value with the synced value shared with your workspace / collaborators, select <strong>Reset</strong>. You can persist or reset all values in the environment using <strong>Persist All</strong> and <strong>Reset All</strong>.</p>
</blockquote>
<br>
<p>So, when you copy or export/ import an environment it is like the “Reset All” action is performed.</p>
<p>The “Persist All” would fit better for my needs. But it is still manual work: each time when the environment is changed the “Persist All” command should be called.</p>
<p>I did some research and found that it is possible to do programmatically. The information can be found under the link to the <a href="https://support.postman.com/hc/en-us/articles/4409005403031-How-to-persist-variable-values-">Postman Support</a>.</p>
<p>The script they provided worked well but I created an improved that version to fit my purposes better:</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">let</span> currentEnvId = pm.<span class="hljs-property">environment</span>.<span class="hljs-property">id</span>.<span class="hljs-title function_">replace</span>(<span class="hljs-string">"environment/"</span>, <span class="hljs-string">""</span>);
<span class="hljs-keyword">let</span> index = currentEnvId.<span class="hljs-title function_">indexOf</span>(<span class="hljs-string">"/"</span>);
currentEnvId = currentEnvId.<span class="hljs-title function_">substring</span>(<span class="hljs-number">0</span>, index);

<span class="hljs-keyword">const</span> getRequestOptions = {
    <span class="hljs-attr">url</span>: <span class="hljs-string">'https://api.getpostman.com/environments/'</span> + currentEnvId,
    <span class="hljs-attr">method</span>: <span class="hljs-string">'GET'</span>,
    <span class="hljs-attr">header</span>: {
        <span class="hljs-string">'X-API-Key'</span>: pm.<span class="hljs-property">environment</span>.<span class="hljs-title function_">get</span>(<span class="hljs-string">"postmanApiKey"</span>)
    }
}

pm.<span class="hljs-title function_">sendRequest</span>(getRequestOptions, <span class="hljs-function">(<span class="hljs-params">error, response</span>) =></span> {
    <span class="hljs-keyword">if</span> (error)
    {
        <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(error);
    }
    <span class="hljs-keyword">else</span>
    {
        <span class="hljs-keyword">let</span> body = response.<span class="hljs-title function_">json</span>()
        <span class="hljs-keyword">const</span> envVariablestoUpdate = [<span class="hljs-string">'variable2'</span>, <span class="hljs-string">'variable3'</span>];

        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &#x3C; body.<span class="hljs-property">environment</span>.<span class="hljs-property">values</span>.<span class="hljs-property">length</span>; i++)
        {
            <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> j = <span class="hljs-number">0</span>; j &#x3C; envVariablestoUpdate.<span class="hljs-property">length</span>; j++)
            {
                <span class="hljs-keyword">if</span> (envVariablestoUpdate[j] == body.<span class="hljs-property">environment</span>.<span class="hljs-property">values</span>[i].<span class="hljs-property">key</span>)
                {
                    body.<span class="hljs-property">environment</span>.<span class="hljs-property">values</span>[i].<span class="hljs-property">value</span> = pm.<span class="hljs-property">environment</span>.<span class="hljs-title function_">get</span>(envVariablestoUpdate[j]).<span class="hljs-title function_">toString</span>();
                }                   
            }
        }

        <span class="hljs-keyword">const</span> putRequestOptions = {
            <span class="hljs-attr">url</span>: <span class="hljs-string">'https://api.getpostman.com/environments/'</span> + currentEnvId,
            <span class="hljs-attr">method</span>: <span class="hljs-string">'PUT'</span>,
            <span class="hljs-attr">header</span>: {
                <span class="hljs-string">'X-API-Key'</span>: pm.<span class="hljs-property">environment</span>.<span class="hljs-title function_">get</span>(<span class="hljs-string">"postmanApiKey"</span>)
            },
            <span class="hljs-attr">body</span>: {
                <span class="hljs-attr">mode</span>: <span class="hljs-string">'raw'</span>,
                <span class="hljs-attr">raw</span>: body
            }
        }

        pm.<span class="hljs-title function_">sendRequest</span>(putRequestOptions, <span class="hljs-function">(<span class="hljs-params">error, response</span>) =></span> {
            <span class="hljs-keyword">if</span> (error)
            {
                <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(error);
            }
        })
    }
})
</code></pre>
<p><ins>const envVariablestoUpdate = [‘variable1’, ‘variable3’]</ins> should be populated with names of the environment variables which values should be copied from the current to the initial ones.</p>
<p>The script should be used in the Tests section of the requests in which the needed environment variables are changed.</p>
<p>Now the tests can be run after each successful deployment of the remote service we are developing or locally and it doesn't break anything. A GitHub workflow is created to run the tests remotely. And the <a href="http://matt-ball/newman-action@master">Newman Action (via Postman API)</a> is used there.</p>
<p>Every time I learn something new about Postman, it never ceases to amaze me. Well, is there something impossible for it?</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/FPK6K5OUFVA">Rubaitul Azad</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Automating tasks with SPM plugins]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/11/16/SPM-Plugins</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/11/16/SPM-Plugins</guid>
            <pubDate>Wed, 16 Nov 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Swift Package Manager plugins are a great way of automating tasks, like, enforcing code conventions and style.</p>
<p>In this tutorial we're going to learn what are package plugins and implement 2 plugins; one that outputs code statistics and another that generates type-safe UserDefaults preferences. Plugins require swift tools version 5.6 and up.</p>
<p>There are 2 types of plugins, Command plugins and Build Tool plugins. Let's start with command plugins.</p>
<h2>Command Plugins</h2>
<p>Command Plugins are tasks that are manually triggered by the developer.
As an example, we're going to create a plugin
to generate code statistics, such as, how many source code files and lines of code your package has.</p>
<p>First, let's create a package.</p>
<pre><code class="hljs language-bash">$ mkdir CodeStats &#x26;&#x26; <span class="hljs-built_in">cd</span> CodeStats
$ swift package init
$ mkdir -p Plugins/GenerateCodeStats &#x26;&#x26; touch Plugins/GenerateCodeStats/GenerateCodeStats.swift
$ open Package.swift
</code></pre>
<p><strong>Note:</strong> Plugins go into the <strong><code>Plugins</code></strong> folder, as opposed to <strong><code>Source</code></strong>.</p>
<figure>
<img src="/assets/img/articles/2022-11-16-SPM-Plugins/plugin-file-structure.webp">
</figure>
<p>In the manifest file let's create a target and a product.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> package <span class="hljs-operator">=</span> <span class="hljs-type">Package</span>(
    name: <span class="hljs-string">"CodeStats"</span>,
    products: [
        .plugin(
            name: <span class="hljs-string">"GenerateCodeStats"</span>,
            targets: [<span class="hljs-string">"GenerateCodeStats"</span>]
        )
    ],
    targets: [
        .plugin(
            name: <span class="hljs-string">"GenerateCodeStats"</span>,
            capability: .command(
                intent: .custom(
                    verb: <span class="hljs-string">"code-stats"</span>, <span class="hljs-comment">// Verb used from the command line</span>
                    description: <span class="hljs-string">"Generates code statistics"</span>),
                permissions: [
                    .writeToPackageDirectory(reason: <span class="hljs-string">"Generate code statistics file at root level"</span>)
                ])
        ),
    ]
)
</code></pre>
<p>We define our plugin as a command-type plugin with a custom intent and writing permissions. We can use <strong><code>code-stats</code></strong> verb to invoke this plugin from the command line (more on than later).</p>
<p>Now let's implement our simple plugin.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// GenerateCodeStats.swift</span>
<span class="hljs-keyword">import</span> Foundation
<span class="hljs-keyword">import</span> PackagePlugin

<span class="hljs-keyword">@main</span> <span class="hljs-comment">// Plugin's entry point</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">GenerateCodeStats</span>: <span class="hljs-title class_">CommandPlugin</span> {
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">performCommand</span>(<span class="hljs-params">context</span>: <span class="hljs-type">PluginContext</span>, <span class="hljs-params">arguments</span>: [<span class="hljs-type">String</span>]) <span class="hljs-keyword">async</span> <span class="hljs-keyword">throws</span> {
        <span class="hljs-comment">// 1 - Parse all targets from the arguments. These are the targets</span>
        <span class="hljs-comment">// that the developer has manually chosen</span>
        <span class="hljs-keyword">let</span> targets <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> parseTargets(context: context, arguments: arguments)
        <span class="hljs-keyword">let</span> processor <span class="hljs-operator">=</span> <span class="hljs-type">FileStatsProcessor</span>()
        <span class="hljs-keyword">let</span> fm <span class="hljs-operator">=</span> <span class="hljs-type">FileManager</span>.default
        <span class="hljs-keyword">let</span> dirs <span class="hljs-operator">=</span> targets.isEmpty <span class="hljs-operator">?</span> [context.package.directory] : targets.map(\.directory)

        <span class="hljs-comment">// 2 - Loop through all targets' files</span>
        <span class="hljs-keyword">for</span> dir <span class="hljs-keyword">in</span> dirs {
            <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> files <span class="hljs-operator">=</span> fm.enumerator(atPath: dir.string) <span class="hljs-keyword">else</span> { <span class="hljs-keyword">continue</span> }

            <span class="hljs-comment">// 2.1 - Process only swift files</span>
            <span class="hljs-keyword">for</span> <span class="hljs-keyword">case</span> <span class="hljs-keyword">let</span> path <span class="hljs-keyword">as</span> <span class="hljs-type">String</span> <span class="hljs-keyword">in</span> files {
                <span class="hljs-keyword">let</span> fullpath <span class="hljs-operator">=</span> dir.appending([path])
                <span class="hljs-keyword">var</span> isDirectory: <span class="hljs-type">ObjCBool</span> <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>

                <span class="hljs-keyword">guard</span>
                    fullpath.extension <span class="hljs-operator">==</span> <span class="hljs-string">"swift"</span>,
                    fm.fileExists(atPath: fullpath.string, isDirectory: <span class="hljs-operator">&#x26;</span>isDirectory),
                    <span class="hljs-operator">!</span>isDirectory.boolValue
                <span class="hljs-keyword">else</span> { <span class="hljs-keyword">continue</span> }

                <span class="hljs-keyword">try</span> processor.processFile(at: fullpath)
            }
        }

        <span class="hljs-keyword">let</span> output <span class="hljs-operator">=</span> context.package.directory.appending([<span class="hljs-string">"CodeStats.md"</span>])

        <span class="hljs-built_in">print</span>(processor.stats.description)

        <span class="hljs-comment">// 3 - Write the stats to a file in the root directory of the package</span>
        <span class="hljs-keyword">try</span> processor.stats.description.write(
            to: <span class="hljs-type">URL</span>(fileURLWithPath: output.string),
            atomically: <span class="hljs-literal">true</span>,
            encoding: .utf8)
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">parseTargets</span>(
        <span class="hljs-params">context</span>: <span class="hljs-type">PluginContext</span>,
        <span class="hljs-params">arguments</span>: [<span class="hljs-type">String</span>]
    ) <span class="hljs-keyword">throws</span> -> [<span class="hljs-type">Target</span>] {
        <span class="hljs-keyword">return</span> arguments
            .enumerated()
            .filter { <span class="hljs-variable">$0</span>.element <span class="hljs-operator">==</span> <span class="hljs-string">"--target"</span> }
            .map { arguments[<span class="hljs-variable">$0</span>.offset <span class="hljs-operator">+</span> <span class="hljs-number">1</span>] }
            .compactMap { <span class="hljs-keyword">try?</span> context.package.targets(named: [<span class="hljs-variable">$0</span>]) }
            .flatMap { <span class="hljs-variable">$0</span> }
    }
}

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">CodeStats</span>: <span class="hljs-title class_">CustomStringConvertible</span> {
    <span class="hljs-keyword">var</span> numberOfFiles: <span class="hljs-type">Int</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>
    <span class="hljs-keyword">var</span> numberOfLines: <span class="hljs-type">Int</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>
    <span class="hljs-keyword">var</span> numberOfClasses: <span class="hljs-type">Int</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>
    <span class="hljs-keyword">var</span> numberOfStructs: <span class="hljs-type">Int</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>
    <span class="hljs-keyword">var</span> numberOfEnums: <span class="hljs-type">Int</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>
    <span class="hljs-keyword">var</span> numberOfProtocols: <span class="hljs-type">Int</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>

    <span class="hljs-keyword">var</span> description: <span class="hljs-type">String</span> {
        <span class="hljs-keyword">return</span> [
            <span class="hljs-string">"## Code statistics<span class="hljs-subst">\n</span>"</span>,
            <span class="hljs-string">"Number of files:     <span class="hljs-subst">\(fmt(numberOfFiles))</span>"</span>,
            <span class="hljs-string">"Number of lines:     <span class="hljs-subst">\(fmt(numberOfLines))</span>"</span>,
            <span class="hljs-string">"Number of classes:   <span class="hljs-subst">\(fmt(numberOfClasses))</span>"</span>,
            <span class="hljs-string">"Number of structs:   <span class="hljs-subst">\(fmt(numberOfStructs))</span>"</span>,
            <span class="hljs-string">"Number of enums:     <span class="hljs-subst">\(fmt(numberOfEnums))</span>"</span>,
            <span class="hljs-string">"Number of protocols: <span class="hljs-subst">\(fmt(numberOfProtocols))</span>"</span>,
        ].joined(separator: <span class="hljs-string">"<span class="hljs-subst">\n</span>"</span>)
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">fmt</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">i</span>: <span class="hljs-type">Int</span>) -> <span class="hljs-type">String</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-type">String</span>(format: <span class="hljs-string">"%8d"</span>, i)
    }
}

<span class="hljs-keyword">class</span> <span class="hljs-title class_">FileStatsProcessor</span> {
    <span class="hljs-keyword">private(set)</span> <span class="hljs-keyword">var</span> stats <span class="hljs-operator">=</span> <span class="hljs-type">CodeStats</span>()
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> definitionsRegex: <span class="hljs-type">NSRegularExpression</span> <span class="hljs-operator">=</span> {
        <span class="hljs-keyword">let</span> pattern <span class="hljs-operator">=</span> <span class="hljs-string">#"\b(?&#x3C;name>protocol|class|struct|enum)\b"#</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">try!</span> <span class="hljs-type">NSRegularExpression</span>(pattern: pattern)
    }()
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> newlinesRegex: <span class="hljs-type">NSRegularExpression</span> <span class="hljs-operator">=</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">try!</span> <span class="hljs-type">NSRegularExpression</span>(pattern: <span class="hljs-string">#"$"#</span>, options: [.anchorsMatchLines])
    }()

    <span class="hljs-keyword">func</span> <span class="hljs-title function_">processFile</span>(<span class="hljs-params">at</span> <span class="hljs-params">path</span>: <span class="hljs-type">Path</span>) <span class="hljs-keyword">throws</span> {
        <span class="hljs-keyword">let</span> text <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">String</span>(contentsOfFile: path.string)
        <span class="hljs-keyword">let</span> textRange <span class="hljs-operator">=</span> <span class="hljs-type">NSRange</span>(text.startIndex<span class="hljs-operator">..&#x3C;</span>text.endIndex, in: text)

        stats.numberOfFiles <span class="hljs-operator">+=</span> <span class="hljs-number">1</span>
        stats.numberOfLines <span class="hljs-operator">+=</span> newlinesRegex.matches(in: text, range: textRange).count

        definitionsRegex.enumerateMatches(in: text, range: textRange) { match, <span class="hljs-keyword">_</span>, <span class="hljs-keyword">_</span> <span class="hljs-keyword">in</span>
            <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> nsRange <span class="hljs-operator">=</span> match<span class="hljs-operator">?</span>.range(withName: <span class="hljs-string">"name"</span>),
                  <span class="hljs-keyword">let</span> range <span class="hljs-operator">=</span> <span class="hljs-type">Range</span>(nsRange, in: text)
            <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> }

            <span class="hljs-keyword">switch</span> text[range.lowerBound] {
            <span class="hljs-keyword">case</span> <span class="hljs-string">"p"</span>: stats.numberOfProtocols <span class="hljs-operator">+=</span> <span class="hljs-number">1</span>
            <span class="hljs-keyword">case</span> <span class="hljs-string">"c"</span>: stats.numberOfClasses <span class="hljs-operator">+=</span> <span class="hljs-number">1</span>
            <span class="hljs-keyword">case</span> <span class="hljs-string">"s"</span>: stats.numberOfStructs <span class="hljs-operator">+=</span> <span class="hljs-number">1</span>
            <span class="hljs-keyword">case</span> <span class="hljs-string">"e"</span>: stats.numberOfEnums <span class="hljs-operator">+=</span> <span class="hljs-number">1</span>
            <span class="hljs-keyword">default</span>: <span class="hljs-keyword">break</span>
            }
        }
    }
}
</code></pre>
<p>Essentially, this walks through all the swift files in the package or selected targets and outputs various statistics.</p>
<p>To run it, we must first add it to a package as a dependency. Let's clone <a href="https://www.google.com/url?sa=t&#x26;rct=j&#x26;q=&#x26;esrc=s&#x26;source=web&#x26;cd=&#x26;ved=2ahUKEwiywL_Fzvv6AhVShFwKHdvXDzMQFnoECA0QAQ&#x26;url=https%3A%2F%2Fgithub.com%2Frealm%2FSwiftLint&#x26;usg=AOvVaw2wYDH76Z7-QFiV30y4Mvro">SwiftLint</a> and use it as an example.</p>
<p>Add <strong><code>CodeStats</code></strong> to its dependencies.</p>
<p><strong>Note:</strong> <strong><code>CodeStats</code></strong> and <strong><code>SwiftLint</code></strong> packages are in the same directory.</p>
<pre><code class="hljs language-swift">    <span class="hljs-operator">...</span>
    dependencies: [
        <span class="hljs-operator">...</span>,
        .package(path: <span class="hljs-string">"../CodeStats"</span>)
    ],
    <span class="hljs-operator">...</span>
</code></pre>
<p>Make sure it shows up under dependencies.</p>
<figure>
<img src="/assets/img/articles/2022-11-16-SPM-Plugins/package-dependencies.webp">
</figure>
<p>Then, right-click on the package and select <strong><code>GenerateCodeStats</code></strong>.</p>
<figure>
<img src="/assets/img/articles/2022-11-16-SPM-Plugins/context-menu.webp">
</figure>
<p>You should now be able to see a new file named <strong><code>CodeStats.md</code></strong> in the root directory.</p>
<figure>
<img src="/assets/img/articles/2022-11-16-SPM-Plugins/generated-file.webp">
</figure>
<p>It's also possible to run it from the command line using the verb that we've defined earlier.</p>
<pre><code class="hljs language-bash">$ swift package code-stats
<span class="hljs-comment">## Code statistics</span>

Number of files:         1224
Number of lines:       318508
Number of classes:       2116
Number of structs:       2431
Number of enums:          889
Number of protocols:      452
</code></pre>
<p>If you want to run these kinds of plugins on Xcode projects you just have to import <strong><code>XcodeProjectPlugin</code></strong> and make your plugin conform to <strong><code>XcodeCommandPlugin</code></strong>.</p>
<h2>Build Tool Plugins</h2>
<p>Build tool plugins are plugins that run whenever a target is built.
There are 2 different types of build tool commands, pre-build and in-build.
The main difference is that in-build commands have a pre-defined set of outputs.</p>
<p>These plugins are specially useful for generating and formatting code.</p>
<h3>In-Build Tool Example</h3>
<p>Let's build a plugin that auto-generates <strong><code>UserDefaults</code></strong> preferences based on a simple specification file.</p>
<p>Plugins can only depend on binary and executable targets, so let's create an executable target that will implement the code generation logic and then create a plugin that will call this executable.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Package.swift</span>
    <span class="hljs-operator">...</span>
    targets: [
        .executableTarget(
            name: <span class="hljs-string">"GenUserDefaultsExec"</span>,
            dependencies: []),
    ]
</code></pre>
<p>Here's the code generation.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Sources/GenUserDefaultsExec/Utilities.swift</span>
<span class="hljs-keyword">import</span> Foundation

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">DefaultValue</span> {
    <span class="hljs-keyword">var</span> name: <span class="hljs-type">String</span>
    <span class="hljs-keyword">var</span> type: <span class="hljs-type">String</span>
    <span class="hljs-keyword">var</span> value: <span class="hljs-type">String</span>?
}

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">String</span> {
    <span class="hljs-keyword">subscript</span>(<span class="hljs-params">range</span>: <span class="hljs-type">NSRange</span>) -> <span class="hljs-type">String</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-type">String</span>(<span class="hljs-keyword">self</span>[<span class="hljs-type">Range</span>(range, in: <span class="hljs-keyword">self</span>)<span class="hljs-operator">!</span>])
    }
}

<span class="hljs-keyword">func</span> <span class="hljs-title function_">parseSpecification</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">text</span>: <span class="hljs-type">String</span>) -> [<span class="hljs-type">DefaultValue</span>] {
    <span class="hljs-keyword">let</span> regex <span class="hljs-operator">=</span> <span class="hljs-keyword">try!</span> <span class="hljs-type">NSRegularExpression</span>(
        pattern: <span class="hljs-string">#"^\s*([_\w]+[_\d\w]+)\s+(\w[\d\w]*)\s*(.*)"#</span>)

    <span class="hljs-keyword">return</span> text.components(separatedBy: .newlines).compactMap { line <span class="hljs-keyword">in</span>
        <span class="hljs-keyword">let</span> range <span class="hljs-operator">=</span> <span class="hljs-type">NSRange</span>(location: <span class="hljs-number">0</span>, length: line.utf16.count)

        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> match <span class="hljs-operator">=</span> regex.firstMatch(in: line, range: range) <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
        }

        <span class="hljs-keyword">let</span> value <span class="hljs-operator">=</span> line[match.range(at: <span class="hljs-number">3</span>)]

        <span class="hljs-keyword">return</span> <span class="hljs-type">DefaultValue</span>(
            name: line[match.range(at: <span class="hljs-number">1</span>)],
            type: line[match.range(at: <span class="hljs-number">2</span>)],
            value: value.isEmpty <span class="hljs-operator">?</span> nil : value)
    }
}

<span class="hljs-keyword">func</span> <span class="hljs-title function_">generateCode</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">defaultValues</span>: [<span class="hljs-type">DefaultValue</span>]) -> <span class="hljs-type">String</span> {
    <span class="hljs-keyword">let</span> keys <span class="hljs-operator">=</span> defaultValues
        .map { <span class="hljs-string">"<span class="hljs-subst">\t</span><span class="hljs-subst">\t</span>public static let <span class="hljs-subst">\(<span class="hljs-variable">$0</span>.name)</span> = <span class="hljs-subst">\"</span><span class="hljs-subst">\(<span class="hljs-variable">$0</span>.name)</span><span class="hljs-subst">\"</span>"</span> }
        .joined(separator: <span class="hljs-string">"<span class="hljs-subst">\n</span>"</span>)
    <span class="hljs-keyword">let</span> values <span class="hljs-operator">=</span> defaultValues.map { v <span class="hljs-keyword">in</span>
        <span class="hljs-keyword">let</span> defValue <span class="hljs-operator">=</span> v.value <span class="hljs-operator">!=</span> <span class="hljs-literal">nil</span> <span class="hljs-operator">?</span> <span class="hljs-string">" ?? <span class="hljs-subst">\(v.value<span class="hljs-operator">!</span>)</span>"</span> : <span class="hljs-string">""</span>

        <span class="hljs-keyword">return</span> <span class="hljs-string">"""
        <span class="hljs-subst">\t</span>public var <span class="hljs-subst">\(v.name)</span>: <span class="hljs-subst">\(v.type)</span><span class="hljs-subst">\(v.value <span class="hljs-operator">==</span> <span class="hljs-literal">nil</span> <span class="hljs-operator">?</span> <span class="hljs-string">"?"</span> : <span class="hljs-string">""</span>)</span> {
        <span class="hljs-subst">\t</span><span class="hljs-subst">\t</span>get {
        <span class="hljs-subst">\t</span><span class="hljs-subst">\t</span><span class="hljs-subst">\t</span>return object(forKey: .Keys.<span class="hljs-subst">\(v.name)</span>) as? <span class="hljs-subst">\(v.type)</span><span class="hljs-subst">\(defValue)</span>
        <span class="hljs-subst">\t</span><span class="hljs-subst">\t</span>}
        <span class="hljs-subst">\t</span><span class="hljs-subst">\t</span>set {
        <span class="hljs-subst">\t</span><span class="hljs-subst">\t</span><span class="hljs-subst">\t</span>set(newValue, forKey: .Keys.<span class="hljs-subst">\(v.name)</span>)
        <span class="hljs-subst">\t</span><span class="hljs-subst">\t</span>}
        <span class="hljs-subst">\t</span>}
        """</span>
    }.joined(separator: <span class="hljs-string">"<span class="hljs-subst">\n</span><span class="hljs-subst">\n</span>"</span>)

    <span class="hljs-keyword">return</span> <span class="hljs-string">"""
    import Foundation<span class="hljs-subst">\n</span>
    extension String {<span class="hljs-subst">\n</span><span class="hljs-subst">\t</span>public enum Keys {<span class="hljs-subst">\n</span><span class="hljs-subst">\(keys)</span><span class="hljs-subst">\n</span><span class="hljs-subst">\t</span>}<span class="hljs-subst">\n</span>}<span class="hljs-subst">\n</span>
    extension UserDefaults {<span class="hljs-subst">\n</span><span class="hljs-subst">\(values)</span><span class="hljs-subst">\n</span>}
    """</span>
}
</code></pre>
<p>And here's the main script.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Sources/GenUserDefaultsExec/main.swift</span>
<span class="hljs-keyword">import</span> Foundation

<span class="hljs-keyword">func</span> <span class="hljs-title function_">abortProgram</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">errorCode</span>: <span class="hljs-type">Int32</span>) -> <span class="hljs-type">Never</span> {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">"Usage: GenUserDefaultsExec &#x3C;input spec> &#x3C;output>"</span>)
    exit(errorCode)
}

<span class="hljs-keyword">let</span> arguments <span class="hljs-operator">=</span> <span class="hljs-type">ProcessInfo</span>().arguments

<span class="hljs-keyword">guard</span> arguments.count <span class="hljs-operator">></span> <span class="hljs-number">2</span> <span class="hljs-keyword">else</span> {
    abortProgram(<span class="hljs-number">1</span>)
}

<span class="hljs-keyword">let</span> (input, output) <span class="hljs-operator">=</span> (arguments[<span class="hljs-number">1</span>], arguments[<span class="hljs-number">2</span>])
<span class="hljs-keyword">let</span> inputURL <span class="hljs-operator">=</span> <span class="hljs-type">URL</span>(fileURLWithPath: input)
<span class="hljs-keyword">let</span> outputURL <span class="hljs-operator">=</span> <span class="hljs-type">URL</span>(fileURLWithPath: output)

<span class="hljs-keyword">do</span> {
    <span class="hljs-keyword">let</span> spec <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">String</span>(contentsOf: inputURL)
    <span class="hljs-keyword">let</span> values <span class="hljs-operator">=</span> parseSpecification(spec)
    <span class="hljs-keyword">let</span> code <span class="hljs-operator">=</span> generateCode(values)

    <span class="hljs-keyword">try</span> code.write(to: outputURL, atomically: <span class="hljs-literal">true</span>, encoding: .utf8)
} <span class="hljs-keyword">catch</span> {
    <span class="hljs-built_in">print</span>(error)
    abortProgram(<span class="hljs-number">2</span>)
}
</code></pre>
<p>The only thing left is implementing the plugin.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Package.swift</span>
    <span class="hljs-operator">...</span>
    targets: [
        .plugin(
            name: <span class="hljs-string">"GenUserDefaults"</span>,
            capability: .buildTool(),
            dependencies: [<span class="hljs-string">"GenUserDefaultsExec"</span>]
        ),
        <span class="hljs-operator">...</span>
    ]
</code></pre>
<p>Build tool plugins need to conform to <strong><code>BuildToolPlugin</code></strong>.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Plugins/GenUserDefaults/GenUserDefaults.swift</span>
<span class="hljs-keyword">import</span> Foundation
<span class="hljs-keyword">import</span> PackagePlugin

<span class="hljs-keyword">@main</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">GenUserDefaults</span>: <span class="hljs-title class_">BuildToolPlugin</span> {
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">createBuildCommands</span>(<span class="hljs-params">context</span>: <span class="hljs-type">PluginContext</span>, <span class="hljs-params">target</span>: <span class="hljs-type">Target</span>) <span class="hljs-keyword">async</span> <span class="hljs-keyword">throws</span> -> [<span class="hljs-type">Command</span>] {
        <span class="hljs-keyword">let</span> tool <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> context.tool(named: <span class="hljs-string">"GenUserDefaultsExec"</span>)
        <span class="hljs-keyword">let</span> input <span class="hljs-operator">=</span> target.directory.appending([<span class="hljs-string">"defaults"</span>])
        <span class="hljs-keyword">let</span> output <span class="hljs-operator">=</span> context.pluginWorkDirectory
            .appending([<span class="hljs-string">"UserDefaultsPreferences.swift"</span>])

        <span class="hljs-keyword">return</span> [
            .buildCommand(
                displayName: <span class="hljs-string">"Generating UserDefaults Fields"</span>,
                executable: tool.path,
                arguments: [input.string, output.string],
                inputFiles: [input],
                outputFiles: [output])
        ]
    }
}
</code></pre>
<p>To test it, let's create a target called <strong><code>AppFeature</code></strong> that implements a simple SwiftUI view.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Package.swift</span>
    <span class="hljs-operator">...</span>
    targets: [
        .target(
            name: <span class="hljs-string">"AppFeature"</span>,
            plugins: [<span class="hljs-string">"GenUserDefaults"</span>]
        ),
        <span class="hljs-operator">...</span>
    ]
</code></pre>
<p>This plugin looks for a specification file called <strong><code>defaults</code></strong> in the root directory of the target. So let's create that file with the contents bellow.</p>
<pre><code class="hljs language-bash">appLaunchCounter Int 0
showOnboarding Bool <span class="hljs-literal">true</span>
userId String
</code></pre>
<p>Each line declares a preference separated by whitespace where the 3rd value is an optional default value.</p>
<p>Whenever we build this target, it will auto generate a file called <strong><code>UserDefaultsPreferences</code></strong> in the build directory, if needed.</p>
<p>We can now use UserDefaults preferences in a type-safe way.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Sources/AppFeature/AppFeature.swift</span>
<span class="hljs-keyword">import</span> Foundation
<span class="hljs-keyword">import</span> SwiftUI

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">AppFeatureView</span>: <span class="hljs-title class_">View</span> {
    <span class="hljs-meta">@AppStorage</span>(.<span class="hljs-type">Keys</span>.showOnboarding) <span class="hljs-keyword">var</span> showOnboarding: <span class="hljs-type">Bool</span> <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
        <span class="hljs-keyword">if</span> showOnboarding {
            <span class="hljs-type">Text</span>(<span class="hljs-string">"Onboarding"</span>)
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-type">Button</span> {
                <span class="hljs-built_in">print</span>(<span class="hljs-string">"UserId: <span class="hljs-subst">\(UserDefaults.standard.userId <span class="hljs-operator">??</span> <span class="hljs-string">""</span>)</span>"</span>)
            } label: {
                <span class="hljs-type">Text</span>(<span class="hljs-string">"AppFeature"</span>)
            }
        }
    }
}
</code></pre>
<p>You can check the output of the plugin by going to the Report Navigator which it's very useful for debugging issues.</p>
<figure>
<img src="/assets/img/articles/2022-11-16-SPM-Plugins/plugin-output.webp">
</figure>
<h3>Xcode Build tool plugins</h3>
<p>So far all plugins we've implemented only run on Packages. In order to make them run on Xcode targets you just need to conform to
<strong><code>XcodeCommandPlugin</code></strong> for Command type plugins and <strong><code>XcodeBuildToolPlugin</code></strong> for Build tool type plugins.
Let's add <strong><code>XcodeBuildToolPlugin</code></strong> support to our previous example.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">@main</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">GenUserDefaults</span>: <span class="hljs-title class_">BuildToolPlugin</span>, <span class="hljs-title class_">XcodeBuildToolPlugin</span> {
    <span class="hljs-operator">...</span>

    <span class="hljs-keyword">func</span> <span class="hljs-title function_">createBuildCommands</span>(
        <span class="hljs-params">context</span>: <span class="hljs-type">XcodeProjectPlugin</span>.<span class="hljs-type">XcodePluginContext</span>,
        <span class="hljs-params">target</span>: <span class="hljs-type">XcodeProjectPlugin</span>.<span class="hljs-type">XcodeTarget</span>
    ) <span class="hljs-keyword">throws</span> -> [<span class="hljs-type">PackagePlugin</span>.<span class="hljs-type">Command</span>] {
        <span class="hljs-keyword">let</span> tool <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> context.tool(named: <span class="hljs-string">"GenUserDefaultsExec"</span>)

        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> input <span class="hljs-operator">=</span> context.xcodeProject.filePaths
            .first(where: { <span class="hljs-variable">$0</span>.stem <span class="hljs-operator">==</span> <span class="hljs-string">"defaults"</span> <span class="hljs-operator">&#x26;&#x26;</span> <span class="hljs-variable">$0</span>.extension <span class="hljs-operator">==</span> <span class="hljs-literal">nil</span> })
        <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">throw</span> <span class="hljs-type">GenUserDefaultsError</span>.specificationFileNotFound
        }

        <span class="hljs-keyword">let</span> output <span class="hljs-operator">=</span> context.pluginWorkDirectory.appending([<span class="hljs-string">"UserDefaultsPreferences.swift"</span>])

        <span class="hljs-built_in">print</span>(<span class="hljs-string">"ℹ️ Input: <span class="hljs-subst">\(input)</span>"</span>)
        <span class="hljs-built_in">print</span>(<span class="hljs-string">"ℹ️ Output: <span class="hljs-subst">\(output)</span>"</span>)

        <span class="hljs-keyword">return</span> [
            .buildCommand(
                displayName: <span class="hljs-string">"Generate UserDefaults preferences"</span>,
                executable: tool.path,
                arguments: [input.string, output.string],
                inputFiles: [input],
                outputFiles: [output])
        ]
    }
}
</code></pre>
<p>Add the specification file to your project.</p>
<figure>
<img src="/assets/img/articles/2022-11-16-SPM-Plugins/defaults-file.webp">
</figure>
<p>And lastly go to build phases and add a new build tool plugin.</p>
<figure>
<img src="/assets/img/articles/2022-11-16-SPM-Plugins/xcode-build-tool.webp">
</figure>
<p>Ta-da! Now whenever your xcode project gets compiled, your UserDefaults fields will be automatically generated, if needed.</p>
<h3>Conclusion</h3>
<p>Package Plugins is a tool that every iOS and macOS developer should familiarize themselves with. It can be used to generate type-safe representations of assets,
localized strings, OpenAPI models, documentation, linting and more.</p>
<p>However, plugins are not without their faults. Implementing plugins can be frustrating at times. Not being able to depend on normal targets tripped me up at first.
I found myself closing Xcode 14 frequently because sometimes Xcode wouldn't clear compilation errors, so you didn't know whether they were actual errors or just Xcode acting up. Debugging is also cumbersome, you have to use the good old print. Xcode could be more stable too. Having said that, the benefits outweighs its quirks.</p>
<p>There you go, I hoped this tutorial was useful and that you give plugins a try.</p>
<h3>Related Articles</h3>
<ul>
<li><a href="https://developer.apple.com/videos/play/wwdc2022/110359/">Meet Swift Package plugins</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2022/110401">Create Swift Package plugins</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Blockchain is here]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/10/10/Blockchain-is-here</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/10/10/Blockchain-is-here</guid>
            <pubDate>Mon, 10 Oct 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Hi, this is Bitcoin and from now on it will live with us!<br>
Traditional money has long been outdated and is gradually losing its popularity.
In most cases, we already use electronic money.
But e-money is not much different from cash, except in form, but the essence remains the same.
According to the plan of evolution, everything weak in this world dies and gives way to something stronger.<br>
So <em><strong>cryptocurrency</strong></em> came to us.</p>
<h3>Let’s figure out what a cryptocurrency is and what is it eaten with?</h3>
<p>The main difference between a cryptocurrency and a regular one is that it is <em><strong>decentralized</strong></em> and exists exclusively in digital form.
In most cases, no one controls it, and the participants in the system are equal members. Such a system allows achieving a high level of trust.
And even if you block some participants, this will not prevent the rest from making transactions with other participants.
<img src="/assets/img/articles/2022-10-10-Blockchain-is-here/image01.webp" alt="">
Emission occurs due to the work of millions of computers around the world according to the following principles:</p>
<ul>
<li><em><strong>Proof of work</strong></em> is when a computer solves complex cryptographic problems in order to gain the right to confirm a transaction on the network, for which it will subsequently receive a reward</li>
<li><em><strong>Proof of Stake</strong></em> is when the most loyal member of the system gets the right to make a decision. The one who stacks (invests) more coins in the system.</li>
<li>Other consensus mechanisms</li>
</ul>
<p><img src="/assets/img/articles/2022-10-10-Blockchain-is-here/image02.webp" alt="">
<em><strong>Bitcoin</strong></em> is the first and most popular <em><strong>decentralized</strong></em> cryptocurrency system using <em><strong>blockchain</strong></em> technology.
In traditional currencies, there is a <em><strong>centralized</strong></em> guarantor of the honesty of transactions - <em><strong>Uncle Sam</strong></em> (usually the state or the Central Bank).
This is the one who decides whether to confirm your transaction or not.
But in cryptocurrencies like bitcoin, such a guarantor is not needed, since it has technology such as consensus mechanisms.
It allows you to check the integrity of the transaction, as well as confirm transactions.</p>
<p>The most important thing for us is that cryptocurrency cannot be banned or destroyed without depriving us of the Internet.
Therefore, cryptocurrencies still exist and develop, despite attempts to regulate them. Let’s look at the statistics.</p>
<h3>Bitcoin versus the Internet:</h3>
<p><img src="/assets/img/articles/2022-10-10-Blockchain-is-here/image03.webp" alt="">
As you can see from the graph, the popularity of bitcoin is growing twice as fast as the popularity of the internet.
This suggests that the probability of the complete destruction of the cryptocurrency is negligible and that it is time to get to know it better.</p>
<p>However, cryptocurrencies are just one of the blockchain-based creations. Using blockchain technology, you can create many different products that will have all the same features as cryptocurrencies and even more!</p>
<p>Have you heard about <em><strong>decentralized application</strong></em>?
<em><strong>dApp</strong></em> is a new generation of applications that run on the blockchain network and have all its advantages.
For example, the <em><strong>Ethereum</strong></em> blockchain has no downtime!
Can you imagine the backend which will never return you 500 Internal Server Error? So Etherium can do it!
A <em><strong>dApp</strong></em> can only stop working if the blockchain itself stops working.
It is common knowledge that large networks like <em><strong>Ethereum</strong></em> are difficult to attack.
<img src="/assets/img/articles/2022-10-10-Blockchain-is-here/image04.webp" alt="">
Simply put, a dapp is, for example, when an application uses blockchain networks as a backend. This means that in order to attack your app, a hacker will have to attack the entire network. And this is for a minute more than 8,000 deployed nodes. This is the same as eight thousand duplicating each other servers.</p>
<p>After all, the technology that carries the fashionable word today - <em><strong>cryptocurrency</strong></em>, is truly magnificent and has tremendous potential.</p>
<p>In this series of articles I will tell you about:</p>
<ul>
<li>How does cryptocurrency work and what kind of beast is this blockchain?</li>
<li>What is a crypto wallet and what types are there?</li>
<li>How to keep track of your own and other people’s transactions.</li>
<li>What is farming and stacking?</li>
<li>Blockchain as a backend for mobile applications.</li>
<li>And much much more…</li>
</ul>
<h2>Resources</h2>
<ul>
<li><a href="https://borisov.school/criptasutra">Criptasutra</a>\</li>
<li><a href="https://ethereum.org/en">Ethereum</a>\</li>
<li><a href="https://ethernodes.org">Ethereum Mainnet Statistics</a></li>
</ul>
<h3>Art</h3>
<ul>
<li>Article Photo by <a href="https://unsplash.com/es/@jonathanborba?utm_source=unsplash&#x26;utm_medium=referral&#x26;utm_content=creditCopyText">Jonathan Borba</a> on <a href="https://unsplash.com/?utm_source=unsplash&#x26;utm_medium=referral&#x26;utm_content=creditCopyText">Unsplash</a>\</li>
<li>Photo by <a href="https://storyset.com/technology">Technology illustrations</a> by <a href="https://storyset.com">Storyset</a>\</li>
<li>Photo by <a href="https://storyset.com/web">Web illustrations</a> by <a href="https://storyset.com">Storyset</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Handling bounced emails by SES with Rails (ActiveMailer)]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/10/05/Handling-bounced-emails-by-SES-with-Rails</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/10/05/Handling-bounced-emails-by-SES-with-Rails</guid>
            <pubDate>Wed, 05 Oct 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>If you are thinking of implementing a mailing system for your application, you would surely need to find a way to know if the sent emails are successfully delivered or not. With AWS SES (Simple Email Service), it is possible to set up bounce webhook notifications whenever an email failed to be delivered.</p>
<p>In one of my recent backend projects at Monstarlab, I needed to find a way to notify administrator users whenever an email sent from the Rails application (through AWS SES) fails to reach the recipients. In this article, I will explain the approach I took for implementing such a system.</p>
<h2>Prerequisite: AWS SES setup to send email from a particular domain</h2>
<p>The first thing you need to do is to create a domain identity for your domain through the AWS console by navigating to Amazon SES > Configuration > Verified identities.
You will then need to verify it to confirm that the domain you are using is effectively yours.</p>
<p>※ I will not go into the details for this setup. If you need some explanations about how to setup a particular domain for sending email, this guide from the AWS documentation will certainly help you: <a href="https://docs.aws.amazon.com/ses/latest/dg/creating-identities.html#verify-domain-procedure">https://docs.aws.amazon.com/ses/latest/dg/creating-identities.html#verify-domain-procedure</a></p>
<h2>Overview</h2>
<p>The objective is to find a way to notify administrator users whenever an email sent from the Rails application (through AWS SES) fails to reach the recipients.</p>
<p>For this, we came up with the below design in order to implement our solution.</p>
<ul>
<li>Rails will be configured to use AWS SES to send emails.</li>
<li>AWS SES will trigger a SNS topic subscription whenever a bounce is detected.</li>
<li>SNS will then trigger an AWS Lambda function that will send a POST request to the Rails application.</li>
<li>Rails application will then perform the appropriate action (in our case, notify the administrator users)</li>
</ul>
<p><img src="/assets/img/articles/2022-10-05-Handling-bounced-emails-by-SES-with-Rails/flow.webp" alt="Handling Bounced Emails by SES - Flow"></p>
<h2>Setting up AWS SES with SNS (Simple Notification Service) to send bounced notification</h2>
<p>Now, let’s dive into the mail topic: the bounce email handling.</p>
<p>By default, no notification of any sort is sent when AWS SES fails to deliver an email. We need to set it up manually through the AWS console by navigating to Amazon SES > Configuration > Verified identities > (your domain) > Notifications.</p>
<p><img src="/assets/img/articles/2022-10-05-Handling-bounced-emails-by-SES-with-Rails/flow.webp" alt="Verifying identities"></p>
<p>You can see that we can setup SNS topic for three types of event:</p>
<ul>
<li>Bounce (when the email cannot be delivered for some reason)</li>
<li>Complaint (when your sender email is marked as undesirable by the email recipient)</li>
<li>Delivery (when the email is successfully delivered)</li>
</ul>
<p>We need to create a SNS topic and assign it to the bounce notification.</p>
<p><div class="html5-video-container">
        <video width="400" controls>
          <source src="/assets/img/articles/2022-10-05-Handling-bounced-emails-by-SES-with-Rails/sns-topic-creation.mp4" type="video/mp4">
        Your browser does not support HTML5 video.
        </video>
      </div></p>
<h3>Creating a SNS Topic</h3>
<p>Following this, we will have to create a subscription to the created topic to be able to perform an action. In our case, we decided to send a webhook request to the Rails backend application so that the related administrator users for the bounced email can be notified.</p>
<h3>Creating a Lambda function for that topic</h3>
<p>The subscription will be an AWS Lambda function that will be called whenever there is a bounce notification. This function will send a request to the provided ENDPOINT_URL (which is an endpoint of our backend application) along with the SNS body data that is containing useful data for our backend to identify the bounced email (like the message ID and also the message body).</p>
<pre><code class="hljs language-python"><span class="hljs-keyword">import</span> json
<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">import</span> os

<span class="hljs-keyword">def</span> <span class="hljs-title function_">lambda_handler</span>(<span class="hljs-params">event, context</span>):
    url = <span class="hljs-string">"https://monstar-lab.com/aws/receive_bounce_notice"</span>

    <span class="hljs-comment"># Body of the request</span>
    <span class="hljs-comment"># In case the sns_message_payload["Type"] == 'SubscriptionConfirmation', the body will contain a url ('SubscribeURL'). The backend application will need to send a get request to that url to confirm it is ready to receive the notifications from now</span>
    <span class="hljs-comment"># Non exhaustive body example: { "SubscribeURL" => "https://aws.ses.com" }</span>
    <span class="hljs-comment">#</span>
		<span class="hljs-comment"># In case the sns_message_payload["Type"] == 'Notification', the body will contain data about the bounced email such as message ID, the sender and recipient(s) etc.</span>
    <span class="hljs-comment"># Non exhaustive body example:</span>
		<span class="hljs-comment">#   {</span>
		<span class="hljs-comment">#      ...</span>
    <span class="hljs-comment">#      "Message": "{\"mail\":{\"timestamp\":\"2022-08-24T13:01:50.867Z\",\"source\":\"no_reply@dev-sc-bcc.com\",\"sourceArn\":\"arn:aws:ses:ap-northeast-1:013046240018:identity/dev-sc-bcc.com\",\"sourceIp\":\"10.0.78.173\",\"callerIdentity\":\"ses-smtp-user.20220530-164025\",\"sendingAccountId\":\"013046240018\",\"messageId\":\"01060182cff1dd93-75aeab42-e753-4ef6-b977-347b64eccb89-000000\",\"destination\":[\"non-existing-email4@monstar-lab.com\"]}}",</span>
   <span class="hljs-comment">#</span>
    sns_message_payload = event[<span class="hljs-string">"Records"</span>][<span class="hljs-number">0</span>][<span class="hljs-string">"Sns"</span>]

    sns_message_headers = {
        <span class="hljs-string">"x-amz-sns-message-type"</span>: sns_message_payload[<span class="hljs-string">"Type"</span>], <span class="hljs-comment"># Can be either SubscriptionConfirmation (for checking the endpoint is ready to get notifications) or Notification (for a simple notification)</span>
    }

    <span class="hljs-keyword">try</span>:
        r = requests.post(url = url, data = json.dumps(sns_message_payload), headers = sns_message_headers)
        <span class="hljs-built_in">print</span>(r)
        <span class="hljs-keyword">return</span> {
            <span class="hljs-string">'statusCode'</span>: <span class="hljs-number">200</span>,
            <span class="hljs-string">'body'</span>: json.dumps(<span class="hljs-built_in">str</span>(r))
        }
    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
        <span class="hljs-built_in">print</span>(e)
        <span class="hljs-keyword">return</span> {
            <span class="hljs-string">'statusCode'</span>: <span class="hljs-number">500</span>,
            <span class="hljs-string">'body'</span>: json.dumps(<span class="hljs-built_in">str</span>(e))
        }
</code></pre>
<p>Once the lambda function, it is necessary to set the previously created SNS topic as the function trigger.</p>
<p>And voilà, we are done on the AWS side!
We have successfully setup a system that will send a webhook request with relevant data to the backend application whenever there is a email that has failed to deliver! 🥳</p>
<h2>Handling the notifications on the Rails side</h2>
<p>In the last part, we could have just sent an email to a particular email address instead of sending a webhook request to the application whenever there is a bounce notification. But in our case, depending on the email that was bounced, the recipients to be notified of this incident are different and only our backend application can identify them.</p>
<p>※ I will not explain the details of setting up the mailer inside Rails in this article.</p>
<p>In order, we will:</p>
<ul>
<li>Create a table for storing the emails that are being sent</li>
<li>Configure the mailer so that every time an email is sent, the data is saved into the database</li>
<li>Create a controller that will be responsible of receiving webhook requests in case of bouncing</li>
<li>Creating a cron job that will run periodically to check any bounced email and notify the corresponding administrator users</li>
<li>Creating another cron job that will periodically delete all non-bounced emails sent at least one day before</li>
</ul>
<p><img src="/assets/img/articles/2022-10-05-Handling-bounced-emails-by-SES-with-Rails/backend-flow.webp" alt="Handling SNS topic on Rails"></p>
<h3>Creating a table that will hold the email</h3>
<p>Below is the structure of the table that is going to store all the email data.</p>
<pre><code class="hljs language-ruby"><span class="hljs-keyword">class</span> <span class="hljs-title class_">CreateEmailDeliveryStatuses</span> &#x3C; <span class="hljs-title class_ inherited__">ActiveRecord::Migration</span>[<span class="hljs-number">7.0</span>]
  <span class="hljs-keyword">def</span> <span class="hljs-title function_">change</span>
    create_table <span class="hljs-symbol">:email_delivery_statuses</span> <span class="hljs-keyword">do</span> |<span class="hljs-params">t</span>|
      t.string <span class="hljs-symbol">:message_id</span>, <span class="hljs-symbol">null:</span> <span class="hljs-literal">false</span> <span class="hljs-comment"># Message ID generated by SES</span>
      t.references <span class="hljs-symbol">:user</span>, <span class="hljs-symbol">null:</span> <span class="hljs-literal">false</span>, <span class="hljs-symbol">polymorphic:</span> <span class="hljs-literal">true</span> <span class="hljs-comment"># User linked to that email</span>
      t.string <span class="hljs-symbol">:recipient_email</span>, <span class="hljs-symbol">null:</span> <span class="hljs-literal">false</span> <span class="hljs-comment"># Recipient email address</span>
      t.string <span class="hljs-symbol">:email_type</span>, <span class="hljs-symbol">null:</span> <span class="hljs-literal">false</span> <span class="hljs-comment"># Method name that trigger the email sending</span>
      t.datetime <span class="hljs-symbol">:sent_at</span>, <span class="hljs-symbol">null:</span> <span class="hljs-literal">false</span> <span class="hljs-comment"># Date and time of email sending</span>
      t.integer <span class="hljs-symbol">:delivery_status</span>, <span class="hljs-symbol">null:</span> <span class="hljs-literal">false</span>, <span class="hljs-symbol">default:</span> <span class="hljs-number">0</span> <span class="hljs-comment"># Delivery status that can be either "processing" or "bounced"</span>
      t.text <span class="hljs-symbol">:raw_bounced_notification</span> <span class="hljs-comment"># The bounce notification body (for debug purposes)</span>

      t.timestamps
    <span class="hljs-keyword">end</span>

    add_index <span class="hljs-symbol">:email_delivery_statuses</span>, <span class="hljs-symbol">:message_id</span>, <span class="hljs-symbol">unique:</span> <span class="hljs-literal">true</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<h3>Configuring the mailer to save sent email data</h3>
<p>Before diving into the code, one configuration is necessary in order to get the message ID generated by SES. You must set <code>return_response</code> to <code>true</code> inside the <code>smtp_settings</code>. Otherwise, it will be impossible to retrieve that ID allowing us to identify which email has bounced.</p>
<pre><code class="hljs language-ruby">config.action_mailer.smtp_settings = {
    <span class="hljs-symbol">address:</span> <span class="hljs-variable constant_">ENV</span>[<span class="hljs-string">"STMP_ADDRESS"</span>],
    <span class="hljs-symbol">port:</span> <span class="hljs-number">587</span>,
    <span class="hljs-symbol">domain:</span> <span class="hljs-variable constant_">ENV</span>[<span class="hljs-string">"STMP_DOMAIN"</span>],
    <span class="hljs-symbol">user_name:</span> <span class="hljs-variable constant_">ENV</span>[<span class="hljs-string">"STMP_USERNAME"</span>],
    <span class="hljs-symbol">password:</span> <span class="hljs-variable constant_">ENV</span>[<span class="hljs-string">"STMP_PASSWORD"</span>],
    <span class="hljs-symbol">authentication:</span> <span class="hljs-symbol">:login</span>,
    <span class="hljs-symbol">enable_starttls_auto:</span> <span class="hljs-literal">true</span>,
    <span class="hljs-symbol">return_response:</span> <span class="hljs-literal">true</span>
  }
</code></pre>
<p>Next, let’s see the code that will take care of the email sending.
We have the <code>ApplicationMailer</code> class inheriting <code>ActionMailer::Base</code> that contains the methods for effectively sending the emails (in our case, only <code>send_dummy_email</code>).</p>
<p>Notice that the default <code>delivery_job</code> is overriden by <code>Jobs::EmailDeliveryJob</code> that will take care of saving email data into the database each time a email sending method is called.</p>
<pre><code class="hljs language-python"><span class="hljs-comment"># typed: false</span>
<span class="hljs-comment"># frozen_string_literal: true</span>

<span class="hljs-keyword">class</span> <span class="hljs-title class_">ApplicationMailer</span> &#x3C; ActionMailer::Base
  self.delivery_job = Jobs::EmailDeliveryJob

  default <span class="hljs-keyword">from</span>: <span class="hljs-string">"no-reply@monstar-lab.com"</span>
  layout <span class="hljs-string">"mailer"</span>

	<span class="hljs-keyword">def</span> <span class="hljs-title function_">send_dummy_email</span>
<span class="hljs-meta">		@user = params[:user]</span>
	  mail(to: user.email, subject: <span class="hljs-string">"Dummy Email"</span>)
  end
end
</code></pre>
<pre><code class="hljs language-ruby"><span class="hljs-comment"># frozen_string_literal: true</span>

<span class="hljs-comment"># Job class inheriting ActionMailer::MailDeliveryJob</span>
<span class="hljs-comment"># This job will call the parent method (via super) for sending the email</span>
<span class="hljs-comment"># and then save the delivery data in the EmailDeliveryStatus table</span>
<span class="hljs-comment"># In case of a bounce notification sent by SNS, the failed email will be searchable via the message_id</span>
<span class="hljs-comment"># and then concerned parties will be notified of the mail delivery failure</span>

<span class="hljs-keyword">class</span> <span class="hljs-title class_">Jobs::EmailDeliveryJob</span> &#x3C; <span class="hljs-title class_ inherited__">ActionMailer::MailDeliveryJob</span>
  queue_as QueueConfig.queues[<span class="hljs-string">"default"</span>]

  <span class="hljs-keyword">def</span> <span class="hljs-title function_">perform</span>(<span class="hljs-params">mailer, mail_method, delivery_method, <span class="hljs-symbol">args:</span>, <span class="hljs-symbol">kwargs:</span> <span class="hljs-literal">nil</span>, <span class="hljs-symbol">params:</span> <span class="hljs-literal">nil</span></span>)
    response = <span class="hljs-variable language_">super</span> <span class="hljs-comment"># Call the parent ActionMailer::MailDeliveryJob `perform` method to send the email</span>

    <span class="hljs-comment"># Store the record inside EmailDeliveryStatus</span>
    user = params[<span class="hljs-symbol">:user</span>]

    <span class="hljs-comment"># Get the message ID generated by SES</span>
    <span class="hljs-comment"># example of response.string => "250 Ok 01060182ed4b24c8-e37b3174-b156-4032-b71b-0a636552035f-000000\n"</span>
    message_id = response.string[<span class="hljs-number">7</span>..<span class="hljs-number">66</span>]

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">if</span> user.blank?

    EmailDeliveryStatus.create!(
      <span class="hljs-symbol">message_id:</span> message_id,
      <span class="hljs-symbol">user:</span> user,
      <span class="hljs-symbol">recipient_email:</span> user.email,
      <span class="hljs-symbol">email_type:</span> <span class="hljs-string">"<span class="hljs-subst">#{mailer}</span>.<span class="hljs-subst">#{mail_method}</span>"</span>,
      <span class="hljs-symbol">sent_at:</span> Time.zone.now,
      <span class="hljs-symbol">delivery_status:</span> <span class="hljs-string">"processing"</span>
    )
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>※ Note: this article (in Japanese) helped me find a way to retrieve the message ID generated by SES: <a href="https://kossy-web-engineer.hatenablog.com/entry/2020/05/30/124520">https://kossy-web-engineer.hatenablog.com/entry/2020/05/30/124520</a></p>
<p>Now, every time a message is sent via the following code, an <code>EmailDeliveryStatus</code> record will be created with the message ID. We will be able to identify which user is related to that email and what kind of email it is.</p>
<pre><code class="hljs language-ruby"><span class="hljs-comment"># Sending en email to <span class="hljs-doctag">@user</span>.email</span>
ApplicationMailer.with(<span class="hljs-symbol">user:</span> <span class="hljs-variable">@user</span>).send_dummy_email.deliver_later!
</code></pre>
<h3>Creating a controller that will receive the AWS webhook requests</h3>
<p>Whenever a bounce happens, a request will be sent to the Rails application. The below controller will handle those requests and set the <code>delivery_status</code> to <code>bounced</code> of the <code>EmailDeliveryStatus</code> corresponding to the message ID included in the request body.</p>
<pre><code class="hljs language-ruby"><span class="hljs-comment"># frozen_string_literal: true</span>

<span class="hljs-comment">##</span>
<span class="hljs-comment"># Controller for receiving webhook requests coming from SNS (for bouncing)</span>
<span class="hljs-comment">#</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">AwsController</span> &#x3C; <span class="hljs-title class_ inherited__">ApplicationController</span>
  skip_before_action <span class="hljs-symbol">:verify_authenticity_token</span>

  before_action <span class="hljs-symbol">:verify_request_headers</span>
  before_action <span class="hljs-symbol">:confirm_sns_subscription</span>

  <span class="hljs-comment"># Endpoint for receiving webhook requests from AWS SNS Topic when an email fails to be delivered</span>
  <span class="hljs-comment"># via SES (probably because the recipient email does not exist or is not functioning anymore)</span>
  <span class="hljs-keyword">def</span> <span class="hljs-title function_">receive_bounce_notice</span>
    body = message_body
    message = <span class="hljs-variable constant_">JSON</span>.parse(body[<span class="hljs-string">"Message"</span>])
    message_id = message.dig(<span class="hljs-string">"mail"</span>, <span class="hljs-string">"messageId"</span>)

    <span class="hljs-keyword">if</span> message_id.blank?
      Rails.logger.error(<span class="hljs-string">"AwsController.receive_bounce_notice - Message ID  has not been found for bounced notification - Raw message body: <span class="hljs-subst">#{raw_message_body}</span>"</span>)
      <span class="hljs-keyword">return</span> head(<span class="hljs-symbol">:ok</span>)
    <span class="hljs-keyword">end</span>

    email_delivery_status = EmailDeliveryStatus.find_by(<span class="hljs-symbol">message_id:</span>)
    <span class="hljs-keyword">if</span> email_delivery_status.blank?
      Rails.logger.error(<span class="hljs-string">"AwsController.receive_bounce_notice - EmailDeliveryStatus has not been found for bounced message_id: <span class="hljs-subst">#{message_id}</span> - Raw message body: <span class="hljs-subst">#{raw_message_body}</span>"</span>)
      <span class="hljs-keyword">return</span> head(<span class="hljs-symbol">:ok</span>)
    <span class="hljs-keyword">end</span>

    email_delivery_status.update!(
      <span class="hljs-symbol">delivery_status:</span> <span class="hljs-string">"bounced"</span>,
      <span class="hljs-symbol">raw_bounced_notification:</span> raw_message_body
    )

    head(<span class="hljs-symbol">:ok</span>)
  <span class="hljs-keyword">end</span>

  private

  <span class="hljs-keyword">def</span> <span class="hljs-title function_">raw_message_body</span>
    <span class="hljs-variable">@raw_message_body</span> |<span class="hljs-params"></span>|= request.body.read
  <span class="hljs-keyword">end</span>

  <span class="hljs-keyword">def</span> <span class="hljs-title function_">message_body</span>
    <span class="hljs-variable">@message_body</span> |<span class="hljs-params"></span>|= <span class="hljs-variable constant_">JSON</span>.parse(raw_message_body)
  <span class="hljs-keyword">end</span>

  <span class="hljs-keyword">def</span> <span class="hljs-title function_">subscription_confirmation_message?</span>
    request.headers[<span class="hljs-string">"x-amz-sns-message-type"</span>] == <span class="hljs-string">"SubscriptionConfirmation"</span>
  <span class="hljs-keyword">end</span>

  <span class="hljs-keyword">def</span> <span class="hljs-title function_">notification_message?</span>
    request.headers[<span class="hljs-string">"x-amz-sns-message-type"</span>] == <span class="hljs-string">"Notification"</span>
  <span class="hljs-keyword">end</span>

  <span class="hljs-comment">#################################</span>
  <span class="hljs-comment">##### before_action methods #####</span>
  <span class="hljs-comment">#################################</span>

  <span class="hljs-keyword">def</span> <span class="hljs-title function_">verify_request_headers</span>
    head(<span class="hljs-symbol">:bad_request</span>) <span class="hljs-keyword">unless</span> subscription_confirmation_message? |<span class="hljs-params"></span>| notification_message?
  <span class="hljs-keyword">end</span>

  <span class="hljs-comment"># Handle first time SNS subscription mail</span>
  <span class="hljs-comment"># Send an HTTP GET request to the `SubscribeURL` in the request body</span>
  <span class="hljs-comment"># to confirm the SNS subscription</span>
  <span class="hljs-keyword">def</span> <span class="hljs-title function_">confirm_sns_subscription</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">unless</span> subscription_confirmation_message?

    subscribe_url = message_body[<span class="hljs-string">"SubscribeURL"</span>]
    head(<span class="hljs-symbol">:bad_request</span>) <span class="hljs-keyword">and</span> <span class="hljs-keyword">return</span> <span class="hljs-keyword">if</span> subscribe_url.blank?

    <span class="hljs-comment"># Send an HTTP GET request to the given URL to confirm the subscription</span>
    url = <span class="hljs-variable constant_">URI</span>.parse(subscribe_url)
    Net::<span class="hljs-variable constant_">HTTP</span>.start(url.host, url.port, <span class="hljs-symbol">use_ssl:</span> <span class="hljs-literal">true</span>) <span class="hljs-keyword">do</span> |<span class="hljs-params">http</span>|
      req = <span class="hljs-title class_">Net::HTTP::Get</span>.new(url)
      http.read_timeout = <span class="hljs-number">5</span>
      http.max_retries = <span class="hljs-number">0</span>
      http.request(req)
    <span class="hljs-keyword">end</span>
    render <span class="hljs-symbol">json:</span> { <span class="hljs-symbol">confirmed:</span> <span class="hljs-literal">true</span> }.to_json, <span class="hljs-symbol">status:</span> <span class="hljs-symbol">:ok</span>
  <span class="hljs-keyword">rescue</span> Errno::<span class="hljs-variable constant_">EADDRNOTAVAIL</span>, Net::ReadTimeout
    head(<span class="hljs-symbol">:bad_request</span>)
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<h3>Creating a cron job that will check those bounced emails</h3>
<p>At this stage, the bounced emails that were notified have their corresponding <code>EmailDeliveryStatus</code> record flagged as <code>bounced</code>. We now need to handle those bounced emails. For that, we will create a cron job that will run every 5 minutes to retrieve them, notify the administrators by email of this incident and then delete the processed <code>EmailDeliveryStatus</code> from the database so that they won’t be processed again.</p>
<pre><code class="hljs language-ruby"><span class="hljs-comment"># frozen_string_literal: true</span>

<span class="hljs-comment">##</span>
<span class="hljs-comment"># Check all the bounced emails and notify by email the corresponding administrator users</span>
<span class="hljs-comment"># Call every 5 minutes by cron job</span>
<span class="hljs-comment">#</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">CheckBouncedEmailDeliveryJob</span> &#x3C; <span class="hljs-title class_ inherited__">ApplicationJob</span>
  <span class="hljs-keyword">def</span> <span class="hljs-title function_">perform</span>
    EmailDeliveryStatus.bounced.find_each <span class="hljs-keyword">do</span> |<span class="hljs-params">email_delivery_status</span>|
      <span class="hljs-keyword">case</span> email_delivery_status.user_type
      <span class="hljs-keyword">when</span> <span class="hljs-string">"ItUser"</span>
        <span class="hljs-comment"># Notify IT department administrators</span>
        ItUser.admins.find_each <span class="hljs-keyword">do</span> |<span class="hljs-params">admin</span>|
          ItUser::BounceMailer.with(<span class="hljs-symbol">user:</span> admin, <span class="hljs-symbol">email_delivery_status:</span>).send_bounce_notification.deliver_later!
        <span class="hljs-keyword">end</span>
      <span class="hljs-keyword">when</span> <span class="hljs-string">"SalesUser"</span>
        <span class="hljs-comment"># Notify Sales department administrators</span>
        SalesUser.users.find_each <span class="hljs-keyword">do</span> |<span class="hljs-params">admin</span>|
          SalesUser::BounceMailer.with(<span class="hljs-symbol">user:</span> admin, <span class="hljs-symbol">email_delivery_status:</span>).send_bounce_notification.deliver_later!
        <span class="hljs-keyword">end</span>
      <span class="hljs-keyword">when</span> <span class="hljs-string">"MarketingUser"</span>
        <span class="hljs-comment"># Notify Marketing department administrators</span>
        MarketingUser.users.find_each <span class="hljs-keyword">do</span> |<span class="hljs-params">admin</span>|
          MarketingUser::BounceMailer.with(<span class="hljs-symbol">user:</span> admin, <span class="hljs-symbol">email_delivery_status:</span>).send_bounce_notification.deliver_later!
        <span class="hljs-keyword">end</span>
      <span class="hljs-keyword">end</span>

      email_delivery_status.destroy!
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p><strong>Notes</strong></p>
<p>In the above situation, if one of the administrators’ email also bounces, that may create a infinite loop situation in which the bounce notification will keep coming because of the same administrator user whose email is bouncing.</p>
<p>To counter this, inside the mailers for sending the bounce notification only, we will not be using the <code>EmailDeliveryJob</code> as the <code>delivery_job</code> and will be using instead the default one which is <code>ActionMailer::MailDeliveryJob</code>.</p>
<pre><code class="hljs language-ruby"><span class="hljs-comment">##</span>
<span class="hljs-comment"># Mailer for sending bounce notification the administrator users</span>
<span class="hljs-comment">#</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">ItUser::BounceMailer</span> &#x3C; <span class="hljs-title class_ inherited__">ApplicationMailer</span>
  <span class="hljs-variable language_">self</span>.delivery_job = ActionMailer::MailDeliveryJob <span class="hljs-comment"># Set back to default because infinite loop may be trigerred if the bounce mail is sent to an email that failed to work previously</span>
<span class="hljs-keyword">end</span>
</code></pre>
<h3>Creating a cron job that will delete all EmailDeliveryStatus that did not bounce</h3>
<p>The last thing we need to do is to destroy the EmailDeliveryStatus of the emails that did not bounce in the last 24 hours. It is wasteful to keep them in the database anymore so we will just destroy them.</p>
<pre><code class="hljs language-ruby"><span class="hljs-comment"># frozen_string_literal: true</span>
<span class="hljs-comment"># Destroy all EmailDeliveryStatus that did not bounce in the last 24 hours</span>
<span class="hljs-comment"># Call every 5 minutes by cron job</span>
<span class="hljs-comment">#</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">DestroyNonBouncedEmailDeliveryStatusJob</span> &#x3C; <span class="hljs-title class_ inherited__">ApplicationJob</span>
  <span class="hljs-keyword">def</span> <span class="hljs-title function_">perform</span>
    EmailDeliveryStatus.processing.where(<span class="hljs-symbol">sent_at:</span> ..Time.zone.now.ago(<span class="hljs-number">1</span>.day)).destroy_all
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<h2>Conclusion</h2>
<p>Thank you a lot for reading!
Now, you know how to handle SES bounce notifications due to recipient email that does not exist with a Rails backend application.</p>
<p>This solution can be further improved by for example setting up a <code>delivery</code> SNS topic that would send a webhook request whenever an email is delivered successfully and let the backend destroy the associated <code>EmailDeliveryStatus</code> so that we do not need the <code>DestroyNonBouncedEmailDeliveryStatusJob</code> job anymore.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/hETJ5PBJDKw">Simona Sergi</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A cheat sheet for acing your coding test when applying for a job at the Monstarlab Tokyo office]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/09/30/Coding-Test</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/09/30/Coding-Test</guid>
            <pubDate>Fri, 30 Sep 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Hi there. I'm An, a Front-end Tech Lead at Monstarlab, Inc. (“Monstarlab”.)</p>
<p>If you are intimidated by coding tests, this article will wash your test anxieties away.
You will be able to optimally allocate time on the evaluation sections and be evaluated on your best performance!</p>
<p><img src="/assets/img/articles/2022-03-25-Coding-Test/tokyo-office.webp" alt=""></p>
<p><strong><em>The coding test described in this article is used at Monstarlab Tokyo Head office when recruiting engineers. Actual tests may differ, or not be conducted at all, depending on job type or hiring location.</em></strong></p>
<h2>What’s the purpose of a coding test?</h2>
<p>The purpose is to check what isn’t covered in the oral interview. Since source code speaks of an engineer’s unique style and experiences, it can show the candidate’s level of achievement.</p>
<p>A Tech Lead at Monstarlab is required to have both engineering and managerial skills. While complex and deeply nested codes are excellent, evaluation criteria include codes to be understandable from a general perspective and have usability in future operations. It’s also used as a measure to assess whether the candidate is an optimum fit for Monstarlab’s team environment which is based on teamwork.</p>
<p>There are 11 evaluation criteria and based on the average points earned, the evaluator makes the final decision. Although average points are important, the key is whether you are a team player and someone the company wants as part of the team.</p>
<p><em>The following is based on the March 2022 evaluation criteria. Please note that criteria may be partially changed depending on the assignment or modified in the future.</em></p>
<h3>General sections</h3>
<p>This is an overall check in a general sense. Some sections are fixed depending on the area and may not be included in the score.</p>
<h4>General Info</h4>
<p>This general section covers platform, framework and language which may be common in the environment for some assignments. It may be excluded from scoring unless there is a specific and applicable element to the given assignment.</p>
<p>Take Frontend as an example. The general environment for Web application is common and therefore excluded from the score.</p>
<h4>Architecture</h4>
<p>This section looks into the choice of architectural pattern. Basically, focus is placed on the intent behind the selection of architecture out of the various patterns.</p>
<p>Technically speaking, evaluating an architecture is not easy and it is not expected in the assignment. This is to check if one of the commonly used modern day architecture is being used.</p>
<h3>In-depth sections</h3>
<p>Your coding skills are checked in these sections.</p>
<h4>Setup/Build</h4>
<p><strong>Are there any issues with the setup and building of the project?</strong></p>
<p>This section covers the setup and building of a project. Initial behavior is checked based on README and others, and points are earned if everything is executed without issues. Once you proceed with development, there may be cases of local dependencies, but the idea is to see if you have handled these correctly.</p>
<p>If possible, check the setup and build it in a different environment than the one you used to develop and make sure that there are no issues. If there are specific tools or libraries required for the project to build, be sure to demonstrate that. You do not need to explain language installation but include anything that is particular, and not general.</p>
<h4>Dependency/Library</h4>
<p><strong>How are the project dependencies?</strong></p>
<p>This section covers libraries. This section checks whether you set up a dependency with a library that is suitable for the project, especially if the library is managed and updated. Using an outdated library which is impracticable in actual operation will cause point reduction.</p>
<p>What this means from a <code>javascript</code> Front End perspective, this section checks to see if you properly separated <code>dependencies</code> and <code>devDependencies</code> and if the library version is up to date. Determining an old library is difficult, but the basic evaluation criteria are LTS (Long Term Support) or npm versions. The differences between libraries are taken into consideration.</p>
<h4>Tests</h4>
<p><strong>Are test cases set up? Did all the test cases pass?</strong></p>
<p>This is a section on unit testing. There is no need to create test cases for all the code. The evaluation is on whether test cases are created for the most critical logic.</p>
<h4>Readability</h4>
<p><strong>Is your code readable?</strong></p>
<p>This section deals with readability.</p>
<h4>Code Style</h4>
<p><strong>Is the code style optimal for the platform?</strong></p>
<p>Although code style differs by platform, this section checks whether it was created appropriately from an up-to-date and general standpoint. Based on consistent usage of unified space, tab, indent, and vertical sort, this section checks consistency in programming grammar (eslint, tslint, beautify, and others are acceptable for this part) and mixed use of old and new grammar.</p>
<h4>Code Comments</h4>
<p><strong>Did you write comments?</strong></p>
<p>Similar to the test section, you will not be evaluated on whether you have comments for all logics or not but if you provided appropriate code comments for the critical business logics that must be commented on.</p>
<h4>Code Structure</h4>
<p><strong>How is the structure of the source code organized?</strong></p>
<p>This section checks if the tree structure is appropriately organized with intention. Any tree structure is acceptable, but evaluation is based on a form that is easy to understand for the platform and framework being used and designed with future expandability.</p>
<p>An example of an extreme case that would cause point reduction is managing all files in a single folder.</p>
<h3>Others</h3>
<p>This section includes maintenance and additional elements based on the evaluator’s experience.</p>
<h4>Maintainability</h4>
<p><strong>How is maintainability based on the code you created?</strong></p>
<p>The code structure, code style, and the ease in which features are added, changed, or deleted based on readability are evaluated.</p>
<h4>Other considerations</h4>
<p>This section includes items that are not part of the 10 sections mentioned above, or those that can be considered to be additional points when all sections are linked together, even if they are covered in another section. This also includes sections that are not mandatory in a coding test but included as additional features in the test assignment.</p>
<p>It’s hard to give an example because there are numerous areas, but it includes whether user satisfaction is met when the actual service is released, and the presence of a DevOps element that eliminates repetitive work.</p>
<p><img src="/assets/img/articles/2022-03-25-Coding-Test/tech-1.webp" alt=""></p>
<h2>Conclusion</h2>
<p>I’ve explained the scoring criteria for Monstarlab’s coding test.</p>
<p>The minimal criteria checks if an acceptable level of business performance is demonstrated, and individually established know-how based on current trends are utilized appropriately based on a general standard.</p>
<p>In other words, Monstarlab is not looking for 100% correct answers in a coding test but rather uses the test as a way to measure the finished quality as a product and the level of your skills.</p>
<p><img src="/assets/img/articles/2022-03-25-Coding-Test/tech-2.webp" alt=""></p>
<p>This has become too lengthy to be considered as a cheat sheet, but as a last note, I’d like to share my personal advice. Instead of focusing on writing codes to receive good evaluation, assume that you are creating a product that will be released and envision yourself as a user who would want to continue using it.</p>
<p>Since all evaluators are engineers who are working on-site, your dedication will be fully communicated through your code. I believe the love and passion instilled in the product are the biggest evaluation criteria.</p>
<p>I hope this helps and look forward to working with you at Monstarlab.</p>
<h2>Reference</h2>
<ul>
<li><a href="https://www.join.monstar-lab.com/">モンスターラボ採用ページ(Japanese)</a></li>
</ul>
<p><em>Article Photo by <a href="https://unsplash.com/photos/8q6e5hu3Ilc">Clay Banks</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introduction to Kotlin Flows]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/09/30/Introduction-to-Kotlin-flows</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/09/30/Introduction-to-Kotlin-flows</guid>
            <pubDate>Fri, 30 Sep 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>A Kotlin flow is an asynchronous stream of data that you can subscribe to and receive objects one by one or all at once.
It's like subscribing to a newspaper. You don't have to go to the newsstand every time and check if they have something new today. You just subscribed and every time they have a new newspaper, they will automatically send it to you.
Flow is also part of the Coroutines framework and therefore allows you to process the received data asynchronously.</p>
<p>Figuratively speaking, Flow consists of three objects:</p>
<ul>
<li>Creator (Producer)</li>
<li>Intermediary (Operator)</li>
<li>Consumer (Collector)</li>
</ul>
<p>For example, The New York Times writes newspapers - they are the <strong>creators</strong>.
The newsstand where this newspaper is sold, sorted, filtered, and so on - is the <strong>Intermediary</strong>.
And we, my friend, are - the <strong>Consumers</strong>.</p>
<p><strong>So there are two types of flows:</strong>
Hot and cold. By default they are cold.
<strong>Cold</strong>, this is when the creator begins to create a newspaper, only after you have subscribed to it.
<strong>Hot</strong> ones are when they start creating a newspaper immediately after they have been created.</p>
<p>I'll show you both examples, but for now, let's start with creating regular cold streams since they are the standard.</p>
<h3>COLD FLOW</h3>
<p><em>"Until you turn on the faucet and start collecting water, it will not start flowing."</em>
<img src="/assets/img/articles/2022-09-30-Introduction-to-Kotlin-Flows/image01.webp" alt=""></p>
<h5>Below is an example of a cold stream:</h5>
<pre><code class="hljs language-Kotlin"><span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span>: <span class="hljs-built_in">Unit</span> = runBlocking {
    <span class="hljs-keyword">val</span> newspaperFlow = produceNewspapers()
    println(<span class="hljs-string">"flow created"</span>)
    println(<span class="hljs-string">"flow collection started"</span>)
    newspaperFlow.collect {
        delay(<span class="hljs-number">100</span>)
        println(<span class="hljs-string">"collected: <span class="hljs-variable">$it</span>"</span>)
    }
}
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">produceNewspapers</span><span class="hljs-params">()</span></span> = flow {
    println(<span class="hljs-string">"flow started"</span>)
    <span class="hljs-keyword">val</span> newspapers = listOf(<span class="hljs-string">"The Guardian"</span>, <span class="hljs-string">"Medium"</span>, <span class="hljs-string">"Reddit"</span>)
    <span class="hljs-keyword">for</span> (item <span class="hljs-keyword">in</span> newspapers) {
        println(<span class="hljs-string">"Producing new newspaper: <span class="hljs-variable">$item</span>"</span>)
        emit(item)
    }
}
</code></pre>
<h5>Here is what we get as a result:</h5>
<pre><code class="hljs language-Kotlin">flow created
flow collection started
flow started
Producing new newspaper: The Guardian
collected: The Guardian
Producing new newspaper: Medium
collected: Medium
Producing new newspaper: Reddit
collected: Reddit
</code></pre>
<p>You could try it here: <a href="https://pl.kotl.in/2Hyqc0gVk">https://pl.kotl.in/2Hyqc0gVk</a></p>
<p>As you can see, the thread didn't start producing the paper until I subscribed to it.</p>
<h3>HOT FLOW</h3>
<p><em>"Water will flow, even if you do not collect it."</em>
<img src="/assets/img/articles/2022-09-30-Introduction-to-Kotlin-Flows/image02.webp" alt=""></p>
<h5>Below is an example of a hot stream:</h5>
<pre><code class="hljs language-Kotlin"><span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span>: <span class="hljs-built_in">Unit</span> = runBlocking {
    withTimeoutOrNull(<span class="hljs-number">1000</span>) {
        <span class="hljs-keyword">val</span> mutableSharedFlow = MutableSharedFlow&#x3C;String>()
        <span class="hljs-keyword">val</span> sharedFlow = mutableSharedFlow.asSharedFlow()
        launch {
            <span class="hljs-keyword">for</span> (i <span class="hljs-keyword">in</span> <span class="hljs-number">1.</span><span class="hljs-number">.5</span>) {
                delay(<span class="hljs-number">100</span>)
                println(<span class="hljs-string">"emitted <span class="hljs-variable">$i</span>"</span>)
                mutableSharedFlow.emit(<span class="hljs-string">"Newspaper - <span class="hljs-variable">$i</span>"</span>)
            }
        }
        launch {
            delay(<span class="hljs-number">100</span>)
            sharedFlow.collect {
                println(<span class="hljs-string">"Shop A collected: <span class="hljs-variable">$it</span>"</span>)
            }
        }
        launch {
            delay(<span class="hljs-number">300</span>)
            sharedFlow.collect {
                println(<span class="hljs-string">"Shop B collected: <span class="hljs-variable">$it</span>"</span>)
            }
        }
    }
}
</code></pre>
<h5>Here is the result:</h5>
<pre><code class="hljs language-Kotlin">emitted <span class="hljs-number">1</span>
emitted <span class="hljs-number">2</span>
Shop A collected: Newspaper - <span class="hljs-number">2</span>
emitted <span class="hljs-number">3</span>
Shop A collected: Newspaper - <span class="hljs-number">3</span>
Shop B collected: Newspaper - <span class="hljs-number">3</span>
emitted <span class="hljs-number">4</span>
Shop A collected: Newspaper - <span class="hljs-number">4</span>
Shop B collected: Newspaper - <span class="hljs-number">4</span>
emitted <span class="hljs-number">5</span>
Shop A collected: Newspaper - <span class="hljs-number">5</span>
Shop B collected: Newspaper - <span class="hljs-number">5</span>
</code></pre>
<p>Here is the playground: <a href="https://pl.kotl.in/PgWJ-1KnV">https://pl.kotl.in/PgWJ-1KnV</a></p>
<p>As you can see, our newspaper continues to be published regardless of whether we are subscribed to it or not.
The first newspaper was missed by both stores. The second newspaper ended up on the shelf only in the first store.
But the third newspaper and all subsequent ones have already been on sale at both stores.
The important thing here is that <strong>Shared flow never completes</strong>. That's why we started <strong>withTimeoutOrNull()</strong>.</p>
<p>Below you can see different options for creating and processing flows.
Here is an example of how else you can create a Flow:</p>
<pre><code class="hljs language-Kotlin">flowOf(“Mars”, “Twix”, “Snickers”)
listOf(“Pepsi”,“Cola”,“Fanta”).asFlow()
(<span class="hljs-number">1.</span><span class="hljs-number">.3</span>).asFlow()
</code></pre>
<h3>OPERATORS</h3>
<p>An equally important point for flows is the operators. These are tools that help process, filter, edit, etc. flow data at the mediation stage.</p>
<h5>For example:</h5>
<pre><code class="hljs language-Kotlin"><span class="hljs-keyword">suspend</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">addMark</span><span class="hljs-params">(drink: <span class="hljs-type">String</span>)</span></span>: String {
    delay(<span class="hljs-number">500</span>)
    <span class="hljs-keyword">return</span> <span class="hljs-string">"EU: <span class="hljs-variable">$drink</span>"</span>
}
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> = runBlocking {
    <span class="hljs-keyword">val</span> drinksFlow = flowOf(<span class="hljs-string">"Coca-Cola"</span>, <span class="hljs-string">"Kofola Zero"</span>, <span class="hljs-string">"Fanta Zero"</span>, <span class="hljs-string">"Sprite"</span>, <span class="hljs-string">"Coca-Cola Zero"</span>)
    drinksFlow
        .map { addMark(it) } <span class="hljs-comment">// apply some transformation</span>
        .filter { it.contains(<span class="hljs-string">"Zero"</span>) } <span class="hljs-comment">// take only those that do not contain sugar</span>
        .take(<span class="hljs-number">2</span>) <span class="hljs-comment">// take only the first two results</span>
        .collect { println(it) } <span class="hljs-comment">// print results</span>
}
</code></pre>
<p>Try it here: <a href="https://pl.kotl.in/qm9OpTL3T">https://pl.kotl.in/qm9OpTL3T</a></p>
<p>Let's figure out together what's going on here.
We have a drink data flow. 2 standard and 3 drinks without sugar.</p>
<ol>
<li><strong>.map</strong> - here we add a quality label to each product, so "Fanta" becomes "EU: Fanta"
As a result of this operator, our flow will turn into a flow with the following data:
[“EU: Coca-Cola", "EU: Kofola Zero", "EU: Fanta Zero", "EU: Sprite", "EU: Coca-Cola Zero”]</li>
<li><strong>.filter</strong> - then we filter the stream with new data. The result will be a flow with data like this:
[“EU: Kofola Zero”, “EU: Fanta Zero”, “EU: Coca-Cola Zero”]</li>
<li><strong>take(2)</strong> - this operator will return the flow to the first 2 values ​​in the list. The result will be the following:
[“EU: Kofola Zero”, “EU: Fanta Zero”]</li>
<li>Well, in the end, we call a different type of operator, which will launch this whole machine for collecting, editing and display data to us in the log. Let's run this code and check what we got.</li>
</ol>
<pre><code class="hljs language-Kotlin">EU: Kofola Zero
EU: Fanta Zero

Process finished with exit code <span class="hljs-number">0</span>
</code></pre>
<p>If you notice, I omitted the <strong>.collect</strong> operator, and that's because it is not a <strong>terminal operator</strong>, but an <strong>intermediate operator</strong>.</p>
<p>Yes, there are two types of operators in flows, but it's easy.
Essentially, the only difference is that <strong>terminal operators</strong> take a flow and return a flow, while <strong>intermediate operators</strong> take a flow but return a specific value.</p>
<h3>Here are examples of terminal statements:</h3>
<ul>
<li><strong>collect()</strong>: returns all data from the flow</li>
<li><strong>first()</strong>: returns the first value from the flow</li>
<li><strong>last()</strong>: returns the last value from the flow</li>
<li><strong>toList()</strong>: converts flow values ​​into a List collection.</li>
<li><strong>toSet()</strong>: converts  flow values ​​into a Set collection.</li>
<li><strong>count()</strong>: returns the amount of elements in the flow.</li>
<li><strong>reduce()</strong>: accumulates a value starting from the first element and applying operations to them. If there are no values, it will return an error</li>
<li><strong>fold()</strong>: the same as reduce, but it takes an initial value and if the result is empty, it will return the initial value instead of an error.</li>
</ul>
<h3>And here are the options for intermediate operators:</h3>
<ul>
<li><strong>map()</strong>: transform data into something else</li>
<li><strong>filter()</strong>: you are already familiar with this, it filters the flow, leaving only those elements that match the condition</li>
<li><strong>onEach()</strong>: applies a specific function to flow elements before returning them</li>
<li><strong>combine()</strong>: combines two flow into one</li>
<li><strong>zip()</strong>: using the transform function creates one flow from two</li>
<li><strong>take()</strong>: returns a specified number of elements from the stream</li>
<li><strong>transform()</strong>: applies a transform function to the elements of the flow</li>
<li><strong>drop(n)</strong>: returns a flow that ignores the first n elements</li>
</ul>
<p>So, we got acquainted with a very important technology that developers use daily.
Now you know what flows are, what kind of flows are there and how to use them.
Try, practice, and enjoy coding.
Thanks to everybody, I wish you a good flow! Bye! Bye!</p>
<p>Resources:</p>
<ul>
<li><a href="https://developer.android.com/kotlin/flow">Kotlin Flow Documentation</a></li>
</ul>
<p><em>Article Photo by <a href="https://unsplash.com/@zhenhappy?utm_source=unsplash&#x26;utm_medium=referral&#x26;utm_content=creditCopyText">PAN XIAOZHEN</a></em>,
<a href="https://unsplash.com/@dreamevile?utm_source=unsplash&#x26;utm_medium=referral&#x26;utm_content=creditCopyText">Raphael Koh</a><em>,
<a href="https://unsplash.com/@lyovon?utm_source=unsplash&#x26;utm_medium=referral&#x26;utm_content=creditCopyText">Levon Vardanyan</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to implement realtime updates with Live Activities]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/09/30/Live-Activities</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/09/30/Live-Activities</guid>
            <pubDate>Fri, 30 Sep 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Live Activities were introduced in iOS 16.1 as a way to display up-to-date information on the iPhone Lock screen and in the Dynamic Island. In this article, we're going to explore the Live Activity API by implementing a fake Delivery App where the user will receive updates of their order right on their phone's Lock Screen. This feature is still in beta at the time of writing this article. For more details check out the <a href="https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities">Official Documentation</a>.</p>
<p>So, let's get started!</p>
<h2>Step 1: Add Live Activities support</h2>
<p>In the Info.plist of the main target add the following key <strong><code>NSSupportsLiveActivities</code></strong> with its value set to <strong><code>YES</code></strong>.</p>
<h2>Step 2: Implement Activity Attributes</h2>
<p>We first need to create the model that will be used to update the interface of the Live Activity and Dynamic Island.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">enum</span> <span class="hljs-title class_">FoodDeliveryState</span>: <span class="hljs-title class_">String</span>, <span class="hljs-title class_">Codable</span> {
    <span class="hljs-keyword">case</span> processing
    <span class="hljs-keyword">case</span> prepping
    <span class="hljs-keyword">case</span> delivering
    <span class="hljs-keyword">case</span> completed
}

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">FoodDeliveryAttributes</span>: <span class="hljs-title class_">ActivityAttributes</span> {
    <span class="hljs-keyword">typealias</span> <span class="hljs-type">DeliveryState</span> <span class="hljs-operator">=</span> <span class="hljs-type">ContentState</span>

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">ContentState</span>: <span class="hljs-title class_">Codable</span>, <span class="hljs-title class_">Hashable</span> {
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> timestamp: <span class="hljs-type">TimeInterval</span>
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> message: <span class="hljs-type">String</span>?
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> courier: <span class="hljs-type">String</span>
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> deliveryState: <span class="hljs-type">FoodDeliveryState</span>
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> orderName: <span class="hljs-type">String</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> restaurant: <span class="hljs-type">String</span>
}
</code></pre>
<p>All fields declared in <strong><code>FoodDeliveryAttributes</code></strong> are static, which means they'll remain the same once we start the Live Activity. In contrast, the fields in <strong><code>FoodDeliveryAttributes.ContentState</code></strong> may be updated while the Activity is running.</p>
<p><strong><code>ContentState</code></strong> must conform to <em>Codable</em> as Activities can be updated using remote push notifications where the content state is passed as JSON. More on that later.</p>
<h2>Step 3: Create the UI</h2>
<p>Live activities use WidgetKit and SwiftUI for their user interface. Our UI will look like this:</p>
<figure>
<img src="/assets/img/articles/2022-09-30-Live-Activities/lockscreen-ui.webp">
<figcaption>Figure 1. Live Activity UI</figcaption>
</figure>
<pre><code class="hljs language-swift"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">FoodDeliveryWidgetView</span> {
    <span class="hljs-meta">@State</span> <span class="hljs-keyword">var</span> staticData: <span class="hljs-type">FoodDeliveryAttributes</span>
    <span class="hljs-meta">@State</span> <span class="hljs-keyword">var</span> dynamicData: <span class="hljs-type">FoodDeliveryAttributes</span>.<span class="hljs-type">DeliveryState</span>
    <span class="hljs-keyword">var</span> isCompact: <span class="hljs-type">Bool</span> <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> timestampFormatted: <span class="hljs-type">String</span> {
        <span class="hljs-keyword">let</span> timestampDate <span class="hljs-operator">=</span> <span class="hljs-type">Date</span>(timeIntervalSince1970: dynamicData.timestamp)
        <span class="hljs-keyword">let</span> formattedDate <span class="hljs-operator">=</span> <span class="hljs-keyword">Self</span>.hourMinute.string(from: timestampDate)

        <span class="hljs-keyword">return</span> <span class="hljs-string">"Last update: <span class="hljs-subst">\(formattedDate)</span>"</span>
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> infoText: <span class="hljs-type">String</span>? {
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> message <span class="hljs-operator">=</span> dynamicData.message {
            <span class="hljs-keyword">let</span> msg <span class="hljs-operator">=</span> message.replacingOccurrences(
                of: <span class="hljs-string">"[COURIER]"</span>,
                with: dynamicData.courier)

            <span class="hljs-keyword">return</span> <span class="hljs-string">"ⓘ <span class="hljs-subst">\(msg)</span>"</span>
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
        }
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> deliveryState: <span class="hljs-type">String</span> {
        <span class="hljs-keyword">switch</span> dynamicData.deliveryState {
        <span class="hljs-keyword">case</span> .processing:
            <span class="hljs-keyword">return</span> <span class="hljs-string">"Processing order"</span>
        <span class="hljs-keyword">case</span> .prepping:
            <span class="hljs-keyword">return</span> <span class="hljs-string">"<span class="hljs-subst">\(staticData.restaurant)</span> is preparing your food"</span>
        <span class="hljs-keyword">case</span> .delivering:
            <span class="hljs-keyword">return</span> <span class="hljs-string">"<span class="hljs-subst">\(dynamicData.courier)</span> is on their way"</span>
        <span class="hljs-keyword">case</span> .completed:
            <span class="hljs-keyword">return</span> <span class="hljs-string">"Your food has been delivered"</span>
        }
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> hourMinute: <span class="hljs-type">DateFormatter</span> <span class="hljs-operator">=</span> {
        <span class="hljs-keyword">let</span> formatter <span class="hljs-operator">=</span> <span class="hljs-type">DateFormatter</span>()
        formatter.dateFormat <span class="hljs-operator">=</span> <span class="hljs-string">"HH:mm"</span>
        <span class="hljs-keyword">return</span> formatter
    }()
}

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">FoodDeliveryWidgetView</span>: <span class="hljs-title class_">View</span> {
    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
        <span class="hljs-type">VStack</span>(alignment: .leading, spacing: <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> infoText {
                <span class="hljs-type">Text</span>(infoText)
                    .textStyle(.body)
                    .frame(maxWidth: .infinity)
                    .padding(.bottom, isCompact <span class="hljs-operator">?</span> <span class="hljs-number">0</span> : <span class="hljs-number">6</span>)
            }

            <span class="hljs-type">HStack</span> {
                <span class="hljs-type">RoundedRectangle</span>(cornerRadius: <span class="hljs-number">4</span>)
                    .stroke(lineWidth: <span class="hljs-number">1</span>)
                    .frame(width: <span class="hljs-number">44</span>, height: <span class="hljs-number">44</span>, alignment: .center)
                    .overlay {
                        dynamicData.deliveryState.image
                            .resizable()
                            .scaledToFit()
                            .padding(<span class="hljs-number">5</span>)
                    }

                <span class="hljs-type">VStack</span>(alignment: .leading) {
                    <span class="hljs-type">Text</span>(staticData.restaurant)
                        .textStyle(.title)
                    <span class="hljs-type">Text</span>(staticData.orderName)
                        .textStyle(.body)
                }

                <span class="hljs-type">Spacer</span>()
            }

            <span class="hljs-type">Text</span>(deliveryState)
                .textStyle(.title)
                .padding(.top, isCompact <span class="hljs-operator">?</span> <span class="hljs-number">2</span> : <span class="hljs-number">8</span>)

            <span class="hljs-type">Text</span>(timestampFormatted)
                .textStyle(.caption)
        }
        .frame(maxWidth: .infinity)
        .padding(.horizontal, <span class="hljs-number">16</span>)
        .padding(.vertical, isCompact <span class="hljs-operator">?</span> <span class="hljs-number">8</span> : <span class="hljs-number">16</span>)
    }
}
</code></pre>
<p>This is the main UI for our Live Activity and Dynamic Island when expanded. The reason we have the <strong><code>isCompact</code></strong> flag is to allow us to use the same view for the Live Activity, as well as the Dynamic Island despite Live Activities having more vertical real estate available to them than the Dynamic Island.</p>
<h2>Step 4: Create the Widget</h2>
<p>Creating a Widget is pretty straightforward. If we just want to create a Live Activity we only need to do:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">@main</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">FoodDeliveryLiveActivity</span>: <span class="hljs-title class_">Widget</span> {
    <span class="hljs-keyword">let</span> kind: <span class="hljs-type">String</span> <span class="hljs-operator">=</span> <span class="hljs-string">"FoodDeliveryWidget"</span>

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">WidgetConfiguration</span> {
        <span class="hljs-type">ActivityConfiguration</span>(for: <span class="hljs-type">FoodDeliveryAttributes</span>.<span class="hljs-keyword">self</span>) { context <span class="hljs-keyword">in</span>
            <span class="hljs-comment">// Lock Screen view</span>
        } dynamicIsland: { context <span class="hljs-keyword">in</span>
            <span class="hljs-type">DynamicIsland</span> {
                <span class="hljs-comment">// Dynamic Island Expanded view</span>
            } compactLeading: {
                <span class="hljs-comment">// Dynamic Island compact leading view</span>
            } compactTrailing: {
                <span class="hljs-comment">// Dynamic Island compact trailing view</span>
            } minimal: {
                <span class="hljs-comment">// Dynamic Island minimal view</span>
            }
        }
    }
}

</code></pre>
<p>However, if we already have a Widget or want to provide one alongside our Live Activity we must create a <strong>WidgetBundle</strong> to <em>bundle</em> our Widget and our LiveActivity together, provided that the user is on iOS 16.1+.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">@main</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">FoodDeliveryWidgetBundle</span>: <span class="hljs-title class_">WidgetBundle</span> {
    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">Widget</span> {
        <span class="hljs-type">FoodDeliveryWidget</span>()

        <span class="hljs-keyword">if</span> <span class="hljs-keyword">#available</span>(<span class="hljs-keyword">iOS</span> <span class="hljs-number">16.1</span>, <span class="hljs-operator">*</span>) {
            <span class="hljs-type">FoodDeliveryLiveActivity</span>()
        }
    }
}
</code></pre>
<p>Next, we create the view for the Live Activity and the views for the Dynamic Island. In our example, we've used the same view as our Live Activity for our expanded Dynamic Island bottom view.</p>
<figure>
<img src="/assets/img/articles/2022-09-30-Live-Activities/expanded-di-view.webp">
<figcaption>Figure 2. Dynamic Island in expanded mode</figcaption>
</figure>
<pre><code class="hljs language-swift"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">FoodDeliveryWidget</span>: <span class="hljs-title class_">Widget</span> {
    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">WidgetConfiguration</span> {
        <span class="hljs-comment">// Here is where you'd implement your Timeline based Widget</span>
        <span class="hljs-type">EmptyWidgetConfiguration</span>()
    }
}

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">FoodDeliveryLiveActivity</span>: <span class="hljs-title class_">Widget</span> {
    <span class="hljs-keyword">let</span> kind: <span class="hljs-type">String</span> <span class="hljs-operator">=</span> <span class="hljs-string">"FoodDeliveryWidget"</span>

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">WidgetConfiguration</span> {
        <span class="hljs-type">ActivityConfiguration</span>(for: <span class="hljs-type">FoodDeliveryAttributes</span>.<span class="hljs-keyword">self</span>) { context <span class="hljs-keyword">in</span>
            <span class="hljs-comment">// 1 - Live Activity View</span>
            <span class="hljs-type">FoodDeliveryWidgetView</span>(
                staticData: context.attributes,
                dynamicData: context.state)
        } dynamicIsland: { context <span class="hljs-keyword">in</span>
            <span class="hljs-comment">// 2 - Dynamic Island view</span>
            <span class="hljs-type">DynamicIsland</span> {
                <span class="hljs-comment">// 2.1 - Expanded view</span>
                <span class="hljs-type">DynamicIslandExpandedRegion</span>(.bottom) {
                    <span class="hljs-type">FoodDeliveryWidgetView</span>(
                        staticData: context.attributes,
                        dynamicData: context.state,
                        isCompact: <span class="hljs-literal">true</span>)
                }
            } compactLeading: {
                <span class="hljs-comment">// 2.2 - Compact leading view</span>
                context.state.deliveryState.image
                    .resizable()
                    .scaledToFit()
                    .foregroundColor(.white)
                    .padding(<span class="hljs-number">8</span>)
            } compactTrailing: {
                <span class="hljs-comment">// 2.3 - Compact trailing view</span>
                <span class="hljs-keyword">if</span> context.state.message <span class="hljs-operator">!=</span> <span class="hljs-literal">nil</span> {
                    <span class="hljs-type">Image</span>(systemName: <span class="hljs-string">"info.circle.fill"</span>)
                        .resizable()
                        .scaledToFit()
                        .foregroundColor(.white)
                        .padding(<span class="hljs-number">8</span>)
                }
            } minimal: {
                <span class="hljs-comment">// Empty view</span>
            }
        }
    }
}

</code></pre>
<p>In the compact leading position, we display the state of the delivery and in the compact trailing position, we show ⓘ to alert the user that there's a message.</p>
<figure>
<img src="/assets/img/articles/2022-09-30-Live-Activities/compact-design.webp">
<figcaption>Figure 3. Dynamic Island in compact mode</figcaption>
</figure>
<p>Here is how all possible regions are defined.</p>
<figure>
<img src="/assets/img/articles/2022-09-30-Live-Activities/dynamic-island-areas.webp">
<figcaption>Figure 4. Taken from Apple Developer docs.</figcaption>
</figure>
<h2>Step 4: Start the Live Activity</h2>
<p>Live Activities can only be started when the app is in the foreground but can be updated and terminated in the background using ActivityKit or remote push notifications. First, let's look how we manage the state of Live Activities using ActivityKit. For that intent, let's create a helper class so that we can start, update and end Live Activities.</p>
<p><strong>Note:</strong> It is possible to have multiple Live Activities of the same type running, but to keep things simple, in our example we will only have 1.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">public</span> <span class="hljs-keyword">enum</span> <span class="hljs-title class_">LiveActivityControllerError</span>: <span class="hljs-title class_">Error</span> {
    <span class="hljs-keyword">case</span> liveActivitiesNotSupported
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LiveActivityController</span>&#x3C;<span class="hljs-title class_">Attributes</span>: <span class="hljs-title class_">ActivityAttributes</span>>: <span class="hljs-title class_">ObservableObject</span> {
    <span class="hljs-meta">@Published</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">private(set)</span> <span class="hljs-keyword">var</span> isRunning: <span class="hljs-type">Bool</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">weak</span> <span class="hljs-keyword">var</span> delegate: <span class="hljs-type">LiveActivityManagerDelegate</span>?
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> onPushTokenUpdate: (<span class="hljs-type">Data</span>) -> <span class="hljs-type">Void</span> <span class="hljs-operator">=</span> { data <span class="hljs-keyword">in</span>
        <span class="hljs-built_in">print</span>(<span class="hljs-string">"Push token: <span class="hljs-subst">\(data.hexFormatted)</span>"</span>)
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> activitySubject <span class="hljs-operator">=</span> <span class="hljs-type">CurrentValueSubject</span>&#x3C;<span class="hljs-type">Activity</span>&#x3C;<span class="hljs-type">Attributes</span>>?, <span class="hljs-type">Never</span>>(<span class="hljs-literal">nil</span>)
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> tokenUpdatesTask: <span class="hljs-type">Task</span>&#x3C;<span class="hljs-type">Void</span>, <span class="hljs-type">Error</span>>?
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> subscriptions <span class="hljs-operator">=</span> <span class="hljs-type">Set</span>&#x3C;<span class="hljs-type">AnyCancellable</span>>()
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> activity: <span class="hljs-type">Activity</span>&#x3C;<span class="hljs-type">Attributes</span>>? {
        <span class="hljs-keyword">get</span> { <span class="hljs-keyword">return</span> activitySubject.value }
        <span class="hljs-keyword">set</span> { activitySubject.value <span class="hljs-operator">=</span> newValue }
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">init</span>() {
        <span class="hljs-comment">// Resume running Live Activity if present</span>
        <span class="hljs-keyword">let</span> runningActivity <span class="hljs-operator">=</span> <span class="hljs-type">Activity</span>&#x3C;<span class="hljs-type">Attributes</span>>.activities.first
        isRunning <span class="hljs-operator">=</span> runningActivity <span class="hljs-operator">!=</span> <span class="hljs-literal">nil</span>

        <span class="hljs-comment">// Update isRunning publisher depending on the state of the activity subject</span>
        activitySubject
            .map { <span class="hljs-variable">$0</span> <span class="hljs-operator">!=</span> <span class="hljs-literal">nil</span> }
            .receive(on: <span class="hljs-type">DispatchQueue</span>.main)
            .sink { [<span class="hljs-keyword">weak</span> <span class="hljs-keyword">self</span>] isRunning <span class="hljs-keyword">in</span>
                <span class="hljs-keyword">self</span><span class="hljs-operator">?</span>.isRunning <span class="hljs-operator">=</span> isRunning
            }
            .store(in: <span class="hljs-operator">&#x26;</span>subscriptions)

        activity <span class="hljs-operator">=</span> runningActivity
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">start</span>(
        <span class="hljs-params">staticData</span>: <span class="hljs-type">Attributes</span>,
        <span class="hljs-params">contentState</span>: <span class="hljs-type">Attributes</span>.<span class="hljs-type">ContentState</span>
    ) <span class="hljs-keyword">async</span> <span class="hljs-keyword">throws</span> {
        <span class="hljs-comment">// 1. Check if device supports Live Activities and that they are enabled</span>
        <span class="hljs-keyword">guard</span> <span class="hljs-type">ActivityAuthorizationInfo</span>().areActivitiesEnabled <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">throw</span> <span class="hljs-type">LiveActivityControllerError</span>.liveActivitiesNotSupported
        }

        <span class="hljs-comment">// 2. Cancel running activities of type `Attributes`</span>
        <span class="hljs-keyword">for</span> activity <span class="hljs-keyword">in</span> <span class="hljs-type">Activity</span>&#x3C;<span class="hljs-type">Attributes</span>>.activities {
            <span class="hljs-keyword">await</span> activity.end(using: <span class="hljs-literal">nil</span>, dismissalPolicy: .immediate)
        }

        <span class="hljs-comment">// 3. Create Activity</span>
        activity <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">Activity</span>&#x3C;<span class="hljs-type">Attributes</span>>.request(
            attributes: staticData,
            contentState: contentState,
            pushType: .token)

        <span class="hljs-comment">// 4. Listen to push token updates</span>
        tokenUpdatesTask<span class="hljs-operator">?</span>.cancel()
        tokenUpdatesTask <span class="hljs-operator">=</span> <span class="hljs-type">Task</span>.detached {
            <span class="hljs-keyword">for</span> <span class="hljs-keyword">await</span> tokenData <span class="hljs-keyword">in</span> <span class="hljs-keyword">self</span>.activity<span class="hljs-operator">!</span>.pushTokenUpdates {
                <span class="hljs-keyword">self</span>.onPushTokenUpdate(tokenData)
                <span class="hljs-keyword">self</span>.delegate<span class="hljs-operator">?</span>.tokenUpdated(tokenData)
            }
        }

        <span class="hljs-built_in">print</span>(<span class="hljs-string">"Started activity with ID: <span class="hljs-subst">\(activity<span class="hljs-operator">!</span>.id)</span>"</span>)
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">update</span>(
        <span class="hljs-keyword">_</span> <span class="hljs-params">contentState</span>: <span class="hljs-type">Attributes</span>.<span class="hljs-type">ContentState</span>,
        <span class="hljs-params">alertConfiguration</span>: <span class="hljs-type">AlertConfiguration</span>? <span class="hljs-operator">=</span> <span class="hljs-literal">nil</span>
    ) <span class="hljs-keyword">async</span> {
        <span class="hljs-keyword">await</span> activity<span class="hljs-operator">?</span>.update(using: contentState, alertConfiguration: alertConfiguration)
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">end</span>(
        <span class="hljs-keyword">_</span> <span class="hljs-params">contentState</span>: <span class="hljs-type">Attributes</span>.<span class="hljs-type">ContentState</span>,
        <span class="hljs-params">dismissalPolicy</span>: <span class="hljs-type">ActivityUIDismissalPolicy</span> <span class="hljs-operator">=</span> .default
    ) <span class="hljs-keyword">async</span> {
        <span class="hljs-keyword">await</span> activity<span class="hljs-operator">?</span>.end(using: contentState, dismissalPolicy: dismissalPolicy)

        <span class="hljs-keyword">self</span>.activity <span class="hljs-operator">=</span> <span class="hljs-literal">nil</span>
    }
}
</code></pre>
<p>In order to start our Activity, we call <strong><code>start</code></strong> with its static and initial content state as parameters.</p>
<h2>Step 5: Update the Live Activity with ActivityKit</h2>
<p>Updating Live Activities with ActivityKit is easy, just like <strong><code>start</code></strong>, we call <strong><code>update</code></strong> with the desired changes.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> contentState <span class="hljs-operator">=</span> <span class="hljs-type">FoodDeliveryAttributes</span>.<span class="hljs-type">DeliveryState</span>(
    timestamp: <span class="hljs-type">Date</span>().timeIntervalSince1970,
    message: <span class="hljs-literal">nil</span>,
    courier: <span class="hljs-string">"John"</span>,
    deliveryState: .prepping)

<span class="hljs-keyword">await</span> activityController.update(contentState)
</code></pre>
<h2>Step 6: Update Live Activity with remote Push Notifications</h2>
<p>First we need to add <code>Push Notifications</code> to our supported capabilities and second we need to register for remote notifications.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">class</span> <span class="hljs-title class_">AppDelegate</span>: <span class="hljs-title class_">NSObject</span>, <span class="hljs-title class_">UIApplicationDelegate</span> {
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">application</span>(
        <span class="hljs-keyword">_</span> <span class="hljs-params">application</span>: <span class="hljs-type">UIApplication</span>,
        <span class="hljs-params">didFinishLaunchingWithOptions</span> <span class="hljs-params">launchOptions</span>: [<span class="hljs-type">UIApplication</span>.<span class="hljs-params">LaunchOptionsKey</span> : <span class="hljs-keyword">Any</span>]<span class="hljs-operator">?</span> <span class="hljs-operator">=</span> <span class="hljs-literal">nil</span>
    ) -> <span class="hljs-type">Bool</span> {
        <span class="hljs-type">UIApplication</span>.shared.registerForRemoteNotifications()

        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>
    }
}
</code></pre>
<p>Once we have push notification set up we can send notifications using APNS (Apple Push Notification Service) API. Here's a modified script taken from <a href="https://developer.apple.com/documentation/usernotifications/sending_push_notifications_using_command-line_tools">Apple's Documentation</a>.</p>
<pre><code class="hljs language-bash"><span class="hljs-meta">#!/usr/bin/env bash</span>

ENVIRONMENT=dev <span class="hljs-comment"># (dev|prod)</span>

TEAM_ID=<span class="hljs-string">"&#x3C;Team ID>"</span>
TOKEN_KEY_FILE_NAME=<span class="hljs-string">"&#x3C;Path to your private AuthKey .p8 file>"</span>
AUTH_KEY_ID=<span class="hljs-string">"&#x3C;Auth Key ID>"</span>
TOPIC=<span class="hljs-string">"&#x3C;Main Target Bundle ID>.push-type.liveactivity"</span>
DEVICE_TOKEN=<span class="hljs-string">"&#x3C;Push token returned from pushTokenUpdates>"</span>

<span class="hljs-comment"># Set host name</span>
<span class="hljs-keyword">if</span> [ <span class="hljs-string">"<span class="hljs-variable">$ENVIRONMENT</span>"</span> = <span class="hljs-string">"dev"</span> ]; <span class="hljs-keyword">then</span>
    APNS_HOST_NAME=api.sandbox.push.apple.com
<span class="hljs-keyword">else</span>
    APNS_HOST_NAME=api.push.apple.com
<span class="hljs-keyword">fi</span>

JWT_ISSUE_TIME=$(date +%s)
JWT_HEADER=$(<span class="hljs-built_in">printf</span> <span class="hljs-string">'{ "alg": "ES256", "kid": "%s" }'</span> <span class="hljs-string">"<span class="hljs-variable">${AUTH_KEY_ID}</span>"</span> | openssl base64 -e -A | tr -- <span class="hljs-string">'+/'</span> <span class="hljs-string">'-_'</span> | tr -d =)
JWT_CLAIMS=$(<span class="hljs-built_in">printf</span> <span class="hljs-string">'{ "iss": "%s", "iat": %d }'</span> <span class="hljs-string">"<span class="hljs-variable">${TEAM_ID}</span>"</span> <span class="hljs-string">"<span class="hljs-variable">${JWT_ISSUE_TIME}</span>"</span> | openssl base64 -e -A | tr -- <span class="hljs-string">'+/'</span> <span class="hljs-string">'-_'</span> | tr -d =)
JWT_HEADER_CLAIMS=<span class="hljs-string">"<span class="hljs-variable">${JWT_HEADER}</span>.<span class="hljs-variable">${JWT_CLAIMS}</span>"</span>
JWT_SIGNED_HEADER_CLAIMS=$(<span class="hljs-built_in">printf</span> <span class="hljs-string">"<span class="hljs-variable">${JWT_HEADER_CLAIMS}</span>"</span> | openssl dgst -binary -sha256 -sign <span class="hljs-string">"<span class="hljs-variable">${TOKEN_KEY_FILE_NAME}</span>"</span> | openssl base64 -e -A | tr -- <span class="hljs-string">'+/'</span> <span class="hljs-string">'-_'</span> | tr -d =)
AUTHENTICATION_TOKEN=<span class="hljs-string">"<span class="hljs-variable">${JWT_HEADER}</span>.<span class="hljs-variable">${JWT_CLAIMS}</span>.<span class="hljs-variable">${JWT_SIGNED_HEADER_CLAIMS}</span>"</span>

PAYLOAD=$(cat &#x3C;&#x3C;-<span class="hljs-string">END
    {
        "aps": {
            "timestamp": ${JWT_ISSUE_TIME},
            "event": "update",
            "content-state": {
                "timestamp": ${JWT_ISSUE_TIME},
                "message": null,
                "courier": "Daniel",
                "deliveryState": "prepping"
            }
        }
    }
END</span>
)

curl -v \
    --header <span class="hljs-string">"apns-topic: <span class="hljs-variable">$TOPIC</span>"</span> \
    --header <span class="hljs-string">"apns-push-type: liveactivity"</span> \
    --header <span class="hljs-string">"authorization: bearer <span class="hljs-variable">$AUTHENTICATION_TOKEN</span>"</span> \
    --data <span class="hljs-string">"<span class="hljs-variable">${PAYLOAD}</span>"</span> \
    --http2 https://<span class="hljs-variable">${APNS_HOST_NAME}</span>/3/device/<span class="hljs-variable">${DEVICE_TOKEN}</span>
</code></pre>
<p>The most important part of this script is the payload. You must have an <strong><code>event</code></strong> field which has 2 possible values, <strong><code>update</code></strong> and <strong><code>end</code></strong>. It does exactly what it says on the tin, <strong><code>update</code></strong> refreshes the state of the Activity with the data passed in <strong><code>content-state</code></strong> and <strong><code>end</code></strong> terminates the Activity. The Activity stays on the Lock Screen for about 4 hours.</p>
<p><strong>Important:</strong> The schema of ContentState must match the one used by the ActivityKit, in this case, <strong><code>FoodDeliveryAttributes.ContentState</code></strong>.</p>
<h2>Step 7: End the Activity</h2>
<p>Activities can be terminated by either calling ActivityKit's <code>end</code> method</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> contentState <span class="hljs-operator">=</span> <span class="hljs-type">FoodDeliveryAttributes</span>.<span class="hljs-type">DeliveryState</span>(
    timestamp: <span class="hljs-type">Date</span>().timeIntervalSince1970,
    message: <span class="hljs-literal">nil</span>,
    courier: <span class="hljs-string">"John"</span>,
    deliveryState: .completed)

<span class="hljs-keyword">await</span> activityController.end(contentState)
</code></pre>
<p>or, as mentioned above, by sending a remote push notification with <strong><code>event</code></strong> set to <strong><code>end</code></strong>.</p>
<p>When using ActivityKit, we can specify precisely when we want the Live Activity to be dismissed. We can choose among 3 dismissal policies.</p>





















<table><thead><tr><th>Dismissal Policy</th><th>When is the Activity dismissed from Lock Screen</th></tr></thead><tbody><tr><td><strong>.default</strong></td><td>After up to 4 hours</td></tr><tr><td><strong>.immediate</strong></td><td>Immediately removed</td></tr><tr><td><strong>.after(Date)</strong></td><td>At a specified Date within the next 4 hours</td></tr></tbody></table>
<h2>Conclusion</h2>
<p>It should now be clear how Live Activities and the Dynamic Island may be used to keep your users
up-to-date. We didn't do it here but, like Widgets, Live Activities also support <strong><code>Links</code></strong>, which means, we could've added a <em>Contact The Driver</em> <strong><code>Link</code></strong> to our Live Activity and whenever the user tapped it we'd start a phone call or send an SMS. Another thing to explore in the future is using Background Tasks to update the Activity.</p>
<p>There you have it! We hope you enjoyed our walk-through of Live Activities.</p>
<h3>Related Articles</h3>
<ul>
<li><a href="https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities">Official Documentation</a></li>
<li><a href="https://blog.appcircle.io/article/live-activities-in-ios16">Developing with Live Activities API in iOS 16</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[iOS Accessibility Support Within SwiftUI]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/09/01/Accessibility-support-with-SwiftUI</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/09/01/Accessibility-support-with-SwiftUI</guid>
            <pubDate>Thu, 01 Sep 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Accessibility in our iOS applications is something we should all strive for. To be inclusive and have our applications available to all is a massive goal for us at Monstarlab in every project we undertake. To accommodate to each user, whatever their needs, is extremely important. I wanted to write this blog post to share some of my learnings from adding various accessibility support to recent projects within Monstarlab using SwiftUI. I think a lot of developers will be taken aback by how simple adding support is. Below is a list of some of the various accessibility features iOS provide and how you can add them into your application.</p>
<h2>Voice Control</h2>
<p>Voice control allows people who may not be able to physically interact with their device full control over the application without the requirement of touch. It allows users to tap buttons, request to scroll up or down and all the various gestures you expect using iOS such as swiping and zooming.</p>
<p>Voice control mostly comes fully supported with iOS without much further development required. iOS Voice Control can automatically acknowledge UI elements that can be interacted with. Then with a number of commands, users can control the app using their voice, for example: Saying “Tap ‘Home Button’”, will act as if the user has tapped on the button labelled Home. For buttons that do not have text labels, accessibility labels will need to be provided. You can also use accessibility labels if you wish to have a different name used in Voice Control from the text displayed in the UI.</p>
<pre><code class="hljs language-swift">    <span class="hljs-type">Button</span>(<span class="hljs-string">"Let's GO!"</span>) {
        <span class="hljs-comment">//action</span>
    }.accessibilityLabel(“<span class="hljs-type">Start</span> <span class="hljs-type">Button</span>”)
</code></pre>
<p>At any point during use, users can request “Show me what to say”, at this point, iOS will suggest some voice commands the user could use on the current screen.</p>
<p>![Voice Commands image](/assets/img/articles/2022-09-01-Accessibility-support-with-SwiftUI/commands.webp</p>
<p>Some custom UI elements can be hard to add Voice Control support for, we should always aim to use native iOS components where possible as these will always have Voice Control support. One example of this is our application had a custom slider UI element, for Voice Control support, we had to add extra control buttons to allow Voice Control support, as it was controlled using different swipe/slide gestures not available using Voice Control.</p>
<p>You can read more about iOS Voice Control <a href="https://support.apple.com/en-gb/guide/iphone/iph2c21a3c88/ios">here</a>.</p>
<h2>Voice Over</h2>
<p>Voice over is an accessibility feature available on iOS allowing users to have UI elements read to them.</p>
<p>These elements are read from the top down by default, but the order can be defined by the developer if required. For example, if you had two labels displayed but wish to read them bottom to top, you could use the <code>accessibilitySortPriority</code> view modifier property like so. With these, higher numbers are sorted first (the default sort priority is zero).</p>
<pre><code class="hljs language-swift">    <span class="hljs-type">VStack</span> {
        <span class="hljs-type">Text</span>(<span class="hljs-string">"Read Second"</span>)
            .accessibilitySortPriority(<span class="hljs-number">1</span>)
        <span class="hljs-type">Text</span>(<span class="hljs-string">"Read First"</span>)
            .accessibilitySortPriority(<span class="hljs-number">2</span>)
    } 

</code></pre>
<p>Voice over will also read any element when it is tapped on. Once Voice Over is enabled on iOS, a single tap will read the UI element and a double tap will interact with it (for example, tapping a button). There are plenty of other gestures that are part of Voice Over such as swiping left and right to read previous/next elements on the screen.</p>
<p>We can group elements together to be read as one making it easier to understand, for example, in a workout application where a user accumulates “active days”, having two separate labels displaying “25” &#x26; “active days” would sound much better on Voice Over grouped together, as it will then be read as a singular “25 active days”.</p>
<pre><code class="hljs language-swift">    <span class="hljs-type">VStack</span> {
        <span class="hljs-type">Text</span>(“<span class="hljs-number">25</span>”)
        <span class="hljs-type">Text</span>(“<span class="hljs-type">Active</span> <span class="hljs-type">Days</span>”)
    } 
    .accessibilityElement(children: .combine)
</code></pre>
<p>![Grouped Elements image](/assets/img/articles/2022-09-01-Accessibility-support-with-SwiftUI/groupedElements.webp</p>
<p>You can also group more complex elements together giving them an overall description to give a better understanding to the user what the UI is displaying. You can also disable elements from Voice Over, for example, an image that is purely decorative and adds nothing to the functionality of the screen, you may opt for Voice Over to ignore.</p>
<pre><code class="hljs language-swift">    <span class="hljs-type">Image</span>.home
        .accessibilityHidden(<span class="hljs-literal">true</span>)
</code></pre>
<p>For any elements that do not have text content, you can provide accessibility labels, these will be read by Voice Over to describe the element. We already covered how to add accessibilityLabel’s above as these are also used for Voice Control commands. For example, a “close” button that has an X icon, can have an accessibility label as “close” and Voice over will read it as “close button” and the user can use Voice Control to say “Tap Close”.</p>
<p>iOS has many gestures to learn while using Voice Over, it also has a Voice Over rotor feature that allows for greater control of what is read, the speed it is read etc. You can read more about Voice Over gestures from Apple <a href="https://support.apple.com/en-gb/guide/iphone/iph3e2e2281/ios">here</a>.</p>
<h2>Zoom</h2>
<p>Zoom comes in the box with iOS and no development work is required for specific apps. Controlled in the iOS Settings, zoom functionality allows users to use different finger gestures to zoom and pan around the screen making UI larger.</p>
<h2>Accessibility Text Size Support</h2>
<p>To support larger accessibility text sizes, we have to make the UI dynamic enough to support large font sizes in any UI. This means making any view scrollable if required and having correct spacing between elements. It also means any text cannot be restricted to one line as most text will require multiple lines on larger text size options.</p>
<p>Using SwiftUI makes this much easier due to how VStacks and HStacks automatically grow depending on the content provided. iOS will scale your text sizes up and down for you but from experience this will not always work straight out of the box. Designs will never be a pixel-perfect match when developing this way as we let iOS determine much more of specific spacing/layout, but this is good, it hands over more control to iOS with less dependency on developers coding specific values and will ultimately result in a better looking &#x26; performing application. It is up to designers to always bear this in mind and for us developers to point out when designs need to be more dynamic.</p>
<p>The main challenge we faced with adding this support was having views be scrollable only when required. It would be easy enough to put all your app’s views into scrollviews but for larger devices or cases where the user’s preferences have veered towards a smaller text size, we still want SwiftUI to work its magic by spacing elements across the screen and giving us the easy, great looking, SwiftUI element layout system we’ve come to rely on.  </p>
<p>We ended up building an AccessibilityScrollView content wrapper which either wraps your content within a scrollview or not. It is determined by the users selected ContentSizeCategory (the text size selected in their device settings). This defaults to using a scrollview whenever accessibility sizes are used but a category can be passed in as a parameter for cases where your UI has less or more content. It often can take some testing &#x26; tweaking with different devices and size categories for more complex UIs but we find this solution works really well.</p>
<p><img src="/assets/img/articles/2022-09-01-Accessibility-support-with-SwiftUI/dynamicText.webp" alt="dynamic text image"></p>
<p>eg.</p>
<pre><code class="hljs language-swift">    <span class="hljs-type">AccessibilityScrollView</span>(minimumSizeRequired: .accessibilityExtraLarge) {
        <span class="hljs-type">VStack</span> {
            <span class="hljs-comment">//content goes here</span>
        }
    }
</code></pre>
<pre><code class="hljs language-swift"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">AccessibilityScrollView</span>&#x3C;<span class="hljs-title class_">Content</span>>: <span class="hljs-title class_">View</span> <span class="hljs-title class_">where</span> <span class="hljs-title class_">Content</span>: <span class="hljs-title class_">View</span> {
    <span class="hljs-keyword">init</span>(<span class="hljs-params">minimumSizeRequired</span>: <span class="hljs-type">ContentSizeCategory</span>? <span class="hljs-operator">=</span> <span class="hljs-literal">nil</span>,
         <span class="hljs-params">showsIndicators</span>: <span class="hljs-type">Bool</span> <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>,
         <span class="hljs-meta">@ViewBuilder</span> <span class="hljs-params">content</span>: <span class="hljs-keyword">@escaping</span> () -> <span class="hljs-type">Content</span>) {
        <span class="hljs-keyword">self</span>.content <span class="hljs-operator">=</span> content
        <span class="hljs-keyword">self</span>.minimumSizeRequired <span class="hljs-operator">=</span> minimumSizeRequired
        <span class="hljs-keyword">self</span>.showsIndicators <span class="hljs-operator">=</span> showsIndicators
    }
    
    <span class="hljs-comment">//if size is this or larger, scroll view will be used</span>
    <span class="hljs-keyword">var</span> minimumSizeRequired: <span class="hljs-type">ContentSizeCategory</span>?
    <span class="hljs-keyword">var</span> showsIndicators: <span class="hljs-type">Bool</span> <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>
    
    <span class="hljs-keyword">var</span> content: () -> <span class="hljs-type">Content</span>
    <span class="hljs-meta">@Environment</span>(\.sizeCategory) <span class="hljs-keyword">var</span> sizeCategory
    
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> scrollViewRequired: <span class="hljs-type">Bool</span> {
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> minimumSize <span class="hljs-operator">=</span> minimumSizeRequired {
            <span class="hljs-keyword">return</span> sizeCategory <span class="hljs-operator">>=</span> minimumSize
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> sizeCategory.isAccessibilityCategory
        }
    }
    
    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
        <span class="hljs-keyword">if</span> scrollViewRequired {
            <span class="hljs-type">ScrollView</span>(showsIndicators: <span class="hljs-literal">false</span>) {
                content()
            }
            .clipped()
        } <span class="hljs-keyword">else</span> {
            content()
        }
    }
}
</code></pre>
<h3>Conclusion</h3>
<p>Hopefully you've found some helpful tips here, even just adding a little bit of accessibility support to your app is already a great start. It can be difficult to get perfect but there are some quick ways you can support these great features iOS provide which could really help out &#x26; offer your app to more people.</p>
<h4></h4>
<h2>Related Articles:</h2>
<ul>
<li><a href="https://support.apple.com/en-gb/guide/iphone/iph3e2e2281/ios">Apple: Voice Over</a></li>
<li><a href="https://support.apple.com/en-gb/guide/iphone/iph2c21a3c88/ios">Apple: Voice Control</a></li>
<li><a href="https://support.apple.com/en-gb/guide/iphone/iph3e2e367e/ios">Apple: Zoom</a></li>
</ul>
<p><em>Article Photo by <a href="https://miro.medium.com/max/1400/1*0D5sXj2MrV6XfNJ7pegzWw.jpeg">Getty Images</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Accessing AWS RDS databases remotely with no exposed ports]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/08/18/Accessing-RDS-remotely-with-no-exposed-ports</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/08/18/Accessing-RDS-remotely-with-no-exposed-ports</guid>
            <pubDate>Thu, 18 Aug 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Ok, I admit it. The title might sounds weird because, how it is supposed to connect to an SQL service without a port, right? Well the catch is that there IS a port, but it is not an inbound port, therefore nothing is exposed. And it is fully managed by AWS. Interesting, right? Keep reading to know more.</p>
<h2>What scenario do we have?</h2>
<p><img src="/assets/img/articles/2022-08-18-Accessing-RDS-remotely-with-no-exposed-ports/fe-be-db.webp" alt=""></p>
<p>Following best practices, database must always be inside the private subnet, i.e. no access to/from the internet by default, and the only allowed inbound access is from the deployed application in the same VPC. However, you can think of many scenarios/use cases, where developer needs to access the databases from outside the VPC, for example: for development/debug purposes, the access from the local developer machine is needed... etc</p>
<p>If we add some fences, it looks more like this.
<img src="/assets/img/articles/2022-08-18-Accessing-RDS-remotely-with-no-exposed-ports/be-db-into-priv.webp" alt=""></p>
<p>It means the database is inside a private subnet that can be accessed only by the backend. The way the backend is connected with the frontend would vary a lot, so to simplify it, lets just put an arrow from users.</p>
<p>The daily basis tasks requires to get connected to the database to tweak some data, so how do we do that?</p>
<p>The most common scenario is something like this:</p>
<ul>
<li>A bastion host instance is added into the same subnet like the backend instances but with a very small instance size. It can even be turned on and off to save some money.</li>
<li>This bastion has an Ubuntu or an Amazon Linux, and the 22 port open to an SSH server.</li>
<li>The SSH server requires public keys of the users that will connect to it.</li>
<li>The user uses some database client software that can manage tunneling, or creates the tunnel manually right before the SQL connection and engage the task.</li>
<li>Besides the host, the user and the database password; she needs to have the private keys that matches the public keys of the bastion already configured.</li>
<li>We know every key must be rotated periodically and the comply of this security measure depends on humans; therefore, it is very common that you find very old keys which are still in use. I am sure you've seen such keys before.
<img src="/assets/img/articles/2022-08-18-Accessing-RDS-remotely-with-no-exposed-ports/bastion-added.webp" alt=""></li>
</ul>
<p>What happens when that user finishes her services with the company? Those keys need to be removed from every bastion host and any other key she had contact with its correspondent private key must be rotated. It could be lot of work. And if it fails, the person will still have access to the database instance. It is a huge risk.</p>
<h2>A better approach</h2>
<p>A respected company must have SSO access to authorize users into AWS accounts with very specific roles. And every user must have MFA. We can use that.</p>
<p>It is very important to <strong>do not</strong> use IAM users. To bring temporary access key id and its correspondent secret access key from the SSO service we use <a href="https://github.com/Versent/saml2aws">SAML2AWS</a>. And needless to say that IAM creation permissions should not be granted to anyone.</p>
<p>Also, we will use the <strong>SSM console access</strong> feature provided by the <a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html">AWS System Manager Session Manager tool</a>. This tool has CLI connection capabilities similar to those already provided by SSH, but without the downsides of requiring inbounds ports open nor maintenance of key pairs. Can you see where are we going with it? It just requires to have the SSM Agent installed into the bastion host and all inbound ports closed by the security group.</p>
<p>The most powerful components of the tool are the <a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-ssm-docs.html">System Management Documents</a> which describe actions that can be performed inside the managed system. In this case, we will use <code>AWS-StartPortForwardingSessionToRemoteHost</code> which reflects exactly the action we need.</p>
<p>Let's see it in action!
With a command like the following, we create a tunnel from your computer passing through the bastion host, directly to the RDS instance into the correct port.</p>
<pre><code class="hljs language-Bash">aws ssm start-session \
  --region eu-west-2 \
  --target i-fafafafafafafa \
  --document-name AWS-StartPortForwardingSessionToRemoteHost \
  --parameters host=<span class="hljs-string">"<span class="hljs-variable">${RDS_HOST}</span>"</span>,portNumber=<span class="hljs-string">"3306"</span>,localPortNumber=<span class="hljs-string">"3306"</span>
</code></pre>
<p>It is something like this:
<img src="/assets/img/articles/2022-08-18-Accessing-RDS-remotely-with-no-exposed-ports/tunel.webp" alt=""></p>
<p>This is an example that assumes i-fafafafafafafa is the bastion host's instance ID located in the London region and the database port is 3306 because we are using MySQL engines. But you will adjust it accordingly depending on your resources.</p>
<p>Extra tip to get the INSTANCE's Id if you have a bastion host with tags; and the RDS_HOST from a Bash command line interface. Useful for scripting and automation:</p>
<pre><code class="hljs language-Bash">INSTANCE=$(aws ec2 describe-instances \
  --query <span class="hljs-string">"Reservations[].Instances[].InstanceId"</span> \
  --filters <span class="hljs-string">"Name=tag-value,Values=bastion"</span> \
  --output text \
)
RDS_HOST=$(aws rds describe-db-cluster-endpoints \
  --query=<span class="hljs-string">"DBClusterEndpoints[?EndpointType=='WRITER'].Endpoint"</span> \
   --output=text \
)
</code></pre>
<p>Now you can:</p>
<pre><code class="hljs language-Bash">mysql -u your_rds_user -p -h 127.0.0.1 --ssl-mode=disabled
</code></pre>
<p>If the administrator needs to revoke the user's access for any reason, they can do so with the attached roles in SSO. No need for RSA key files, IP addresses or ports, access_key/secret_id rotation, ... nothing.</p>
<h2>Conclusion</h2>
<p>By using the <strong>AWS SSM session manager</strong>, we create a temporary <strong>tunnel</strong> directly to the database instance while keeping <strong>all inbound ports closed</strong> and centralizing user access <strong>via SSO</strong>. This creates a secure environment without personal credentials and therefore does not need to be maintained in the face of an employee termination or similar event.</p>
<p><em>Article Photo by <a href="https://blenderartists.org/u/danchristie25">Dan Christie</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Creating custom layouts with Compose -part 2-]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/08/04/Compose-custom-layouts-part2</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/08/04/Compose-custom-layouts-part2</guid>
            <pubDate>Thu, 04 Aug 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>In the <a href="https://engineering.monstar-lab.com/en/post/2022/06/30/Custom-layouts-with-jetpack-compose/">previous article</a>, we talked about creating custom layouts with Jetpack compose. This article will dive deeper and cover creating a custom modifier with a custom scope.</p>
<p>We created a star layout in the previous article and it looked like this:</p>
<p><img src="/assets/img/articles/2022-08-10-Compose-custom-layouts-part2/preview.webp" alt="Star Layout" title="Preview"></p>
<p>You can access the source code from <a href="https://github.com/KamelMohamad/star_layout_article">here</a>.</p>
<p>Using the previous implementation, we can place child views on star tips. Now we will extend this functionality to be able to put views inside the star, and we will also be able to slide them along the star arms.</p>
<p>Each child should tell the star layout if it will be inside the star or on a star arm. It must also tell it about its relative position along the star arm. Compose has a <strong><code>ParentDataModifier</code></strong> interface that we can implement to allow each child to provide data to the parent layout, which is the star layout in this case.</p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">private</span> <span class="hljs-keyword">data</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StarItemData</span></span>(
    <span class="hljs-keyword">val</span> slide: <span class="hljs-built_in">Float</span>,
    <span class="hljs-keyword">val</span> onArm: <span class="hljs-built_in">Boolean</span> = <span class="hljs-literal">true</span>,
    <span class="hljs-keyword">val</span> customAngle: <span class="hljs-built_in">Double</span> = <span class="hljs-number">0.0</span>
) :
    ParentDataModifier {

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> Density.<span class="hljs-title">modifyParentData</span><span class="hljs-params">(parentData: <span class="hljs-type">Any</span>?)</span></span> = <span class="hljs-keyword">this</span><span class="hljs-symbol">@StarItemData</span>
}

</code></pre>
<p>We created a data class called <em><code>StarItemData</code></em> that implements the <em><code>ParentDataModifier</code></em> interface. This class has two properties:</p>
<ul>
<li>
<p><strong>slide</strong>: defines the position of the child view along the star arm, use <strong><code>1f</code></strong> to place the view on the star arm tip and <strong><code>0f</code></strong> at the star's center, you can also use values > 1 to place the view outside the star tip, and values between 0 and 1 to place it along the star tip.</p>
</li>
<li>
<p><strong>onArm</strong>: used to tell whether there will be a star arm for this view or not. We don’t need to draw a star arm for a view if we want to place it inside the star.</p>
</li>
<li>
<p><strong>customAngle</strong>: in degrees, this value will be used with <strong>slide</strong> value to define the position of the views that are not on a star arm.</p>
</li>
</ul>
<p><code>Density.modifyParentData</code> function provides parent data for the parent layout. We simply return the instance of <code>StarItemData</code>.
The image below shows how this works :
<img src="/assets/img/articles/2022-08-10-Compose-custom-layouts-part2/customItem.webp" alt="custom item" title="custom item"></p>
<p>We want to make the <code>StarItemData</code> available only for views inside the star layout. To achieve that, we will create a custom scope.We can do this in  Compose by creating an Interface that contains an extension function on <code>Modifier</code>. Let’s name our interface <code>StarLayoutScope</code>.</p>
<pre><code class="hljs language-kotlin"><span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">StarLayoutScope</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">fun</span> Modifier.<span class="hljs-title">adjustPlace</span><span class="hljs-params">(slide: <span class="hljs-type">Float</span>, onArm: <span class="hljs-type">Boolean</span> = <span class="hljs-literal">true</span>, customAngle: <span class="hljs-type">Double</span> = <span class="hljs-number">0.0</span>)</span></span> =
        <span class="hljs-keyword">this</span>.then(
            StarItemData(slide, onArm, customAngle)
        )

    <span class="hljs-keyword">companion</span> <span class="hljs-keyword">object</span> : StarLayoutScope
}
</code></pre>
<p>We will use the adjustPlace function to set the star data for the view. We also create a companion object which is an implementation of the interface, becaue our interface contain only an extension function, there is nothing to override.</p>
<p>Now let's update the old code to implement the new features, let's look at this section from the previous code.</p>
<pre><code class="hljs language-kotlin"><span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">StarLayout</span><span class="hljs-params">(
   radius: <span class="hljs-type">Dp</span>,
   modifier: <span class="hljs-type">Modifier</span> = Modifier,
   drawStyle: <span class="hljs-type">DrawStyle</span> = Stroke(<span class="hljs-number">12</span>f)</span></span>,
   color: Color = Color.Yellow,
   content: <span class="hljs-meta">@Composable</span> () -> <span class="hljs-built_in">Unit</span>
) {
</code></pre>
<p>we will update this line</p>
<pre><code class="hljs language-kotlin"> content: <span class="hljs-meta">@Composable</span> () -> <span class="hljs-built_in">Unit</span>
</code></pre>
<p>to become like this</p>
<pre><code class="hljs language-kotlin"> content: <span class="hljs-meta">@Composable</span> StarLayoutScope.() -> <span class="hljs-built_in">Unit</span>
</code></pre>
<p>We made the type of the  <em>content</em>  an extention function on <em>StarLayoutScope</em> so we can call <em>adjustPlace</em> function on the <em>modifier</em>, we are basically making the <em>adjustPlace</em> accessiable inside <em>content</em>. Let's also modify</p>
<pre><code class="hljs language-kotlin">   Layout(
        content = content,
        modifier = modifier
    ) { measurables, constraints ->
</code></pre>
<p>to</p>
<pre><code class="hljs language-kotlin">   Layout(
        content = { StarLayoutScope.content() },
        modifier = modifier
    ) { measurables, constraints ->
</code></pre>
<p>Here we call the <em>content()</em> on a concrete implementation of the <em>StarLayoutScope</em> interface, which is the companion object in this case. This is done inside a lambda function.</p>
<p>The position of each child view inside the star layout will depend on its StarItemData, we can get this data for each child view from its corresponding measurable. Let’s store the StarLayoutData for the measurables in a list called <em>itemDataList</em>.</p>
<pre><code class="hljs language-kotlin">{ measurables, constraints ->
        <span class="hljs-keyword">val</span> placeables = measurables.map { it.measure(constraints) }
        <span class="hljs-keyword">val</span> itemDataList = measurables.map { it.parentData <span class="hljs-keyword">as</span>? StarItemData }
        count = itemDataList.filter { it == <span class="hljs-literal">null</span> || it.onArm }.size
</code></pre>
<p>Views with no StarItemData should follow the default behaviour, which is being positioned on a start arm, also views with value <em>onArm</em> set to true will be drawn on a star arm. That is why we set the value of the <em>count</em> variable to the number of measurables that has <em>onArm</em> set to true instead of the total count of measurables.</p>
<h3>Placing child views</h3>
<p>We previously used this block of code to place child views inside the star layout</p>
<pre><code class="hljs language-kotlin">placeables.forEach { placeable ->
    placeable.place(
        totalRadius - placeable.width / <span class="hljs-number">2</span> + (starRadiusPx * cos(angle)).toInt(),
        totalRadius - placeable.height / <span class="hljs-number">2</span> + (starRadiusPx * sin(angle)).toInt(),
    )
    angle += step
}
</code></pre>
<p>Now we will update to take into account the values provided by StarItemData.</p>
<pre><code class="hljs language-kotlin">placeables.forEachIndexed { index, placeable ->
    <span class="hljs-keyword">val</span> itemData = itemDataList.getOrNull(index)
    <span class="hljs-keyword">val</span> slideFactor = itemData?.slide ?: <span class="hljs-number">1f</span>
    <span class="hljs-keyword">val</span> customAngle = itemData?.customAngle ?: <span class="hljs-number">0.0</span>
    <span class="hljs-keyword">val</span> onEdge = itemData?.onArm ?: <span class="hljs-literal">true</span>
    println(<span class="hljs-string">"index is <span class="hljs-variable">$index</span>, slide data : <span class="hljs-variable">$itemData</span>"</span>)
    <span class="hljs-keyword">val</span> drawAngle = <span class="hljs-keyword">if</span> (onEdge) angle <span class="hljs-keyword">else</span> Math.toRadians(-customAngle)
    placeable.place(
        totalRadius - placeable.width / <span class="hljs-number">2</span> + (starRadiusPx * slideFactor * cos(drawAngle)).toInt(),
        totalRadius - placeable.height / <span class="hljs-number">2</span> + (starRadiusPx * slideFactor * sin(drawAngle)).toInt(),
    )
    <span class="hljs-keyword">if</span> (onEdge) angle += step
}
</code></pre>
<p>What we are doing in the new code is using the index of each placeable to get the StarItemData for each child view from the <em>itemDataList</em> we created earlier. If the slide value is set, we store it in <em>slideFactor</em> variable. Otherwise, we set it to 1f.</p>
<p>We also set the value of <em>onArm</em> variable to be equal to the value set on the StarItemData. If no value is set, we set it to true.</p>
<p>We will also need the angle value if the <em>onArm</em> value is set to false. The angle value will help us define the position of the view.
<em>drawAngle</em> will hold the angle value for each view. Suppose the <em>onArm</em> value for the view is true. In that case, we assign the value of the gradually increased <em>angle</em> to  <em>drawAngle</em>. If its value is false, we assign the <em>customAngle</em> value provided to the <em>drawAngle</em>. The negative sign is to make the angle go counterclockwise.</p>
<p>Then we calculate the x and y position for each view. We also increase the angle value by the amount of <em>step</em>, which depends on the count of views that will be drawn on star arms. You can refer to the previous article for more details about the positioning process.</p>
<p>We modify the logic for placing each view, taking into account the slide factor. We also increase the angle only when the value of <em>onArm</em> is true because the count of items distributed shouldn't be affected by views that are not on the star arms.</p>
<p>Now lets test our new star layout, in the main activity modify the code to look like this :</p>
<pre><code class="hljs language-kotlin">setContent {
    Star_layout_articleTheme {
        <span class="hljs-comment">// A surface container using the 'background' color from the theme</span>

         Column(
                    horizontalAlignment = Alignment.CenterHorizontally
        ) {

            StarLayout(radius = <span class="hljs-number">160.</span>dp) {
                items.forEach {

                    StarItem(
                        selected = it,
                        radius = <span class="hljs-number">42.</span>dp,
                        modifier = Modifier.adjustPlace(<span class="hljs-number">0.9f</span>)
                    ) {}
                }
                StarItem(
                    selected = <span class="hljs-literal">false</span>,
                    radius = <span class="hljs-number">42.</span>dp,
                    modifier = Modifier.adjustPlace(<span class="hljs-number">0.3f</span>, <span class="hljs-literal">false</span>, <span class="hljs-number">120.0</span>)
                ) {}
                StarItem(
                    selected = <span class="hljs-literal">false</span>,
                    radius = <span class="hljs-number">42.</span>dp,
                    modifier = Modifier.adjustPlace(<span class="hljs-number">0f</span>, <span class="hljs-literal">false</span>)
                ) {

                }
            }
        }
    }
}
</code></pre>
<p>the result should look something like this</p>
<p><img src="/assets/img/articles/2022-08-10-Compose-custom-layouts-part2/Screenshot.webp" alt="screenshot" title="screenshot"></p>
<p>As you see, we still have five items on star tips, but a bit slid towards the star's center. You can play with the slide value and see the results. We also have two items inside the star. The first one is at the center of the star because we set the slide to be 0f and the <em>onArm</em> to false.</p>
<pre><code class="hljs language-kotlin">                StarItem(
                    selected = <span class="hljs-literal">false</span>,
                    radius = <span class="hljs-number">42.</span>dp,
                    modifier = Modifier.adjustPlace(<span class="hljs-number">0f</span>, <span class="hljs-literal">false</span>)
                ) {

                }
</code></pre>
<p>The second one is a bit far from the center and at an angle of 120 degrees; this is because we provided a 0.3f slide and a 120 custom degree.</p>
<pre><code class="hljs language-kotlin">               StarItem(
                    selected = <span class="hljs-literal">false</span>,
                    radius = <span class="hljs-number">42.</span>dp,
                    modifier = Modifier.adjustPlace(<span class="hljs-number">0.3f</span>, <span class="hljs-literal">false</span>, <span class="hljs-number">120.0</span>)
                ) {}
</code></pre>
<p>A cool idea would be having  pictures of people or objects distributed on the star arms, and when selecting one of them a small description appears inside the star, we can also animate the slide value to get cool enter/exist animation.</p>
<p>Source code can be found <a href="https://github.com/KamelMohamad/star_layout_article/tree/custom_modifier">here</a> on the "custom_modifier" branch.</p>
<p>References :</p>
<p><a href="https://fvilarino.medium.com/creating-a-custom-modifier-attribute-on-jetpack-compose-f100c6bcb987">https://fvilarino.medium.com/creating-a-custom-modifier-attribute-on-jetpack-compose-f100c6bcb987</a></p>
<p><a href="https://android-developers.googleblog.com/2019/05/whats-new-with-android-jetpack.html">Android jetpack logo</a>
<a href="https://developer.android.com/distribute/marketing-tools/brand-guidelines">Google brand guidelines</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Code Coverage with SonarQube and Bitrise for Swift]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/07/22/Code-Coverage-with-SonarQube-and-Bitrise-for-Swift</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/07/22/Code-Coverage-with-SonarQube-and-Bitrise-for-Swift</guid>
            <pubDate>Fri, 22 Jul 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>At <a href="https://monstar-lab.com/global">Monstarlab</a>, we are using <a href="https://sonarqube.com/">SonarQube</a> to gather metrics about the quality of our code. One of the metrics we were interested in is code coverage. However, just running <code>sonar-scanner</code> on the project will not upload the coverage data to our instance of SonarQube. Here's how we got code coverage reports on SonarQube with Bitrise, our favourite CI/CD service.</p>
<h2>Getting started</h2>
<p>Our apps are already set up on Bitrise, and we had already set up SonarQube to run on every pull request. What we want now is to also gather code coverage data and send them to SonarQube.</p>
<p>First of all, we need to gather the code coverage data from Xcode. We do that by editing the Test scheme action in Xcode:</p>
<ul>
<li>Open your project in Xcode</li>
<li>Go to Product > Scheme > Edit Scheme (or CMD + &#x3C;, or option + click on the target, next to the simulator in the top bar)</li>
<li>Select the Test scheme action</li>
<li>In the Options tab, make sure the checkmark next to Code Coverage is selected
<img src="/assets/img/articles/2022-07-22-Code-Coverage-with-SonarQube-and-Bitrise-for-Swift/scheme.webp" alt="">
Make sure this change is committed to git when you start testing your updated Bitrise workflow.</li>
</ul>
<h2>Running the tests and getting the coverage data</h2>
<p>Let's have Bitrise run the tests so it can then gather the coverage data. We are editing our pull-request workflow on Bitrise and adding the step "Xcode Test for iOS". We won't change any of the default parameters of this step. This step will run the tests and also output a variable, <code>$BITRISE_XCRESULT_PATH</code>, which contains the path to the <code>xcresult</code> file.
<img src="/assets/img/articles/2022-07-22-Code-Coverage-with-SonarQube-and-Bitrise-for-Swift/xcode-test-step.webp" alt=""></p>
<p>SonarQube expects the coverage data in a specific XML format. So after Xcode runs the tests and generates the <code>xcresult</code>, we need to get from there the coverage data and transform it into the format expected by SonarQube. We will be using the <a href="https://github.com/SonarSource/sonar-scanning-examples/blob/master/swift-coverage/swift-coverage-example/xccov-to-sonarqube-generic.sh">script</a> from the <a href="https://github.com/SonarSource/sonar-scanning-examples/tree/master/swift-coverage">SonarQube Code Coverage examples</a> to do that. So we need to tell Bitrise to fetch the script and run it.</p>
<p>In our pull request workflow in Bitrise, we're adding the "File Download" step. We tell it to download the script from the URL <code>https://raw.githubusercontent.com/SonarSource/sonar-scanning-examples/master/swift-coverage/swift-coverage-example/xccov-to-sonarqube-generic.sh</code> to the destination path <code>xccov-to-sonarqube-generic.sh</code> and we set the file permissions to 755 (to make sure it's executable).
<img src="/assets/img/articles/2022-07-22-Code-Coverage-with-SonarQube-and-Bitrise-for-Swift/file-downloader-step.webp" alt="">
<img src="/assets/img/articles/2022-07-22-Code-Coverage-with-SonarQube-and-Bitrise-for-Swift/file-downloader-step-options.webp" alt=""></p>
<p>The script has <code>jq</code> as a dependency, which is a tool for processing JSON. We can install this via Homebrew, so we add another step for this.
<img src="/assets/img/articles/2022-07-22-Code-Coverage-with-SonarQube-and-Bitrise-for-Swift/brew-install-step.webp" alt="">
We specify <code>jq</code> as the formula name and leave the rest of the parameters unchanged.</p>
<p>Now we have everything we need to run the script we downloaded and extract the coverage data from the <code>xcresult</code> into the format that SonarQube expects. So we add the Run Script step
<img src="/assets/img/articles/2022-07-22-Code-Coverage-with-SonarQube-and-Bitrise-for-Swift/run-script-step.webp" alt="">
And we add the following script content:</p>
<pre><code class="hljs language-bash"><span class="hljs-meta">#!/usr/bin/env bash</span>
<span class="hljs-comment"># fail if any commands fails</span>
<span class="hljs-built_in">set</span> -e
<span class="hljs-comment"># make pipelines' return status equal the last command to exit with a non-zero status, or zero if all commands exit successfully</span>
<span class="hljs-built_in">set</span> -o pipefail
<span class="hljs-comment"># debug log</span>
<span class="hljs-built_in">set</span> -x

<span class="hljs-built_in">echo</span> <span class="hljs-string">"Converting code coverage log to Sonar format"</span>
./xccov-to-sonarqube-generic.sh <span class="hljs-variable">$BITRISE_XCRESULT_PATH</span> > cov.xml
</code></pre>
<p>We're running the script on the <code>xcresult</code> file and exporting the coverage data to a file called <code>cov.xml</code>.</p>
<h2>Uploading the results to SonarQube</h2>
<p>With the coverage data in the <code>cov.xml</code> file, now we can run <code>sonar-scanner</code>. We already had this setup on Bitrise, using the SonarQube Scanner step made by <a href="https://github.com/DroidsOnRoids/bitrise-step-sonarqube-scanner">DroidsOnRoids</a>, but we didn't have the coverage data. We do now, so we just have to add this line <code>sonar.coverageReportPaths=cov.xml</code> in the step's options under "Scanner parameters in Java properties format".</p>
<p><img src="/assets/img/articles/2022-07-22-Code-Coverage-with-SonarQube-and-Bitrise-for-Swift/sonarqube-scanner-step.webp" alt=""></p>
<p>Overall, this is what our pull request workflow looks like in Bitrise:</p>
<p><img src="/assets/img/articles/2022-07-22-Code-Coverage-with-SonarQube-and-Bitrise-for-Swift/workflow.webp" alt=""></p>
<p>There can be variations based on your specific project (dependency manager, etc), so use this as inspiration and not as an absolute source of truth.</p>
<p>We can now open a new pull request to test this workflow in Bitrise. We're not covering setting up the workflow trigger in this article, you can check out <a href="https://devcenter.bitrise.io/en/builds/starting-builds/triggering-builds-automatically.html#triggering-builds-with-pull-requests">Bitrise's official guide for that</a>.</p>
<p>After the Bitrise workflow has successfully been run, we can now see the results in the SonarQube dashboard, showing some data for code coverage.</p>
<p><img src="/assets/img/articles/2022-07-22-Code-Coverage-with-SonarQube-and-Bitrise-for-Swift/sonar-report.webp" alt=""></p>
<h2>Wrap-up</h2>
<p>Getting the code coverage data into SonarQube is not a complicated task, but it requires a bit more work than we initially expected. We found this way of doing it, which relies on the script from the SonarSource swift examples. This is a solution that works for now, but it is possible that in the future the script will break and not work with new versions of Xcode. We will be keeping an eye on this and updating our workflow if necessary.</p>
<p><em>Article Photo by <a href="https://unsplash.com/@mcverry">Mikail McVerry</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A Guideline to GitHub Actions with CI Pipeline]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/07/15/A-Guideline-to-GitHub-Actions-with-CI-Pipeline</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/07/15/A-Guideline-to-GitHub-Actions-with-CI-Pipeline</guid>
            <pubDate>Fri, 15 Jul 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>CI Pipelines help improve efficiency by automating complex workflows. With GitHub Actions, it's easier than ever to bring CI/CD directly into your workflow from your repository. Put together with the CI pipeline, a series of automated workflows that help DevOps teams cut down on manual tasks.</p>
<p>In this article, I will describe the integration between GitHub actions with the CI pipeline where I will cover the following topics:</p>
<ul>
<li>Basic understanding of <a href="https://www.edureka.co/blog/ci-cd-pipeline/">CI/CD pipeline</a>, how and why we should use GitHub Actions instead of other tools (Here we will only focus on the CI pipeline).</li>
<li>Provided step-by-step guidelines so that anyone can prepare a workflow just by following this article.</li>
<li>Step-by-step guideline on how to trigger the QA Workflow from the Developer repository.</li>
</ul>
<h2>What is CI/CD</h2>
<p><a href="https://www.atlassian.com/continuous-delivery/continuous-integration">Continuous integration</a> (CI) in CI/CD is an automated process that lets multiple developers contribute software elements to the same project without any integration conflicts. CI engages automated testing whenever a software change is integrated into the repository.</p>
<p><a href="https://www.atlassian.com/continuous-delivery/principles/continuous-integration-vs-delivery-vs-deployment">CD</a> is known for continuous delivery or continuous deployment. Both involve continuously integrating and deploying the code to QA or production environments. Some teams practice continuous deployment to deploy daily or even hourly to production, though continuous deployment is not optimal for every business application.
<img src="/assets/img/articles/2022-07-15-A-Guideline-to-GitHub-Actions-with-CI-Pipeline/ci-cd.webp" alt="">
<em>Image Source <a href="https://faun.pub/most-popular-ci-cd-pipelines-and-tools-ccfdce429867">FAUN Publication</a></em></p>
<h2>What is GitHub Actions</h2>
<p><a href="https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions">GitHub Actions</a> is an event-driven automation platform that allows you to run a group of commands after a specified event. It uses <a href="https://www.javatpoint.com/yaml">YAML</a> syntax to specify the workflow. This action makes it easier to write a script (.yml) in your workflow quickly. Workflows are stored as a separate YAML file in your code repository. You can create a workflow in your repository that automatically triggers a series of commands whenever code is pushed. A frequent use case for Actions is automated continuous integration and deployment.</p>
<h2>Why GitHub Actions</h2>
<p>GitHub Actions is free to use on both public repositories and self-hosted runners. It can be used for free on private repositories with a limit of 2,000 minutes a month. It is quick and easy to set up directly from the GitHub website. It permits building CI/CD pipelines for testing, releasing, and deploying software without third-party platforms. It allows easier integration with code on GitHub and the option to reduce the number of services you need to subscribe, to have a complete CI/CD workflow. It also allows for much easier viewing of each build's pipeline status since you do not have to leave GitHub's website to see any logs or errors.</p>
<h2>Getting started with GitHub Actions</h2>
<ol>
<li>At first, you need to create a new repository
<img src="/assets/img/articles/2022-07-15-A-Guideline-to-GitHub-Actions-with-CI-Pipeline/Create-repository.webp" alt=""></li>
<li>You need to push your code into the repository. For example, I am using the Selenium (Java with maven) project.
<img src="/assets/img/articles/2022-07-15-A-Guideline-to-GitHub-Actions-with-CI-Pipeline/java-with-maven.webp" alt=""></li>
</ol>
<h5><strong>Create and Configure a New Workflow</strong></h5>
<ol>
<li>In the Actions tab, you will see that GitHub recommends <a href="https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions">Workflow</a>, depending on the project. You can select your desired template based on your project repository (for my project, I am selecting Java with Maven). You can create multiple workflows in the same repository. Alternatively, you can set up your workflow by clicking on the "set up workflow yourself" option.
<img src="/assets/img/articles/2022-07-15-A-Guideline-to-GitHub-Actions-with-CI-Pipeline/create-workflow.webp" alt=""></li>
<li>It will automatically generate a .yml file for the java with maven project.
Workflows will be located inside the .github/workflows directory. For example- .github/workflows/maven.yml to your project.</li>
</ol>
<h5>A workflow file consists of the following contents-</h5>
<p><strong>Name of the Workflow:</strong> The workflow's name is denoted by the keyword <strong>'name'</strong>. If you wish to include multiple CI jobs in your project, you should name each one appropriately.</p>
<p><strong>Event:</strong> A workflow can be triggered manually or in response to specific events. You will use the <strong>'on'</strong> keyword to specify the events that trigger a workflow. There are many ways to trigger the workflow, such as:</p>
<ol>
<li>push</li>
<li>pull_request</li>
<li>release &#x26; many more</li>
</ol>
<p>You can mention the branch name below the specific event. If you create a workflow in your default branch(main) then you don't need to mention the branch name.</p>
<p><strong>Jobs:</strong> A workflow execution made up of one or more jobs. A job consists of multiple steps and runs in an instance of the virtual environment. Jobs can run independently or sequentially.</p>
<p><strong>Runner:</strong>  A runner is a server that executes your workflows when they're triggered. You can host your runner or use GitHub-hosted runners such as Ubuntu Linux, Microsoft Windows, and macOS. In my example, I am using the Ubuntu runner. You can choose any other operating system (Windows/Mac OS) per the machine's requirement. We need to run a single job at a time.
<img src="/assets/img/articles/2022-07-15-A-Guideline-to-GitHub-Actions-with-CI-Pipeline/yml-file.webp" alt="">
<strong>Steps:</strong> After you specify the runner, determine the steps with the commands. Steps are a group of tasks that a job can execute.</p>
<p><strong>Actions:</strong> An action is a command which is executed on a runner and is one of the core elements of GitHub Actions. You can create actions or you can use publicly shared Actions from the Marketplace.</p>
<h5><strong>Run The Workflow</strong></h5>
<ol>
<li>You will see the "Start commit" button on the right side of the page. By clicking on the commit button, you can execute the .yml file.
<img src="/assets/img/articles/2022-07-15-A-Guideline-to-GitHub-Actions-with-CI-Pipeline/commit.webp" alt=""></li>
<li>Once the commit is successful, you will notice that your new workflow is being added in the "Actions" tab.
<img src="/assets/img/articles/2022-07-15-A-Guideline-to-GitHub-Actions-with-CI-Pipeline/run_workflow_resize.webp" alt=""></li>
<li>The workflow build job starts building and running the instructions/commands in the .yml file and changes the build status from "Queued" to "In progress".
<img src="/assets/img/articles/2022-07-15-A-Guideline-to-GitHub-Actions-with-CI-Pipeline/queue-workflow.webp" alt=""></li>
<li>When you go to the "Actions" tab, you will notice that a job is set up and executed. You can check it to see the live console.
<img src="/assets/img/articles/2022-07-15-A-Guideline-to-GitHub-Actions-with-CI-Pipeline/console.webp" alt=""></li>
<li>After completing all the build runs, you will notice the status changed to pass/fail based on the outcome of the build run.</li>
<li>If the build passes, you will see a Green Tick Mark (✅) beside the built-to run.</li>
<li>If the build fails, you will see a Red Cross Mark (❌) beside the built and an email notification will be sent to the user.
<img src="/assets/img/articles/2022-07-15-A-Guideline-to-GitHub-Actions-with-CI-Pipeline/pass-fail.webp" alt=""></li>
</ol>
<h5><strong>Disable The Workflow</strong></h5>
<ol>
<li>Go to the Action tab and select your desired workflow from the workflows section (left-side).</li>
<li>Next you need to click on the meatball icon (<strong>...</strong>) and choose the disable workflow option.
<img src="/assets/img/articles/2022-07-15-A-Guideline-to-GitHub-Actions-with-CI-Pipeline/disable-workflow.webp" alt=""></li>
</ol>
<h4><strong>Scheduler (Cron job)</strong></h4>
<p>The scheduled event executes your workflow on a specific timeline using the <a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/crontab.html#tag_20_25_07">cron syntax</a>. The cron expression is made of 5 fields which are denoted as asterisk signs ( * * * * * ) that means "every minute of every day". You can define multiple schedules based on your project need. I have instructed the job to run every 15 minutes in the below example. Also, be aware that GitHub Actions only runs scheduled jobs on the default branch of your repository (main). As times are showing on UTC, so you may have to do some time zone conversion.</p>
<script src="https://gist.github.com/sabrina-monstar/b196031d38ec8bcd88f41c818e62cf30.js"></script>
<h4><strong>Multiple Jobs &#x26; Dependencies</strong></h4>
<p>To execute our tests in GitHub Actions in parallel and reduce execution times, you can run multiple jobs simultaneously. You can also set up jobs that depend on other jobs.</p>
<ol>
<li>If any job needs to run sequentially where the current job depends on the previous job to succeed, in that case, you have to add the <strong><code>needs</code></strong> attribute to create the dependency.</li>
<li>If your parent job fails, you can still run the child job by using the <strong><code>if:always()</code></strong> keyword. If the first job fails, it will run the next job inspite of having the dependency.</li>
</ol>
<script src="https://gist.github.com/sabrina-monstar/ad99604a3d60c114976c7afc31190b2f.js"></script>
<h4><strong>Trigger the Other Workflow</strong></h4>
<p>When Developers and QAs are working on the same project, it is essential that whenever a developer modifies or changes some code on his project repository and pushes those changes, it should automatically trigger the QA Workflow. You can trigger multiple workflows from the Dev repository.
To implement that, you need two repositories, the Developer Repository (Project) and QA Repository (Automation).</p>
<h5><strong>Developer Workflow</strong></h5>
<p>If you want to trigger the QA workflow, you have to follow these steps:</p>
<ul>
<li>You need to use a public repository called <a href="https://github.com/actions/checkout">checkout</a>, it is an official GitHub Actions used to check out a repository so a workflow can access it.</li>
<li>From the <a href="https://github.com/convictional/">GitHub conventional site</a>, you need to use a public repository called <a href="https://github.com/convictional/trigger-workflow-and-wait">trigger-workflow-and-wait</a>. You must provide some required information about the repository that you want to trigger.</li>
<li>You need to generate a <a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-tokens">github token</a> from the developer option and add that token to the developer repository as a <a href="https://docs.github.com/en/actions/security-guides/encrypted-secrets">secret key</a>.</li>
</ul>
<script src="https://gist.github.com/sabrina-monstar/adcb33c722cdb727b8996b6085e051b8.js"></script>
<h5><strong>QA Workflow</strong></h5>
<p>In QA workflow, you have to follow these steps:</p>
<ul>
<li>You need to add the developer as a collaborator so that he can access the QA repository.</li>
<li>Next, you need to add an attribute called <a href="https://github.com/marketplace/actions/workflow-dispatch">workflow_dispatch</a> to the event section so that it can be triggered.</li>
</ul>
<script src="https://gist.github.com/sabrina-monstar/f09ca8ea95d4f97c8587b5182245b9ca.js"></script>
<p>By following the above steps, you can trigger a QA Automation workflow. And whenever a developer changes or modifies anything and pushes those changes to their project repository, it will automatically trigger the QA Workflow as well. In this way, both the developer and QA can work together in the CI pipeline.</p>
<h2>Pro Tips</h2>
<ol>
<li>You should name your workflows accordingly if you want multiple workflows to build a CI pipeline to run tests with Selenium GitHub Actions. "Name" is not a required field; if skipped, GitHub shall create a default name for us from the defined path &#x26; name of the file.</li>
<li>If you have multiple workflows with the event <strong>(on:push)</strong>. Whenever you modify or push anything to that repository, it will trigger all the workflows and will be running on the Actions tab.</li>
<li>The formats you need to keep in mind while dealing with YAML files are that you cannot enter incorrect formats, and you need to follow the proper indentation, colons, and dashes across the file, which primarily consists of key-value pairs, lists, and maps.</li>
<li>For example, if a workflow is scheduled to run every 15 minutes. After executing the workflow, you will notice that it did not run within 15 minutes. A delay of 2 to 10 minutes may occur.</li>
<li>In QA workflow, you must add the "workflow_dispatch" event; otherwise, it will not be triggered.</li>
<li>While generating the PAT, we should generate the token using the custom options, or it may create some issues.</li>
<li>If you use a free version of the GitHub account and want to keep your cron job running for many days, it might use 100% of included services for GitHub Actions. You can make your repository public or switch to a paid version of your GitHub account to resolve these issues. You can check this link for more details- <a href="https://github.com/pricing#compare-features">pricing/compare-features</a></li>
<li>To get more information about GitHub Actions, you can check this link- <a href="https://docs.github.com/en/actions">GitHub Actions Documentation</a></li>
</ol>
<h2>Additional resources</h2>
<p>Check out the following tutorials and resources to learn more about GitHub Actions:</p>
<ol>
<li><a href="https://javascript.plainenglish.io/how-to-setup-advanced-ci-cd-pipeline-with-github-actions-no-travis-circle-ci-29b02b03c501">How to Set Up Advanced CI/CD Pipeline with GitHub Actions</a></li>
<li><a href="https://medium.com/@michaelekpang/creating-a-ci-cd-pipeline-using-github-actions-b65bb248edfe">Creating a CI/CD pipeline using Github Actions</a></li>
</ol>
<p><em>Article Photo by <a href="https://unsplash.com/photos/kRNZiGKtz48">NordWood Themes</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Navigation parameters, and dynamic startDestination with Jetpack Compose]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/07/13/Navigation-parameters-with-Jetpack-Compose</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/07/13/Navigation-parameters-with-Jetpack-Compose</guid>
            <pubDate>Wed, 13 Jul 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Our team was recently given the opportunity to work with Jetpack Compose on a brand new project in collaboration with a client who required two separate Android applications. It was successfully released and the client was very satisfied with the end product.
I am making this blog post to share some of the learnings gained along the way, specifically involving dealing with parameters in Jetpack Compose Navigation, which was quite tricky at first.</p>
<p>As of writing, the latest Jetpack Compose Navigation version is <strong><a href="https://mvnrepository.com/artifact/androidx.navigation/navigation-compose/2.5.0" title="2.5.0">2.5.0</a></strong>, which is what we used.</p>
<p>All code has been stripped of any custom business logic, down to the bare minimum, and only used for educational purposes.</p>
<h1>Our App's Design</h1>
<h2>Logic Flow</h2>
<p>On the Login Screen, after successful login, the database is checked for the presence of a flag indicating whether it is the user's first login or not. In the case of first login, the user is sent to the "Change Password Screen" to change their password. Otherwise, they are sent to the "Home Screen".</p>
<p><img src="/assets/img/articles/2022-07-13-Navigation-parameters-with-Jetpack-Compose/project_login_flow.webp" alt="Login Flow Design" title="Login Flow Design"></p>
<p>The Change Password screen can however also be accessed from the Home Screen at any time.</p>
<p><img src="/assets/img/articles/2022-07-13-Navigation-parameters-with-Jetpack-Compose/change_password_home.webp" alt="Home Screen to Change Password" title="Home Screen to Change Password"></p>
<p>In the Change Password screen, if <code>firstLogin</code> is <strong>false</strong>, then the user is asked to first enter their current password. If <strong>true</strong>, they are not required to enter their current password.</p>
<h2>Main Challenge : Dynamic <code>startDestination</code></h2>
<p>Google defines the <code>startDestination</code> as :</p>
<blockquote>
<p>the first screen the user sees when they launch your app from the launcher. This destination is also the last screen the user sees when they return to the launcher after pressing the Back button.</p>
</blockquote>
<p>And furthermore, they specify that :</p>
<blockquote>
<p>Every app you build has a <a href="https://developer.android.com/guide/navigation/navigation-principles#fixed_start_destination">fixed start destination</a>.</p>
</blockquote>
<p>However, the design of our app specifies that :</p>
<blockquote>
<p>If the user presses the Android Back button on the Change Password Screen.</p>
<ul>
<li>If firstLogin == true, exit app.</li>
<li>If firstLogin == false, go back as normal (to the Home Screen).</li>
</ul>
</blockquote>
<p>And thus we must find a way to change the <code>startDestination</code>.</p>
<h1>Navigation in Jetpack Compose</h1>
<h2>Basic Project Layout</h2>
<p>The basic structure of our Jetpack Compose project (without parameters) might thus look something like this :</p>
<pre><code class="hljs language-Kotlin"><span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">MainScreen</span><span class="hljs-params">(
    mainViewModel: <span class="hljs-type">MainViewModel</span> = viewModel()</span></span>
) {
    <span class="hljs-keyword">val</span> navController = rememberNavController()

    Scaffold {
        <span class="hljs-keyword">val</span> startDestination = mainViewModel.startDestination.collectAsState().value

        NavHost(
            navController = navController,
            startDestination = startDestination
        ) {
            composable(AppRoute.LOGIN.route) {
                LoginScreen(
                    navController = navController
                )
            }
            composable(AppRoute.HOME.route) {
                HomeScreen(
                    navController = navController
                )
            }
            composable(AppRoute.CHANGE_PASSWORD.route) {
                ChangePasswordScreen(
                    navController = navController
                )
            }
        }
    }
}

<span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">LoginScreen</span><span class="hljs-params">(navController: <span class="hljs-type">NavController</span>)</span></span> { ... }

<span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">HomeScreen</span><span class="hljs-params">(navController: <span class="hljs-type">NavController</span>)</span></span> { ... }

<span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">ChangePasswordScreen</span><span class="hljs-params">(navController: <span class="hljs-type">NavController</span>)</span></span> { ... }

</code></pre>
<p>In this context, the <code>MainViewModel</code> is as follows :</p>
<pre><code class="hljs language-Kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MainViewModel</span></span>(application: Application) : AndroidViewModel(application) {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> _startDestination = MutableStateFlow(AppRoute.LOGIN.route)
    <span class="hljs-keyword">val</span> startDestination: StateFlow&#x3C;String> <span class="hljs-keyword">get</span>() = _startDestination

    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">updateStartDestination</span><span class="hljs-params">(value: <span class="hljs-type">String</span>)</span></span> {
        _startDestination.value = value
    }
}
</code></pre>
<p>And the <code>AppRoute</code> enum is defined as follows :</p>
<pre><code class="hljs language-Kotlin"><span class="hljs-keyword">enum</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppRoute</span></span>(
    <span class="hljs-keyword">val</span> route: String
) {
    LOGIN(<span class="hljs-string">"login"</span>),
    HOME(<span class="hljs-string">"home"</span>),
    CHANGE_PASSWORD(<span class="hljs-string">"change_password"</span>)
}
</code></pre>
<p>In this setup, the default <code>startDestination</code> is set to <code>AppRoute.LOGIN.route</code>, so the App will boot up and show the Login Screen.</p>
<h2>Adding Parameters</h2>
<p>In case we wish to add multiple parameters in future, it is a good idea to store all parameters in a data class. Data classes can then be conveniently very easily converted/parsed to/from JSON using the <a href="https://github.com/google/gson">GSON library</a>.</p>
<pre><code class="hljs language-Kotlin"><span class="hljs-keyword">data</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ChangePasswordScreenArguments</span></span>(
    <span class="hljs-keyword">val</span> isFirstLogin: <span class="hljs-built_in">Boolean</span>
)
</code></pre>
<p>Convenience JSON/String extension file to abstract the JSON serialisation/deserialisation :</p>
<pre><code class="hljs language-Kotlin"><span class="hljs-keyword">object</span> ExtensionJSON {
    <span class="hljs-function"><span class="hljs-keyword">fun</span> ChangePasswordScreenArguments.<span class="hljs-title">toJson</span><span class="hljs-params">()</span></span>: String =
        URLEncoder.encode(Gson().toJson(<span class="hljs-keyword">this</span>), StandardCharsets.UTF_8.toString())

    <span class="hljs-function"><span class="hljs-keyword">fun</span> String.<span class="hljs-title">toChangePasswordScreenArguments</span><span class="hljs-params">()</span></span>: ChangePasswordScreenArguments =
        Gson().fromJson(<span class="hljs-keyword">this</span>, ChangePasswordScreenArguments::<span class="hljs-keyword">class</span>.java)
}
</code></pre>
<p>A parameter must be now added to the Change Password Screen NavHost composable definition to represent first login:</p>
<pre><code class="hljs language-Kotlin">NavHost(<span class="hljs-comment">/**/) {
	/**/</span>
	composable(<span class="hljs-string">"<span class="hljs-subst">${AppRoute.CHANGE_PASSWORD.route}</span>/{<span class="hljs-subst">${AppRouteParameter.CHANGE_PASSWORD_SCREEN_ARGS.value}</span>}"</span>) { backStackEntry ->
        <span class="hljs-keyword">val</span> changePasswordScreenJson =
                        backStackEntry.arguments?.<span class="hljs-keyword">get</span>(AppRouteParameter.CHANGE_PASSWORD_SCREEN_ARGS.value) <span class="hljs-keyword">as</span> String
        ChangePasswordScreen(
            navController = navController,
            arguments = changePasswordScreenJson.toChangePasswordScreenArguments()
        )
    }			
}

<span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">ChangePasswordScreen</span><span class="hljs-params">(navController: <span class="hljs-type">NavController</span>, arguments: <span class="hljs-type">ChangePasswordScreenArguments</span>)</span></span> { <span class="hljs-comment">/**/ }
</span></code></pre>
<p>With the addition of this parameter, we can then implement the business logic in the <code>ChangePasswordScreen</code> which depends on this parameter. However, we have still not resolved the matter of dynamic <code>startDestination</code>.</p>
<h2>startDestination which has parameters</h2>
<h3>First attempt : Crash</h3>
<p>In the <code>MainViewModel</code>, we will add code to the <code>init</code> block which retrieves the value of <code>firstLogin</code> for the current logged in user, after the app has booted, and update the startDestination accordingly.</p>
<pre><code class="hljs language-Kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MainViewModel</span></span>(application: Application) : AndroidViewModel(application) {
    <span class="hljs-comment">// Repo for Firestore collection containing user data. Contents not pertinent to this blog.</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> userRepository: UserRepository =
        getApplication&#x3C;App>().userRepository

    <span class="hljs-keyword">init</span> {
        viewModelScope.launch {
            <span class="hljs-keyword">if</span> (userRepository.isFirstLogin()) {
                updateStartDestination(
                    <span class="hljs-string">"<span class="hljs-subst">${AppRoute.CHANGE_PASSWORD.route}</span>/{<span class="hljs-subst">${AppRouteParameter.CHANGE_PASSWORD_SCREEN_ARGS.value}</span>}"</span>
                )
            } <span class="hljs-keyword">else</span> {
                updateStartDestination(AppRoute.HOME.route)
            }
        }
    }
}
</code></pre>
<p>Now, when <code>firstLogin</code> is set to <code>false</code>, the app successfully navigates to the Home Screen.
However, when <code>firstLogin</code> is set to <code>true</code>, the app crashes with the following exception :</p>
<pre><code class="hljs language-kotlin">java.lang.IllegalArgumentException: navigation destination change_password/{isFirstLogin:<span class="hljs-literal">true</span>} <span class="hljs-keyword">is</span> not a direct child of <span class="hljs-keyword">this</span> NavGraph

</code></pre>
<p>After some investigation, it seems that Jetpack Compose Navigation requires the composable's definition in the Navhost to match with the <code>startDestination</code>, which makes things complicated when the route contains arguments.</p>
<h3>Second attempt : Success</h3>
<p>The solution? You need to specify a <code>defaultValue</code> for the arguments to be used as a startDestination, as well as their <code>type</code>. In our case, our arguments are JSON, which is String.</p>
<pre><code class="hljs language-Kotlin">composable(
    route = <span class="hljs-string">"<span class="hljs-subst">${AppRoute.CHANGE_PASSWORD.route}</span>/{<span class="hljs-subst">${AppRouteParameter.CHANGE_PASSWORD_SCREEN_ARGS.value }</span>}"</span>,
    arguments = listOf(navArgument(AppRouteParameter.CHANGE_PASSWORD_SCREEN_ARGS.value ) {
        <span class="hljs-comment">//The required changes are the following 2 lines</span>
        type = NavType.StringType; defaultValue = ChangePasswordScreenArguments(
            isFirstLogin = <span class="hljs-literal">true</span>
        ).toJson()
})
) {
    backStackEntry ->
    <span class="hljs-keyword">val</span> changePasswordScreenJson =
        backStackEntry.arguments?.<span class="hljs-keyword">get</span>(AppRouteParameter.CHANGE_PASSWORD_SCREEN_ARGS.value) <span class="hljs-keyword">as</span> String
    ChangePasswordScreen(
        navController = navController,
        arguments = changePasswordScreenJson.toChangePasswordScreenArguments()
    )
}
</code></pre>
<h1>Credits</h1>
<p><em>Article Photo by <a href="https://www.lookphotos.com/en-us/search?photographer=f5520">Leue, Holger</a></em></p>
<p><em>Jetpack Compose logo <a href="https://developers-jp.googleblog.com/2019/06/whats-new-with-android-jetpack.html">Google Developers</a></em></p>
<p><em>Sextant Image <a href="https://www.freepik.com/free-photos-vectors/sextant">Freepik</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Creating custom layouts with Jetpack Compose]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/06/30/Custom-layouts-with-jetpack-compose</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/06/30/Custom-layouts-with-jetpack-compose</guid>
            <pubDate>Thu, 30 Jun 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Classic Android layouts are good for creating general purpose layouts, but when it comes to creating more complex layouts, you might find yourself creating custom views, and with Jetpack Compose it is now easier and more performant than ever to do so.</p>
<p>In this article we will use Jetpack Compose to create a star layout, the children of the layout are distributed on the tips of star arms, as shown in the picture below.
<img src="/assets/img/articles/2022-06-30-Custom-layouts-with-jetpack-compose/preview.webp" alt="Star Layout" title="Preview"></p>
<h2>A brief overview</h2>
<p>Compose goes through 3 phases to draw each frame on the screen:</p>
<ul>
<li><strong>Composition</strong>: where composable functions run to create a description of the UI.</li>
<li><strong>Layout</strong>: where views are measured and placed.</li>
<li><strong>Drawing</strong>: where each UI element is drawn.</li>
</ul>
<p>For creating a custom layout our focus will be on (you guessed it) the layout phase. Each node in this phase must go through 3 steps:</p>
<ul>
<li>Measure its children</li>
<li>Decide its own size</li>
<li>Place its children</li>
</ul>
<p>We will explain each step as we go through the implementation of the star layout.</p>
<p>To create a custom layout, Compose provides the Layout composable. Layouts like Row and Column also use the Layout composable under the hood.
The <code>StarLayout</code> composable function will take 5 arguments:</p>
<ul>
<li><em>radius</em>: defines the size of the star and it represents the distance from the center of the star to the tip of one arm.</li>
<li><em>modifier</em>: (optional) the modifier that can be passed to this layout to make some tweaks</li>
<li><em>drawStyle</em>: (optional) the draw style of the star, it helps us customize the look of the star</li>
<li><em>color</em>: (optional) the fill color of the star if the draw style is fill, or the stroke color if the draw style is stroke</li>
<li><em>content</em>: a composable that contains the items that will be displayed on the star layout</li>
</ul>
<p>The code will look like this :</p>
<pre><code class="hljs language-Kotlin"><span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">StarLayout</span><span class="hljs-params">(
   radius: <span class="hljs-type">Dp</span>,
   modifier: <span class="hljs-type">Modifier</span> = Modifier,
   drawStyle: <span class="hljs-type">DrawStyle</span> = Stroke(<span class="hljs-number">12</span>f)</span></span>,
   color: Color = Color.Yellow,
   content: <span class="hljs-meta">@Composable</span> () -> <span class="hljs-built_in">Unit</span>
) {
   <span class="hljs-keyword">var</span> starRadiusPx = with(LocalDensity.current) { radius.roundToPx() }
   <span class="hljs-keyword">var</span> count = <span class="hljs-number">0</span>
   <span class="hljs-keyword">var</span> totalRadius = <span class="hljs-number">0</span>
   <span class="hljs-keyword">var</span> maxChildDiameter = <span class="hljs-number">0</span>
   <span class="hljs-comment">// Use the Layout composable to create the custom layout</span>
   Layout(
        content = content,
        modifier = modifier
    ) { measurables, constraints ->
        <span class="hljs-comment">//measure child nodes to get a list of placeables </span>
        <span class="hljs-keyword">val</span> placeables = measurables.map { it.measure(constraints) }
        <span class="hljs-comment">// set the count variable value to be equal to children count</span>
        count = placeables.size
        <span class="hljs-comment">// find the diameter of the largest child so we can set the layout size properly </span>
        placeables.forEach {
          <span class="hljs-keyword">val</span> h = it.height.toDouble()
          <span class="hljs-keyword">val</span> w = it.width.toDouble()
          <span class="hljs-keyword">val</span> diameter = sqrt(h * h + w * w)
          <span class="hljs-keyword">if</span> (diameter > maxChildDiameter) maxChildDiameter = diameter.toInt()
        }
        <span class="hljs-comment">// set the total radius to be the sum of star radius and half the diameter of largest child </span>
        totalRadius = starRadiusPx + maxChildDiameter/<span class="hljs-number">2</span>
        <span class="hljs-comment">// set layout size to be twice the total radius in width and height</span>
        layout(totalRadius * <span class="hljs-number">2</span>, totalRadius * <span class="hljs-number">2</span>) {
            <span class="hljs-keyword">val</span> step = PI * <span class="hljs-number">2</span> / placeables.size
            <span class="hljs-keyword">var</span> angle = <span class="hljs-number">0.0</span>
            <span class="hljs-comment">// place child elements</span>
            placeables.forEach {
                it.place(
                    radiusPx - it.width / <span class="hljs-number">2</span> + (radiusPx * cos(angle)).toInt(),
                    radiusPx - it.height / <span class="hljs-number">2</span> + (radiusPx * sin(angle)).toInt(),
                )
                angle += step
            }
        }
    }
   }
</code></pre>
<h2>Measuring children</h2>
<pre><code class="hljs language-Kotlin"><span class="hljs-keyword">var</span> starRadiusPx = with(LocalDensity.current) { radius.roundToPx() }
<span class="hljs-keyword">var</span> count = <span class="hljs-number">0</span>
<span class="hljs-keyword">var</span> totalRadius = <span class="hljs-number">0</span>
<span class="hljs-keyword">var</span> maxChildDiameter = <span class="hljs-number">0</span>
</code></pre>
<p>We use the <em>starRadiusPx</em> variable to save the radius value in pixels as we will need it later to draw the star, we also declare the <em>count</em> variable to store the count of child elements, which will be later used to draw the star arms.</p>
<p>Furthermore, we declare the <em>totalRadius</em> variable which will be used to set the final layout size, and we use the <em>maxChildDiameter</em> variable to store the diameter of the largest child.</p>
<pre><code class="hljs language-Kotlin">Layout(
        content = content,
        modifier = modifier
    ) { measurables, constraints ->
        <span class="hljs-comment">//measure child nodes to get a list of placeables </span>
        <span class="hljs-keyword">val</span> placeables = measurables.map { it.measure(constraints) }
        <span class="hljs-comment">// set the count variable value to be equal to children count</span>
        count = placeables.size
        ..........
</code></pre>
<p>Then we use the Layout composable to create our custom layout, which requires three arguments, a content, a modifier, and a measure policy. We pass the content and the modifier, and a lambda expression that Kotlin accepts instead of a MeasurePolicy implementation. Due to the existence of SAM conversions, in the lambda expression, we have measurables, each related to one of the layout children, and we also have constraints that we should consider when measuring the children.</p>
<p>Our <em>StarLayout</em> is not a tough parent, so it allows the measurement of its children to happen without tweaking the <em>constraints</em>. We map the measurables to a list of placeables by measuring each element with the provided <em>constraints</em>.
Then we set the value of the <em>count</em> variable to be the size of the measurables.</p>
<h2>Setting layout size</h2>
<pre><code class="hljs language-Kotlin">placeables.forEach {
  <span class="hljs-keyword">val</span> h = it.height.toDouble()
  <span class="hljs-keyword">val</span> w = it.width.toDouble()
  <span class="hljs-keyword">val</span> diameter = sqrt(h * h + w * w)
  <span class="hljs-keyword">if</span> (diameter > maxChildDiameter) maxChildDiameter = diameter.toInt()
}
<span class="hljs-comment">// set the total radius to be the sum of star radius and half the diameter of largest child </span>
totalRadius = starRadiusPx + maxChildDiameter/<span class="hljs-number">2</span>
</code></pre>
<p>Here we find the diameter of the largest child by going through each placeable to measure its diameter. If it is larger than the maximum value we set the maximum value to be equal to it.</p>
<p>And finally, we set the total radius to be equal to the sum of the star radius and half of the diameter of the largest child.</p>
<pre><code class="hljs language-Kotlin">  layout(totalRadius * <span class="hljs-number">2</span>, totalRadius * <span class="hljs-number">2</span>) {
</code></pre>
<p>We use the totalRadius parameter to set the size of the star, so both width and height will be twice the totalRadius. We set the size of the layout by calling the layout function passing totalRadius*2 as the value for both width and height.</p>
<h2>Placing children</h2>
<pre><code class="hljs language-Kotlin">.............
 <span class="hljs-keyword">val</span> step = PI * <span class="hljs-number">2</span> / placeables.size
 <span class="hljs-keyword">var</span> angle = <span class="hljs-number">0.0</span>
 <span class="hljs-comment">// place child elements</span>
 placeables.forEach {
     it.place(
         totalRadius - it.width / <span class="hljs-number">2</span> + (starRadiusPx * cos(angle)).toInt(),
         totalRadius - it.height / <span class="hljs-number">2</span> + (starRadiusPx * sin(angle)).toInt(),
     )
     angle += step
}
</code></pre>
<p>If we think about placing the children in the layout we will find that it is like distributing items equally on a circle, the position of each item will be rotated around the center of the circle by an angle equal to 2*PI divided by the children's count.
<img src="/assets/img/articles/2022-06-30-Custom-layouts-with-jetpack-compose/star_distribution.webp" alt="Items distribution on the star" title="distribution"></p>
<p>In math, we get the coordinates of a any given point on a circle by using the following formula: <code>X = R.cos(a), Y = R.sin(a)</code>, where <code>R</code> is the radius of the circle, and <code>a</code> is the angle between the X-axis and the line connecting the center of the circle to the given point.</p>
<p>In our case we need to distribute the children equally on the circle, which basically means distributing points equally on a circle, this can be done by using the previous equations while increasing the value of (a) each time by a  2*PI divided by the children count.</p>
<p>We know that in Android the origin of any layout is at the top left corner, so to distribute the children correctly inside the layout we have to offset their coords by the total radius, this way we make sure that the children are distributed equally on a circle which its center is in the center of the layout.</p>
<p>We also need to make sure to place each child in a way where its center is on the circle and not its top left corner, that is why we also have <em>it.width / 2</em> and <em>it.height / 2</em>.</p>
<h2>Drawing the star</h2>
<p><img src="/assets/img/articles/2022-06-30-Custom-layouts-with-jetpack-compose/drawing_star.gif" alt="Drawing a star" title="drawing a Star"></p>
<p>If you check the image above you can see that we can think of drawing a star as connecting points between 2 circles with different radii (Rmax and Rmin), the values of Rmax and Rmin define the shape and size of the star, as you can see we alternate between the 2 circles and we increase the angle by a specific value. It is very similar to the concept of distributing items equally on a circle, but we just alternate the radius each time between the 2 values Rmax and Rmin, while increasing the angle each time.</p>
<p>To draw the star shape we will need to draw a path on the canvas, for creating the star path we will use the following code:</p>
<pre><code class="hljs language-Kotlin"><span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">createStarPath</span><span class="hljs-params">(
    maxR: <span class="hljs-type">Float</span>,
    minR: <span class="hljs-type">Float</span>,
    tipCount: <span class="hljs-type">Int</span>,
)</span></span>: Path {
    <span class="hljs-keyword">val</span> path = Path()
    <span class="hljs-keyword">val</span> step = PI / tipCount
    <span class="hljs-keyword">var</span> r = maxR
    <span class="hljs-keyword">var</span> angle = <span class="hljs-number">0.0</span>;
    path.moveTo(<span class="hljs-number">2</span> * maxR, maxR)
    repeat(tipCount * <span class="hljs-number">2</span>) {
        angle += step
        r = <span class="hljs-keyword">if</span> (r == maxR) minR <span class="hljs-keyword">else</span> maxR
        path.lineTo(maxR + r * cos(angle).toFloat(), maxR + r * sin(angle).toFloat())
    }
    path.close()
    <span class="hljs-keyword">return</span> path
}
</code></pre>
<p>The previous code is basically an implementation of the idea earlier, <em>maxR</em> is the radius of the larger circle, <em>minR</em> is the radius of the small circle, and <em>tipCount</em> is the count of star tips, which will also help us define the <em>step</em> value. Here we see that the amount of lines drawn is twice the count of the star tips, that is also why the step value is <em>PI / tipCount</em> instead of <em>2</em>PI / tipCount*.</p>
<p>To draw the star on the canvas we will use the <code>drawWithCache</code>, so let's modify this section of the code:</p>
<pre><code class="hljs language-Kotlin">Layout(
        content = content,
        modifier = modifier
    ) { measurables, constraints ->
</code></pre>
<p>to become like this:</p>
<pre><code class="hljs language-Kotlin">Layout(
        content = {StarLayoutScope.content()},
        modifier = modifier
            .drawWithCache {
                onDrawBehind {
                    <span class="hljs-keyword">val</span> maxR = starRadiusPx.toFloat()
                    <span class="hljs-keyword">val</span> minR = maxR*<span class="hljs-number">0.5f</span>
                    <span class="hljs-keyword">val</span> path = createStarPath(maxR, minR,count)
                    path.translate(Offset(maxChildDiameter * <span class="hljs-number">0.5f</span>, maxChildDiameter * <span class="hljs-number">0.5f</span>))
                    drawPath(
                        path,
                        color = color,
                        style = drawStyle
                    )
                }
            }
    ) { measurables, constraints ->
</code></pre>
<p>We want to draw the star as a background for the layout so we draw it inside <code>onDrawBehind</code>. We set the maxR to be equal to the star radius, we set the minR to be half of maxR, you can tweak this value to get stars with different looks, but for now I will use half of maxR.</p>
<p>Then we create the star path using the function we wrote earlier, and then we offset the path by half of the diameter of the largest child on both the X and Y axis, to make sure to align the star tips with the children's position. <em>We previously added width/2 and height /2 to the position of each child in the <strong>Placing children</strong> section.</em></p>
<h2>Testing the layout</h2>
<p>When you finish implementing a cool feature, you are usually thrilled to present it and get feedback. You might be tempted to care less about how that feature looks, but we will not do that here.</p>
<p>In order to make our work look better and presentable, we will create a nice composable function for the child element:</p>
<pre><code class="hljs language-Kotlin"><span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">StarItem</span><span class="hljs-params">(
    selected: <span class="hljs-type">Boolean</span>,
    radius: <span class="hljs-type">Dp</span> = <span class="hljs-number">32.</span>dp,
    modifier: <span class="hljs-type">Modifier</span> = Modifier,
    content: @<span class="hljs-type">Composable</span> () -> <span class="hljs-type">Unit</span>
)</span></span> {

    Box(
        modifier = modifier
            .clip(CircleShape)
            .size(<span class="hljs-keyword">if</span> (selected) radius.times(<span class="hljs-number">1.2f</span>) <span class="hljs-keyword">else</span> radius)
            .background(<span class="hljs-keyword">if</span> (selected) Color.Green <span class="hljs-keyword">else</span> Color.Blue)
            .then(
                Modifier.border(<span class="hljs-keyword">if</span> (selected) <span class="hljs-number">4.</span>dp <span class="hljs-keyword">else</span> <span class="hljs-number">0.</span>dp, Color.Yellow, CircleShape)
            )
    ) {

    }
}
</code></pre>
<p>This composable function will draw a blue circle if the item is not selected, and a green circle with a yellow border if the item is selected. The important parameters for this function are <em>selected</em> which indicates whether the item is selected or not and <em>radius</em> which defines the radius of the circle.</p>
<p>Now we can use it as an item for our star layout, let's type the following code in <em>MainActivity</em>:</p>
<pre><code class="hljs language-Kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MainActivity</span> : <span class="hljs-type">ComponentActivity</span></span>() {
    <span class="hljs-keyword">val</span> items = listOf(<span class="hljs-literal">false</span>, <span class="hljs-literal">false</span>, <span class="hljs-literal">true</span>, <span class="hljs-literal">false</span>, <span class="hljs-literal">false</span>)
    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onCreate</span><span class="hljs-params">(savedInstanceState: <span class="hljs-type">Bundle</span>?)</span></span> {
        <span class="hljs-keyword">super</span>.onCreate(savedInstanceState)
        setContent {
            Star_layout_articleTheme {
                <span class="hljs-comment">// A surface container using the 'background' color from the theme</span>

                Column(
                    horizontalAlignment = Alignment.CenterHorizontally
                ) {

                    StarLayout(radius = <span class="hljs-number">160.</span>dp) {
                        items.forEach {

                            StarItem(selected = it, radius = <span class="hljs-number">42.</span>dp) {}
                        }
                    }
                }
            }
        }
    }
}
</code></pre>
<p>Nothing special here, we just declared a list of Booleans that will represent the items, then we put the <em>StarLayout</em> inside a Column layout. We also set the radius of the layout to 160.dp, then for the layout content we create a <em>starItem</em> for each one of the items providing the selected value and the radius.</p>
<p>You can also access the <a href="https://github.com/KamelMohamad/star_layout_article">source code here</a>.</p>
<p>I hope you found this information useful, there are lots of improvements that can be done and cool ideas to add, but I had to stop here because the article is already very long. Hopefully, we can talk about creating custom modifiers for a specific layout in another article soon.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A Streamlit app to browse drug sensitivity predictions based on Transfer Learning]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/06/21/streamlit_app_for_transfer_learning</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/06/21/streamlit_app_for_transfer_learning</guid>
            <pubDate>Tue, 21 Jun 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>At Monstarlab we help build dedicated Machine Learning (ML) applications to tackle a client challenge that is best solved with this kind of technology. Within the ML domain, Deep Learning (DL) has been successfully applied to tackle data science challenges in many different areas. However, currently, the applications of DL in Life Sciences have been more limited.</p>
<p>In this article, we will describe how to use previously gathered, unrelated but large datasets to train a DL model that is then used to make predictions with a newer, smaller and dedicated dataset. This methodology is generally called Transfer Learning (TL). We also show how this can be embedded in a web app built with Streamlit in order to explore how the model performs on various tasks and datasets.</p>
<h2>What's this about</h2>
<p>It is difficult to find large, good-quality data to use in DL models. Depending on the number of parameters to train, which can be several millions, in order to train a DL model one often needs to have high-quality data consisting of thousands of samples. A rule of thumb for a pre-hoc determination of the number of samples needed to obtain a 95% accuracy from a DL model is 1000 samples for each category to predict.</p>
<p>Over the years, the scientific community in both private and public sectors have collected, curated and in some cases made publicly available datasets that the Machine Learning community routinely utilises to build and test models. What about using these data to tackle related challenges for which good data are hard to find?</p>
<p>In biotech and pharmaceutical industries, challenges for which data are hard to find include the prediction of molecule-to-molecule interactions, the cellular response to external stimuli such as drugs and antibiotics, or the prediction of cell growth and behavior. Here we demonstrate the use of public experimental gene expression data on cancer cell response to drugs applied to predict drug response of single cancer cells, for which adequate datasets are hard to find.</p>
<img src="/assets/img/articles/2022-06-21-streamlit-app-for-transfer-learning/expression_fig1.gif" width="300" height="150">
<h2>Transfer Learning: what it is and how it works.</h2>
<blockquote>
<p><em>Transfer learning (TL) is a research problem in machine learning (ML) that focuses on storing knowledge gained while solving one problem and applying it to a different but related problem. For example, knowledge gained while learning to recognize cars could be applied to recognize trucks. (Wikipedia)</em></p>
</blockquote>
<p><br><br></p>
<p>Some advantages of using TL include a shorter training time and improved performance of DL models, as well as a reduced need of new and large training data. The way TL is adopted depends on the specific task at hand and factors such as whether the source and target data domains or tasks are the same or not.</p>
<p>TL is used to transfer knowledge learned with one dataset for a certain task to a model using a different dataset and/or tackling a different task. There are three basic ways to transfer this knowledge:</p>
<ol>
<li>
<p><strong>Inductive TL.</strong> In this type of TL, the source and target tasks are the same, however, are still different from one another. The model will use inductive biases from the source task to improve the performance of the target task. The source task may or may not contain labeled data, which leads to the model using multitask learning or self-taught learning respectively.</p>
</li>
<li>
<p><strong>Unsupervised TL</strong>. In this case, the source and target data are similar and unlabeled, however, the tasks are different. Here the model trained on source data performs hopefully better in identifying patterns in target data. Dimensionality reduction and clustering are two of many unsupervised learning techniques. This approach can also be used for feature reduction if the target dataset is very large.</p>
</li>
<li>
<p><strong>Transductive TL</strong>. In this type of TL, the source and target tasks share similarities, however, the domains are different. For example, the source domain contains a lot of labeled data while the target domain does not. Here TL is used for domain adaptation.</p>
</li>
</ol>
<img src="/assets/img/articles/2022-06-21-streamlit-app-for-transfer-learning/Transfer_learning_idea.webp" width="650" height="300">
<h2>Cancer cell lines and single cells</h2>
<p>The use of single-cell omics data has seen in recent years a very strong interest in the pharmaceutical industry. The availability of new technology and computational and bioinformatics methods allow now to obtain a much larger amount of data at a much higher resolution.</p>
<p>Single-cell RNA-sequencing (scRNA-seq) data are very valuable to dissect the individual response of cancer subpopulations to specific drugs. However, public, good quality and benchmarked single-cell data that can be used to train DL models are limited. On the other hand, abundant <em>in vitro</em> drug screening studies have been conducted over the years, leading to the availability of drug response data on different cancer cell lines (CCLs).</p>
<p>CCls bulk RNAseq data can be a great resource to help train DL models that predict drug responses at single-cell level based on scRNA-seq data. Here we show how this can be done via TL.</p>
<img src="/assets/img/articles/2022-06-21-streamlit-app-for-transfer-learning/single_cells.webp" width="400" height="200">
<h2>Transfer Learning applied to RNAseq data</h2>
<p>We present here a framework published recently by a team of academic researchers under the name scDEAL (J. Chen et al., 2021). The framework involves the following 4 major components:</p>
<ol>
<li>a bulk gene feature extractor</li>
<li>a single-cell gene feature extractor</li>
<li>a drug response predictor</li>
<li>a whole Deep Transfer Learning (DTL) model combining all the extractors and the predictor as one.</li>
</ol>
<p>Without going too much into the details, the extractors extract gene expression features that best represent the original datasets. The predictor is trained to receive bulk RNAseq data and minimize the difference between predicted and true drug response labels. The whole pre-training of these models also aims at generating the best Neural Network weights to be used within the DTL model. This model works in a multitask (Inductive TL) manner by minimizing the differences in gene features between the two extractors, as well as the loss function of the common predictor output.</p>
<img src="/assets/img/articles/2022-06-21-streamlit-app-for-transfer-learning/Transfer_learning_concept.webp" width="400" height="200">
<h2>What is Streamlit</h2>
<p>Streamlit is an open-source Python library that makes it easy to create custom web apps for ML and data science. Streamlit is an elegant and simple Python-only library, which helps reduce the time needed to make a web app from weeks to hours especially when the ML framework is also developed in Python. Streamlit has seen a rapid expansion since its inception and has garnered a large and bustling community of developers and enthusiasts.</p>
<p>Based on the scDEAL framework and the available RNAseq data, we were able to quickly build a Streamlit app enabling us to run and test different versions of the DTL model, using different parameters and different encoder/decoder pairs.</p>
<p>Through the web app, changing and running the model, and explore the results, was done by simply using buttons and dropdown menus. In other words, a web app allows a scientist with no coding experience to explore and test ML and DL applications very easily.</p>
<img src="/assets/img/articles/2022-06-21-streamlit-app-for-transfer-learning/streamlit.webp" width="650" height="200">
<h2>Explore DTL model predictions</h2>
<p>We tested one DTL model in its ability to predict resistant and sensitive single cells based on scRNAseq data. The scDEAL framework covers many different types of CCL and drug combinations. All these combinations were integrated into our Streamlit app, allowing for an easy exploration of all possible combinations, and calculating performance metrics of the DTL model.</p>
<p>Precision and recall, two very popular performance metrics, can be calculated based on either the number of correctly called drug-sensitive or resistant cells. For example, when predicting the response of prostate cancer cells to the drug Docetaxel, the model showed high prediction performance of drug-sensitive cells with a 0.83 precision and 0.98 recall.</p>
<img src="/assets/img/articles/2022-06-21-streamlit-app-for-transfer-learning/confusion_matrix.webp" width="400" height="200">
<h2>Conclusion</h2>
<p>The main obstacle in developing DL tools for predicting single-cell drug responses, and for other tasks in biological sciences, is the lack of sufficient training power due to the limited number of benchmarked data in the public domain.</p>
<p>Here, we demonstrate how DTL can be used to integrate and trasfer knowledge from cell line to single-cell data. Additionally, we show how this modeling framework can be embedded in an easy-to-use web application for researchers with little or no DL or coding knowledge.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[WWDC22 - A first look at Apple's new Augmented Reality features]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/06/07/WWDC22-A-first-look-at-Apples-new-Augmented-Reality-features</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/06/07/WWDC22-A-first-look-at-Apples-new-Augmented-Reality-features</guid>
            <pubDate>Tue, 07 Jun 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Over 34 million developers are part of Apple's developers community with access to great frameworks including AR specific ones like ARKit and RealityKit. Here is an overview of what we know so far from this year's <a href="https://developer.apple.com/wwdc22/">WWDC</a> when it comes to augmented reality, with more details to unravel this week after all the engineering sessions. You can find a list of recommended AR sessions and labs to check out from this year's WWDC at the end of the article.</p>
<h1><a href="https://developer.apple.com/augmented-reality/roomplan/">RoomPlan</a></h1>
<p><img src="/assets/img/articles/2022-06-07-WWDC22-A-first-look-at-Apples-new-Augmented-Reality-features/roomplan.webp" alt="">
<em>Image source: <a href="https://developer.apple.com/augmented-reality/roomplan/">Apple</a></em></p>
<p>A great use of the LiDAR scanner to create 3D floor plans for rooms in real-time. What is great about this new Swift API is that it also provides characteristics like dimensions and what types of furnitures and appliances are included in the scanned environment. Instructions and structural highlights are available to help guide the users in the scanning process.</p>
<p>My colleague from Berlin, Cristian Diaz was super fast in testing this out and sharing his results. RoomPlan can provide the content in USD and USDZ file formats, which can be imported and further developed in compatible tools like Cinema 4D or AutoCAD. Opening the scanned room file and seeing the individual components is really cool. The walls, chairs, tables and even the refrigerator and oven were identified and named accordingly as you can see in the example below.</p>
<p><img src="/assets/img/articles/2022-06-07-WWDC22-A-first-look-at-Apples-new-Augmented-Reality-features/roomplan-example.webp" alt=""></p>
<p>Applications in the real estate and e-commerce sectors are quite obvious, but I am looking forward to see all the creative ways developers will make use of it!</p>
<p><em>Available on iOS 16.0+, iPadOS 16.0+ and Mac Catalyst 16.0+.</em></p>
<hr>
<p>More information for developers can be found on <a href="https://developer.apple.com/documentation/RoomPlan">the new RoomPlan framework documentation page</a>.</p>
<h1><a href="https://developer.apple.com/augmented-reality/arkit/">ARKit 6</a></h1>
<p>ARKit was first released in 2017, as Apple's go-to AR framework. In 2019, RealityKit came along with improvements as it was built from the ground up specifically for AR experiences. Here are the main updates announced this year with ARKit 6, while no news have been announced for RealityKit:</p>
<h2>4K Video Capture</h2>
<p>Available on iPhone 11 or later and iPad Pro (5th generation), this new feature will allow better quality video capture, especially great for applications that outputs video content.</p>
<h2>Image and video capture improvements</h2>
<blockquote>
<p>"With ARKit 6, you can capture a high-resolution background image during an ARKit session, which is great for enabling higher-quality virtual object reconstruction and for photography apps that want to guide users to the perfect shot while maximizing final image quality. ARKit 6 also introduces support for HDR video and adds EXIF tags, such as exposure and white balance, to every ARKit frame. And you can now directly control the setting exposure, white balance, and focus of the camera during an ARKit session." - <a href="https://developer.apple.com/augmented-reality/arkit/">Apple</a></p>
</blockquote>
<h2>Location Anchors (in more cities)</h2>
<p>Location anchors were introduced in 2020 but with a limited availability in major United States cities and London. The update we are getting this year is availability in the following additional cities:</p>
<ul>
<li><strong>Australia</strong>: Sydney, Melbourne</li>
<li><strong>Canada</strong>: Vancouver, Montreal, Toronto</li>
<li><strong>Japan</strong>: Fukuoka, Hiroshima, Kyoto, Nagoya, Osaka, Tokyo, Yokohama</li>
<li><strong>Singapore</strong></li>
</ul>
<h2>Improvements to Motion Capture</h2>
<p>The Motion Capture feature lets you capture the motion of a person in real time just with the use of your device. The new ARKit 6 highlights that this feature now includes tracking for your left and right ears, with improved overall body pose detection. Great potential for face filters and health applications with those additional improvements.</p>
<hr>
<p>More information for developers can be found on <a href="https://developer.apple.com/documentation/arkit">the updated ARKit framework documentation page</a>.</p>
<h1><a href="https://graphics.pixar.com/usd/release/index.html">USD advancements</a></h1>
<p>Wednesday is all about USD in WWDC's AR schedule with sessions on the latest advancements in working with those assets. Understanding the core concepts of this file format is crucial if you are an Apple AR developer.</p>
<h1>WWDC22 AR sessions</h1>
<p><strong>Tuesday June 7:</strong></p>
<ul>
<li><a href="https://developer.apple.com/videos/play/wwdc2022-10128">Bring your world into augmented reality</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2022/10127/">Create parametric 3D room scans with RoomPlan</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2022/10131/">Qualities of great AR experiences</a></li>
</ul>
<p><strong>Wednesday June 8:</strong></p>
<ul>
<li><a href="https://developer.apple.com/videos/play/wwdc2022-10141">Explore USD tools and rendering</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2022-10129">Understand USD fundamentals</a></li>
</ul>
<p><strong>Thursday June 9:</strong></p>
<ul>
<li><a href="https://developer.apple.com/videos/play/wwdc2022-10126">Discover ARKit 6</a></li>
</ul>
<h1>WWDC22 1-on-1 Developer Labs</h1>
<p><strong>Tuesday June 7:</strong></p>
<ul>
<li><a href="https://developer.apple.com/wwdc22/labs-and-lounges/dashboard/S7G7YK3P64/dashboard">USD and AR Quick Look lab</a></li>
<li><a href="https://developer.apple.com/wwdc22/labs-and-lounges/dashboard/NTS6G342QK/dashboard">RealityKit and Reality Composer lab</a></li>
</ul>
<p><strong>Wednesday June 8:</strong></p>
<ul>
<li><a href="https://developer.apple.com/wwdc22/labs-and-lounges/dashboard/W74Q8UTKCJ/dashboard">Object Capture lab</a></li>
<li><a href="https://developer.apple.com/wwdc22/labs-and-lounges/dashboard/YVYT5FMYBK/dashboard">USD and AR Quick Look lab</a></li>
</ul>
<p><strong>Thursday June 9:</strong></p>
<ul>
<li><a href="https://developer.apple.com/wwdc22/labs-and-lounges/dashboard/SK22SGR75J/dashboard">RoomPlan lab</a></li>
<li><a href="https://developer.apple.com/wwdc22/labs-and-lounges/dashboard/WGRFWYCGAV/dashboard">ARKit lab</a></li>
<li><a href="https://developer.apple.com/wwdc22/labs-and-lounges/dashboard/9MRC7GFH7G/dashboard">RealityKit and Reality Composer lab</a></li>
<li><a href="https://developer.apple.com/wwdc22/labs-and-lounges/dashboard/Y369YW9X39/dashboard">Core Motion lab</a></li>
<li><a href="https://developer.apple.com/wwdc22/labs-and-lounges/dashboard/4S93VWZR7B/dashboard">Object Capture lab</a></li>
<li><a href="https://developer.apple.com/wwdc22/labs-and-lounges/dashboard/MTUCQS2B94/dashboard">RoomPlan lab</a></li>
</ul>
<p><strong>Friday June 10</strong></p>
<ul>
<li><a href="https://developer.apple.com/wwdc22/labs-and-lounges/dashboard/MALF95YZVL/dashboard">ARKit lab</a></li>
</ul>
<h1>WWDC22 Digital lounges</h1>
<p>Last year Apple introduced Digital Lounges, which is a Slack workspace you can register based on the topics you are interested in. This year "Augmented Reality" has been added as a topic which will have Q&#x26;A sessions on ARKit, RealityKit, Reality Composer, Object Capture, USD and RoomPlan.
<img src="/assets/img/articles/2022-06-07-WWDC22-A-first-look-at-Apples-new-Augmented-Reality-features/developer-lounges.webp" alt=""></p>
<h2>One more thing... or not?</h2>
<p>The iOS augmented reality community has a bit of mixed feelings about this year's WWDC. Especially when companies like Snap and Niantic are really evolving in this space and catching developers interest. Nothing new on Reality Composer and RealityKit as far as we can see, which seems odd and it just makes me wonder: <em>is Apple saving those for a special AR-focused event coming up?</em></p>
<h2>Resources</h2>
<ul>
<li><a href="https://developer.apple.com/augmented-reality/">Augmented Reality - Apple Developer</a></li>
<li><a href="https://engineering.monstar-lab.com/en/post/2022/04/07/ARAnchors-Bringing-Virtual-Objects-into-the-real-world/">ARAnchors - Bringing virtual objects into the real world</a></li>
<li><a href="https://youtu.be/xSLDST-a6qU">Augmented reality face filters with iOS - Borderless Engineering Conference 2021</a></li>
<li><a href="https://youtu.be/v2Br76XFAYQ">Building Augmented Reality experiences with iOS - Engineering Awesome Conference 2020</a></li>
<li><a href="/en2020-09-07-Easy-web-augmented-reality-with-ar-quick-look">Easy web augmented reality with AR Quick Look</a></li>
<li><a href="/en2020-04-26-how-to-convert-3d-models-to-usdz-files-using-apples-reality-converter">How to convert 3D models to USDZ files using Apple's Reality Converter</a></li>
<li><a href="/en2019-10-07-Using-USDZ-for-a-better-AR-experience">Using USDZ for a better AR experience</a></li>
<li><a href="/en2019-12-31-How-to-make-an-Augmented-Reality-decorating-experience-app-with-AR-Quick-Look">How to make an augmented reality decorating experience app with AR Quick Look</a></li>
</ul>
<p><em>Article Photo by <a href="https://developer.apple.com/augmented-reality/roomplan/">Apple</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to create a chat bot using Google's Dialogflow]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/06/06/How-to-create-a-chat-bot-using-Googles-Dialogflow</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/06/06/How-to-create-a-chat-bot-using-Googles-Dialogflow</guid>
            <pubDate>Mon, 06 Jun 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Every day we are asking "Hey google", "Hi google", "Please play this", do this, do that, but are we considering what's going on in the background? I made a small chatbot, named <code>ml-bot</code>, to find out how such a system works.
<img src="/assets/img/articles/2022-06-06-How-to-create-a-chat-bot-using-Googles-Dialogflow/about-this-blog.webp" alt=""></p>
<h1>What is Dialogflow?</h1>
<p>Dialogflow is a Google AI Service, available in multiple languages.
By using it we can easily make conversational voice or chat bots.
Recently it is getting hard to write code from scratch due to advanced and super-fast client expectations. In a business situation we might need to make a running bot example in a couple of hours or a day. This is the point when thinking about using Dialogflow. Very easy to integrate on web and mobile apps.</p>
<h2>Advantages of Dialogflow</h2>
<ul>
<li>ML/AI is used</li>
<li>Easy to use</li>
<li>Handles the complex conversation</li>
<li>Available in many languages</li>
<li>Integration with other tools is very easy</li>
<li>Easy webhook integration (HTTP request events on specific action inside Dialogflow system)</li>
</ul>
<h1>Types of Dialogflow services</h1>
<h2>1. Dialogflow CX (Customer Experience)</h2>
<p>Designed for complex flows of conversation, it uses a state machine for managing the states of conversation.
Below is an example of what a food ordering flow for a pizza delivery agent can look like. Food ordering systems are a simple example where Dialogflow CX can be used.
<img src="/assets/img/articles/2022-06-06-How-to-create-a-chat-bot-using-Googles-Dialogflow/cx.webp" alt="">
<em>Image source: <a href="https://cloud.google.com/dialogflow/cx/docs/basics">Google</a></em></p>
<h2>2. Dialogflow ES (Essentials)</h2>
<p>This is the older version of Dialogflow, which has been renamed to ES. While designing a bot, if we have simple and small agents, we should use ES.
<img src="/assets/img/articles/2022-06-06-How-to-create-a-chat-bot-using-Googles-Dialogflow/es.webp" alt="">
<em>Image source: <a href="https://cloud.google.com/dialogflow/es/docs/basics">Google</a></em></p>
<h1>Where should I use Dialogflow?</h1>
<ul>
<li>Messaging tools like Slack are very easy to integrate with Dialogflow. See here for <a href="https://cloud.google.com/dialogflow/es/docs/integrations/slack">more details</a>.</li>
<li>Voice bots can be easily made with Dialogflow, for serving all types of users, including physically challenged people.</li>
<li>Chat bots on any mobile app platforms (Android, iOS, Flutter etc) and some kinds of web apps. Very accurate and fast response.</li>
</ul>
<h1>What should I know before writing code or designing bots using CX?</h1>
<h3>What are <strong>Agents</strong>?</h3>
<p>Agents are the entry point to a bot system. Agent basically interact with end users and process the information behind the system.</p>
<p>Example of agent - here "ml-bot" is the agent name
<img src="/assets/img/articles/2022-06-06-How-to-create-a-chat-bot-using-Googles-Dialogflow/ml-bot-agent.webp" alt=""></p>
<h3>What is <strong>Intent</strong>?</h3>
<p>Intent, in simple words, is what the user wants to say.
It is kind of the end user's intention. We can also combine many intents and do much more together.</p>
<p>Example of Dialogflow CX intents - "WecomeIntent", "No", "Yes" etc.
<img src="/assets/img/articles/2022-06-06-How-to-create-a-chat-bot-using-Googles-Dialogflow/intent.webp" alt=""></p>
<p>Example of a welcome intent on a page
<img src="/assets/img/articles/2022-06-06-How-to-create-a-chat-bot-using-Googles-Dialogflow/intent-start.webp" alt=""></p>
<p>The same intent can be described in the following ways:
"hi", "just going to say hi", "long time no see", "hello hi" etc.
<img src="/assets/img/articles/2022-06-06-How-to-create-a-chat-bot-using-Googles-Dialogflow/intent-types-user-say.webp" alt=""></p>
<h3>What are <strong>Entity</strong> and <strong>Entity types</strong>?</h3>
<p>Entity is the data taken by the user. In the below picture eggs benedict is an example of entity.
The way to fetch the user's data is called entity types. In the picture below, <code>breakfast-type</code> is the entity type and <code>eggs benedict</code> is the entity which has synonyms (eggs, Eggs, eggs benedict). Synonyms is the way the user can type his message.
<img src="/assets/img/articles/2022-06-06-How-to-create-a-chat-bot-using-Googles-Dialogflow/entity-types.webp" alt=""></p>
<h3>What are <strong>Routes</strong>?</h3>
<p>Routes are the way to control the flow inside a page. It is more about conditional logic in a flow.</p>
<h3>What are <strong>Pages</strong>?</h3>
<p>Page represents the CX session. When started, the conversation page becomes active. The flow start and end state are handled by special pages.
In the below example, "Breakfast" and "Coffee" are the pages while ordering the food in a restaurant.
Here is one example how to add a page "Order Page" and the already made pages "Breakfast" and "Coffee".
<img src="/assets/img/articles/2022-06-06-How-to-create-a-chat-bot-using-Googles-Dialogflow/Add-page-page-example.webp" alt=""></p>
<h3>What are <strong>Parameters</strong>?</h3>
<p>We can take the value sent by the user to the bot. These values are called parameters.</p>
<h3>What is <strong>Flow</strong>?</h3>
<p>Flow is a module or unit which defines the simple part of a conversation and its flow. Flow acts as a path in the conversation. In the below example, "Breakfast" is a part of conversation while ordering the food in a restaurant.
Example of the coffee flow:
<img src="/assets/img/articles/2022-06-06-How-to-create-a-chat-bot-using-Googles-Dialogflow/coffee-flow.webp" alt=""></p>
<h3>What is <strong>Fulfillment</strong> - Bot response?</h3>
<p>The response given by the bot to users is called fulfillment.
"しばらくお待ちください" (tr. "Please wait") is an example of fulfillment which the agent says after the order of the breakfast has been placed.</p>
<h1>Dialogflow system architecture</h1>
<p>Below is an example when the user says "Hi" to the bot. The message goes to the system and the system connects to the Dialogflow API. Then if we have a webhook, then retract with that.
And now the process goes back, the Dialogflow API returns the response by detect intent and that is returned to the system we have and finally reaches the user.</p>
<p><img src="/assets/img/articles/2022-06-06-How-to-create-a-chat-bot-using-Googles-Dialogflow/cx-interaction-sequence.webp" alt="">
<em>Image source: <a href="https://cloud.google.com/dialogflow/cx/docs/basics">Google</a></em></p>
<h1>Implement a chatbot (ml-bot) using the CX visual builder</h1>
<p>Using the below steps:</p>
<h2>Step 1</h2>
<p>Create a project on <a href="https://dialogflow.cloud.google.com/">Google Dialog</a> and login to Dialogflow Cloud: Cloud Console > New Project > Enter name and select other details as per below image.</p>
<p><img src="/assets/img/articles/2022-06-06-How-to-create-a-chat-bot-using-Googles-Dialogflow/create-project.webp" alt=""></p>
<h2>Step 2</h2>
<p><a href="https://console.cloud.google.com/marketplace/product/google/dialogflow.googleapis.com?project=nodal-crawler-347903">Enable the Dialogflow API</a>. Attached image is after the API is enabled. First time you should see an <code>Enable</code> button.</p>
<p><img src="/assets/img/articles/2022-06-06-How-to-create-a-chat-bot-using-Googles-Dialogflow/enable-api.webp" alt=""></p>
<p>After the API is enabled, the Dialogflow API can be used.</p>
<h2>Step 3</h2>
<p><a href="https://console.cloud.google.com/apis/credentials?project=nodal-crawler-347903">Download the .json file</a>. Select Project -> tap the "CREATE CREDENTIALS" button -> Choose Service Account -> Enter service account details:</p>
<p><img src="/assets/img/articles/2022-06-06-How-to-create-a-chat-bot-using-Googles-Dialogflow/create-credentials.webp" alt="">
<img src="/assets/img/articles/2022-06-06-How-to-create-a-chat-bot-using-Googles-Dialogflow/create-credentials2.webp" alt=""></p>
<p>After you have entered all details go to the "KEYS" tabs and create and download the .json file.
<img src="/assets/img/articles/2022-06-06-How-to-create-a-chat-bot-using-Googles-Dialogflow/create-service-account-key.webp" alt=""></p>
<h2>Step 4</h2>
<p>Make the Dialogflow CX chat bot using the CX visual builder.</p>
<h3>Step 4.1</h3>
<p>Create an agent by selecting Project > Select Agent > Create agent > type details.</p>
<p><img src="/assets/img/articles/2022-06-06-How-to-create-a-chat-bot-using-Googles-Dialogflow/create-agent.webp" alt=""></p>
<h3>Step 4.2: Empty board</h3>
<p>On an empty board we can start with the default start flow and add pages, flow, arguments, intents etc.</p>
<h3>Step 4.3: Board with already made example of coffee</h3>
<p>This is the Dialogflow CX visual builder with an already created example:
<img src="/assets/img/articles/2022-06-06-How-to-create-a-chat-bot-using-Googles-Dialogflow/dialog-flow-board.webp" alt="">
Here you can do anything.</p>
<h3>Step 4.4: Test the chat bot</h3>
<p>This is the Dialogflow CX visual builder with an already created example.</p>
<h4>Example 1 - Ask for avocado</h4>
<p><img src="/assets/img/articles/2022-06-06-How-to-create-a-chat-bot-using-Googles-Dialogflow/test-chat-bot.webp" alt=""></p>
<h4>Example 2 - Ask for eggs</h4>
<p><img src="/assets/img/articles/2022-06-06-How-to-create-a-chat-bot-using-Googles-Dialogflow/test-chat-bot-eggs.webp" alt=""></p>
<h4>Example 3 - Say no to finish the chat</h4>
<p><img src="/assets/img/articles/2022-06-06-How-to-create-a-chat-bot-using-Googles-Dialogflow/test-chat-bot-no.webp" alt=""></p>
<p>Here we can ask any type of questions to bot. The answer will be as defined using the CX visual builder.</p>
<h2>Write some code</h2>
<h3>Follow the APIs</h3>
<ul>
<li><a href="https://cloud.google.com/dialogflow/cx/docs/reference">Dialogflow CX APIs</a></li>
<li><a href="https://cloud.google.com/dialogflow/es/docs/reference">Dialogflow ES APIs</a>
<ul>
<li><a href="https://codelabs.developers.google.com/codelabs/dialogflow-flutter#0">Dialogflow Essentials(ES) Build Voice Bots for Android with Dialogflow Essentials &#x26; Flutter</a></li>
<li><a href="https://pub.dev/documentation/flutter_dialogflow_v2/latest/">Dialogflow Essentials(ES) - Dialogflow Flutter pub API</a></li>
</ul>
</li>
</ul>
<h2>Conclusion</h2>
<p>We learned about the basics of Dialogflow and its types (CX, ES). We summarized the complete guide for using Dialogflow.
Overall, we learned about making a quick chatbot (ml-bot) using the Dialogflow CX visual builder.</p>
<p>I asked some questions using the visual builder from my bot <code>Ask-Me-Any-Thing</code>.</p>
<p>Implementing a chatbot from scratch might take a bit of time and effort, so we might consider using the Google Dialogflow system for chatbots.</p>
<h2>Useful references</h2>
<ul>
<li><a href="https://cloud.google.com/dialogflow/">Dialogflow Documentation</a></li>
<li><a href="https://dialogflow.cloud.google.com/#/getStarted">Dialogflow ES complete guide</a></li>
<li><a href="https://codelabs.developers.google.com/codelabs/dialogflow-cx-retail-agent#0">Dialogflow CX complete guide</a></li>
<li><a href="https://console.cloud.google.com/apis/credentials?project=nodal-crawler-347903">Create service.json</a></li>
<li><a href="https://console.cloud.google.com/marketplace/product/google/dialogflow.googleapis.com?q=search&#x26;referrer=search&#x26;project=nodal-crawler-347903">Enable DialogFlow API page</a></li>
<li><a href="https://www.youtube.com/watch?v=Xfgn9iA1KMk&#x26;list=PLJLSPq0cTRmat9ec-c0hOJJhhNfObZXy3&#x26;index=1">Intro to Dialogflow CX / Youtube</a></li>
<li><a href="https://www.youtube.com/watch?v=O00K10xP5MU&#x26;list=PLIivdWyY5sqK5SM34zbkitWLOV-b3V40B&#x26;index=1">Deconstructing Chatbots / Youtube</a></li>
<li><a href="https://cloud.google.com/dialogflow/pricing">DialogFlow Pricing</a></li>
</ul>
<p><em>Article Photo by <a href="https://unsplash.com/photos/FPt10LXK0cg">Robin Worrall</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Is Virtual DOM Outdated?]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/05/26/Is-Virtual-DOM-Outdated</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/05/26/Is-Virtual-DOM-Outdated</guid>
            <pubDate>Thu, 26 May 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<!-- ## Intro -->
<p>Front-end engineers or anyone who tried to play with popular frontend libraries/frameworks like React and Vue.js, should all have heard about the Virtual DOM. It seems to have become a standard for improving performance in front-end development. However, there is also a trend that claims to not use the Virtual DOM. In this article, we will take a look at the Virtual DOM approach and discuss if Virtual DOM is outdated?</p>
<hr>
<p>![The Dom title image](/assets/img/articles/2022-05-26-Is-Virtual-DOM-Outdated/the-dom.webp</p>
<h2>The DOM</h2>
<p>First, let's talk about the DOM itself. According to <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction">MDN docs</a>,</p>
<blockquote>
<p>the DOM (Document Object Model) represents the web document as nodes and objects; that way, programming languages can interact with the page.</p>
</blockquote>
<p>Therefore, with DOM, we can easily work on the HTML nodes to create interactive components on a web page with Javascript and CSS.</p>
<h3>DOM Tree Example</h3>
<p>![DOM Tree Example](/assets/img/articles/2022-05-26-Is-Virtual-DOM-Outdated/dom-model.webp</p>
<p>This is a simple example that changed the content of an element:</p>
<pre><code class="hljs language-javascript"><span class="hljs-variable language_">document</span>.<span class="hljs-property">xxx</span>.<span class="hljs-property">xxx</span>.<span class="hljs-property">element</span>.<span class="hljs-property">innerHTML</span> = <span class="hljs-string">"Hello DOM!"</span>;
</code></pre>
<h3>Issues with DOM Operations</h3>
<p>If the DOM tree and the HTML content are simple enough, there is no problem with developers and end-users.</p>
<p>However, behind the visible elements on web pages, the DOM itself has much more complicated elements and attributes. There will be a lot of work that needs to be done by the browser, for creating just one simple HTML element.</p>
<p>Moreover, when changes happen on a large DOM node with many child nodes, it will reconstruct the whole node and re-render the whole contents to show the changes to end-users.</p>
<p>Obviously, these issues will cost a lot of unnecessary computing resources and affect performance, especially when the web page has more nodes and more interactive changes.</p>
<hr>
<p>![The Dom Revolutions title image](/assets/img/articles/2022-05-26-Is-Virtual-DOM-Outdated/the-dom-revolutions.webp</p>
<h2>The (Virtual) DOM Revolutions</h2>
<h3>The Concept</h3>
<p>As described above, since it costs so much for operating the DOM directly, what about to create a gateway that can be friendly to developers, at the same time it can help to reduce the unnecessary loads and efficiently reflect changes on DOM at pin-point level? To solve this, here comes our main character, the Virtual DOM.</p>
<p>The Virtual DOM is an light-weight abstract layer between the real DOM and the developers. It is actually a set of Javascript Objects with only the necessary elements and attributes to represent DOM tree and the nodes under it. With this abstract DOM tree, it becomes much easier and faster to create, edit or remove nodes just like the any normal Javascript Object.</p>
<p>Furthermore, with Virtual DOM, the rendering process is also abstract, so that it provides more compatibility for Cross-platform, not only for web browsers, but also mobile native components or other GUI developments.</p>
<h3>The Approach: diff &#x26; patch</h3>
<p>Many people got to know Virtual DOM via React, but in fact Virtual DOM is not invented by React. Many other libraries/frameworks are taking Virtual DOM approach. There are also many projects that only focus on Virtual DOM implementation such as <a href="https://github.com/Matt-Esch/virtual-dom">virtual-dom</a> and <a href="https://millionjs.org/">millionjs</a>.</p>
<p>Although the way they implement Virtual DOM differs, the main approach is quite similar. There are two essential steps: diff and patch.</p>
<p>![Virtual DOM Approach](/assets/img/articles/2022-05-26-Is-Virtual-DOM-Outdated/diff-patch.webp</p>
<p>As we discussed the DOM operation issues, the less unchanged nodes affected in the process, the more efficient the rendering can be. So the first important task is to specify the differences between before and after, this is normally called <strong>diff</strong> computation. After confirming the different contents, we will need another function to apply them to the real DOM, this is the <strong>patch</strong> operation.</p>
<p>In this process, the <strong>diff</strong> computation is the most core part. The basic idea is to traverse all the nodes to make comparison between the old Virtual DOM tree and the changed one.</p>
<p>Here is a sample code to see how diff implemented, by <a href="https://github.com/livoras/simple-virtual-dom">livoras</a>:</p>
<pre><code class="hljs language-javascript"><span class="hljs-comment">// diff compare two DOM tree</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">diff</span> (oldTree, newTree) {
  <span class="hljs-keyword">var</span> index = <span class="hljs-number">0</span> <span class="hljs-comment">// current node index</span>
  <span class="hljs-keyword">var</span> patches = {} <span class="hljs-comment">// record changes on each node</span>
  <span class="hljs-title function_">dfsWalk</span>(oldTree, newTree, index, patches)
  <span class="hljs-keyword">return</span> patches
}

<span class="hljs-comment">// depth-first walk through two DOM tree</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">dfsWalk</span> (oldNode, newNode, index, patches) {
  <span class="hljs-comment">// compare the changes and record by index</span>
  patches[index] = [...]
  <span class="hljs-title function_">diffChildren</span>(oldNode.<span class="hljs-property">children</span>, newNode.<span class="hljs-property">children</span>, index, patches)
}

<span class="hljs-comment">// compare child nodes</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">diffChildren</span> (oldChildren, newChildren, index, patches) {
  <span class="hljs-keyword">var</span> leftNode = <span class="hljs-literal">null</span>
  <span class="hljs-keyword">var</span> currentNodeIndex = index
  oldChildren.<span class="hljs-title function_">forEach</span>(<span class="hljs-keyword">function</span> (<span class="hljs-params">child, i</span>) {
    <span class="hljs-keyword">var</span> newChild = newChildren[i]
    currentNodeIndex = (leftNode &#x26;&#x26; leftNode.<span class="hljs-property">count</span>) <span class="hljs-comment">// calculate node index</span>
      ? currentNodeIndex + leftNode.<span class="hljs-property">count</span> + <span class="hljs-number">1</span>
      : currentNodeIndex + <span class="hljs-number">1</span>
    <span class="hljs-title function_">dfsWalk</span>(child, newChild, currentNodeIndex, patches) <span class="hljs-comment">// traverse all child nodes</span>
    leftNode = child
  })
}
</code></pre>
<p>Each Virtual DOM implementation may have its own algorithm to optimize the diff performance. Thanks to the efforts they have made, Virtual DOM became more and more mature, we don't even need to think about how it works during daily development.</p>
<p>Nevertheless, Virtual DOM is not the only solution for all.</p>
<hr>
<p>![The DOM Resurrections title image](/assets/img/articles/2022-05-26-Is-Virtual-DOM-Outdated/the-dom-resurrections.webp</p>
<h2>The DOM Resurrections</h2>
<h3>New Stars Arise</h3>
<p>Since Virtual DOM has got such advanced that almost became the industry standard, how can someone say it's outdated? Let's look at an interesting fact from a web survey about Frontend frameworks/libraries by <a href="https://2021.stateofjs.com/en-US/libraries/front-end-frameworks/">stateofjs</a>.</p>
<p>![Front-end Frameworks Rank](/assets/img/articles/2022-05-26-Is-Virtual-DOM-Outdated/ranking.webp</p>
<p>According to the survey result, in the past 5 years, React, Angular and Vue.js are keeping the top 3 positions as the most used front-end frameworks. We can understand this without any doubt. However, when we turn to the satisfaction or interest aspect in 2020 and 2021, two unfamiliar names came among the best, they are <a href="https://www.solidjs.com/">Solid</a> and <a href="https://svelte.dev/">Svelte</a>. And both of them are claiming the feature of fast and high performance <strong>with NO Virtual DOM</strong>.</p>
<h3>Back to the DOM</h3>
<p>Thinking about why Solid or Svelte can provide high performance by just using the real DOM, we can roughly summarize it into two reasons:</p>
<h4>1. The real DOM is not "That Bad"</h4>
<p>We know that the real DOM may cost unnecessary computing resources especially when complicated changes happen. But as the computers and browsers are advancing, the real DOM operation may not make obviously a difference in the view of end-users. Jason Knight did a test about the <a href="https://levelup.gitconnected.com/the-live-dom-is-not-slow-bad-or-wrong-web-developers-are-2bf86c3b9e2e">real DOM performance</a>, although the test was done among the real DOM, Doc Fragment and Shadow DOM, we can still get a clue. If developers can handle the real DOM operation carefully, it can be efficient too.</p>
<p>* <em>Doc Fragment and Shadow DOM</em> are different things from Virtual DOM, we will not get into the details in this article.</p>
<h4>2. Virtual DOM is also costly</h4>
<p>Virtual DOM is efficient but not free of unnecessary computing resources cost. Like the diff algorithm, when it traverses and compares all the nodes only for a tiny attribute change, that's definitely not a smart way. In addition, writing components in a specific format to create a Virtual DOM and then render it during run time, actually is generating extra work for the browser.</p>
<p>As Rich Harris, the creator of Svelte wrote:</p>
<blockquote>
<p>the virtual DOM is usually fast enough, but with certain caveats. ... Svelte is a compiler that knows at build time how things could change in your app, rather than waiting to do the work at run time.</p>
</blockquote>
<h3>Keep it simple</h3>
<p>In that case, how can we know is Virtual DOM suitable for us or not? In fact, no need to think too complicated，we can keep it simple.</p>
<p>All the different approaches are made for better development environment, the Virtual DOM is just one of the choices. Like Ryan Carniato said in his article, <em>all the approaches have its own tradeoffs, rather than the performance or size, we should consider the <strong>developer experience</strong> instead</em>.</p>
<p>Thus, the most important things is, which way do you and your team like the most.</p>
<hr>
<h2>Conclusion</h2>
<p>Is Virtual DOM outdated? <strong>Of course NO!</strong></p>
<p>The Virtual DOM has been optimized well enough in most scenarios. Still, it's just one of the approaches we can choose. It's never a true or false question about the Virtual DOM.</p>
<p>It's important to follow best practices that have more usage and community support to ensure projects run well. But in such a fast-changing front-end world, why not keep trying out some new things, who knows, maybe you will make it the next best practice.</p>
<hr>
<h2>Further Reading</h2>
<ul>
<li><em><a href="https://svelte.dev/blog/virtual-dom-is-pure-overhead">Virtual DOM is pure overhead</a> by Rich Harris</em></li>
<li><em><a href="https://javascript.plainenglish.io/reports-of-the-virtual-doms-death-are-greatly-exaggerated-2206d00beead">Reports of the Virtual DOM’s Death are Greatly Exaggerated</a> by Ryan Carniato</em></li>
</ul>
<h2>References</h2>
<ul>
<li><em>Header Photo by <a href="https://unsplash.com/photos/aiqKc07b5PA?utm_source=unsplash&#x26;utm_medium=referral&#x26;utm_content=creditShareLink">Federica Galli</a></em></li>
<li><em>DOM Tree image on <a href="https://en.wikipedia.org/wiki/Document_Object_Model">Wikipedia</a></em></li>
<li><em>Diff and Patch image by <a href="https://blog.bitsrc.io/incremental-vs-virtual-dom-eb7157e43dca">Chameera Dulanga</a></em></li>
<li><em>Front-end frameworks and libraries Rankings by <a href="https://2021.stateofjs.com/en-US/libraries/front-end-frameworks/">stateofjs</a></em></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Solution Design for Free Range Developers]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/04/28/Solution-Design-For-Freerange-Developers</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/04/28/Solution-Design-For-Freerange-Developers</guid>
            <pubDate>Thu, 28 Apr 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>At Monstarlab, the role of Solution Architect (SA) provides one career path for developers to progress towards, should they wish to pursue a less hands-on role over time. Many of the most common problems faced by a rookie SA are around the communication of their solution designs. It can be tempting - as an ex-developer - to involve yourself in every detail of the system you’re working with. Tasks often end up defined so meticulously that every decision is already made by the time they’re picked up by the devs. As ex-coders, many SAs create documentation as if the intended audience is a machine. Diagrams are overly detailed, formal and unintuitive. Written instruction is almost pseudo-code.</p>
<p>There’s a danger that devs working with a fastidious SA become deskilled - turned into automatons, churning out code with no understanding of context, according to a detailed spec that allows no room for interpretation or creativity. And spoon-feeding developers with every detail of the designs we want them to implement is not the best way to help them make the transition from coder to SA, should they choose this path. The SA also becomes a single point that every decision has to pass through.</p>
<p>Here I share five tips to help you ensure your solution design communications are optimised for free range, free-thinking, motivated developers who will be aligned with your technical vision.</p>
<h2>1. Involve developers in the key technical decisions</h2>
<p>Nothing frustrates a developer more than being presented with a technical decision which they disagree with, at a time when it’s too late to change it. When I am doing technical discovery on a feature set, I make a point during our daily stand up of providing a short summary of the key technical decisions I’m working towards. This gives the developers a chance to express a preference, make suggestions, or provide valuable insight as someone closer to the codebase. It is a good way of ensuring that developers are part of the decision-making process, and reduces the chance of pushback when you present your technical solution further down the line.</p>
<h2>2. Get involved in user story creation</h2>
<p>Who writes the user stories for a project will vary from organisation to organisation and will depend on the roles defined. In many organisations, it’ll be the product owner who takes the lead, with detail or tasks added by others in the team such as the scrum master (or delivery lead at Monstarlab), QA engineers, SAs or the developers themselves.</p>
<p>The SA will generally provide the initial detail for the backend elements of user stories, as these may involve third-party integrations or entail interaction with the data layer. Contributing to user stories is an opportunity for you to retain visibility over the solution you’ve designed as it is implemented. What you include will be critical to the relationship between you and your developers. Try to bring a consistent approach to a user story’s structure. Learn over time what works and what doesn’t and discuss this at sprint reviews.</p>
<p>And remember to leave the coding to the devs. The level of detail provided in user stories should strike a careful balance between enough guidance to ensure your broad solution design is met, and so much instruction that you leave no room for the kind of creative problem solving that inspired your developers to get into coding in the first place.</p>
<h2>3. Give developers context</h2>
<p>It’s perfectly possible to spec a task in a way that a developer is able to implement it without having any understanding of the wider context. However, making the effort to provide an understanding of how a feature fits into the overall solution will be empowering and motivating for the development team. Your devs will be better able to suggest efficiencies if they can see the bigger picture.</p>
<p>One of the key elements in my solution designs is the sequence diagram, which is a great way to provide context for a user story. Avoid going into too much detail on these. Ask yourself, “What is the purpose of my drawing?” before you start. Does your team really need a diagram showing every call made between a solution’s classes before they can start coding?</p>
<p>I tend to highlight only the main calls between a solution’s front end application and the middleware, and between the middleware and any third parties. This can provide a useful discussion tool during collaboration sessions with your developers or client and third party stakeholders, as you go through the process of figuring out a key data flow or integration. I use a less formal version of the sequence diagram which is more readable without learning the official UML notation.
<kbd>
![Example sequence diagram](/assets/img/articles/2022-04-28-Solution-Design-For-Freerange-Developers/sequence-diagram.webp
</kbd></p>
<h2>4. Explain the reasons behind your decisions</h2>
<p>Hopefully, your developers will have been involved in the key technical decisions (see point 1) but it’s still possible the developer picking up a user story may not understand the broader context of every element of the task in question. The reasons behind some of a story’s details or acceptance criteria may not be obvious. A short paragraph at the top of a user story’s description goes a long way to giving developers the “why?” and not just the “what?”.</p>
<p>![Example user story](/assets/img/articles/2022-04-28-Solution-Design-For-Freerange-Developers/user-story.webp</p>
<h2>5. Be collaborative with estimation</h2>
<p>Solution design should also not be seen as a top-down exercise, where the SA dictates to the team what’s to be done and how long it should take. Involve your developers early in the estimation process. At Monstarlab, this can be as early as the pre-sale stage where we try to give t-shirt size estimations on features. Involve your development team in the process. Decide roughly how much time a small, medium and large corresponds to and then get the team to estimate each feature.</p>
<p>![T-shirt sizing key](/assets/img/articles/2022-04-28-Solution-Design-For-Freerange-Developers/t-shirt-sizing.webp</p>
<p>Once in production, involve the developers in the process of creating the more detailed estimations for user stories. Our estimation sessions involve a two-stage process, ahead of sprint planning. First, the delivery lead and SA present each of the next batch of user stories to the developers and QA engineers. This provides an opportunity for the team to ask questions, interrogate the stories, suggest improvements or highlight considerations that haven’t been thought of. We edit the stories as necessary during the session and only once everyone is happy does the team play Planning Poker - <a href="http://www.planningpokeronline.com">www.planningpokeronline.com</a> - to estimate their story points.</p>
<h2>Conclusion</h2>
<p>As a new solution architect, it can be hard to relinquish control over every detail of the systems we design. But the more we can, the more we help developers we work with stay motivated and engaged. We also help ourselves by avoiding becoming a decision-making bottleneck for the team. And don’t feel like you need to be the one with all the answers. If you involve the devs in your process, they’ll be more on board with your design when they come to implement it.</p>
<p><em>Article Photo by <a href="https://www.istockphoto.com/vector/set-of-vector-office-elements-with-people-isometric-coworking-or-open-space-gm1205496033-347286760">iStock by Getty Images</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Connecting specialized doctors to refugee camps with the help of artificial intelligence]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/04/15/Connecting-specialized-doctors-to-refugee-camps-with-the-help-of-artificial-intelligence</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/04/15/Connecting-specialized-doctors-to-refugee-camps-with-the-help-of-artificial-intelligence</guid>
            <pubDate>Fri, 15 Apr 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>This year's <a href="https://www.linkedin.com/showcase/monstarhacks">MonstarHacks</a> main theme was Life Science and three selected goals were:</p>
<ol>
<li>Assure equitable health care for everybody</li>
<li>Keep the “human touch” in digital healthcare solutions in isolating times</li>
<li>Connect refugees with medical support in times of crisis (with a focus on low energy consuming solutions)</li>
</ol>
<p>Our team <code>PartTimeHackers</code> decided to tackle the problem of "Connect refugees with medical support in times of crisis" by building a mobile application that will connect health care professionals with people seeking medical care. We think the challenge is important because the problem is so general. Everyone has this problem. Everyone needs to have some diagnosis. Of course not once or twice a year, but frequently, even daily if possible. And if we could solve it then the life of doctors, rural people, and refugees will be a lot easier.</p>
<h2>Problem statement</h2>
<p>We believe there are good people on earth and we want to help those people who want to do good for others. For example if a doctor wants to voluntarily help any person in a refugee camp with medical consultancy, he/she simply can’t or he/she has to overcome a lot of issues. We identified some of those issues as follows:</p>
<ol>
<li>Where to meet</li>
<li>When to meet</li>
<li>Who to meet</li>
</ol>
<p>For most of the issues the main cause is, there is no organized way for a doctor to connect to a person who needs help.</p>
<h2>The proposed solution</h2>
<p>To solve this organizational problem our team came up with the idea of MonstarCare. <code>MonstarCare</code> is an app that connects a person in a refugee camp who needs medical help to a specialized doctor.</p>
<h2>How does it work</h2>
<p>A doctor uses our app to notify others at which place and time he/she will be available for volunteering with medical consultancy. And also in which medical sector he is specialized in. Any person from a refugee camp makes a request from our app letting us know what kind of problems they have. After that our machine learning algorithm matches a doctor to the refugee who needs help. Then those people who are matched to a doctor are notified by SMS at what time and place the doctor will visit their refugee camp zone.</p>
<h2>Our Tech stack</h2>
<ul>
<li><a href="https://reactnative.dev/">React native</a> (App) - Allowed us to prototype our app faster than any other solution. None of our team members was familiar with app development. But react native was very easy to learn.</li>
<li><a href="https://go.dev/">Go</a> (Backend) -  If we want to scale our solution globally Go helps in that area.</li>
<li><a href="https://www.python.org/">Python</a> (Machine Learning Model) -  Industry standard and huge community support.</li>
<li><a href="https://www.figma.com/">Figma</a> - Intuitive tool for mocking and designing app flow.</li>
</ul>
<h3>Links to app demo</h3>
<ul>
<li>🎨 Design: <a href="https://www.figma.com/file/OGlbDeyIYyDZAnm2fUJASe/hacks2022?node-id=0%3A1">Figma file </a></li>
<li>💻 Demo Implementation: <a href="https://expo.dev/@shafin_ashraf/mht?serviceType=classic&#x26;distribution=expo-go">Published expo (React Native)</a></li>
</ul>
<p><em>Article Photo by <a href="https://www.samaritanspurse.ca/article/bc-nurse-offering-helping-hands-to-displaced-in-bangladesh/">samaritanspurse.ca</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Information System for Storing and Analysing Qualitative Studies of Refugees]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/04/14/Information-System-for-Storing-and-Analysing-Qualitative-Studies-of-Refugees</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/04/14/Information-System-for-Storing-and-Analysing-Qualitative-Studies-of-Refugees</guid>
            <pubDate>Thu, 14 Apr 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Refugee crises continue to be a major concern around the world. As of today, the number of refugees and internally displaced people worldwide has grown to almost 90 million. Due to multiple barriers like culture, language, etc., ensuring adequate access to health care and connecting the community to these huge populations remains a major challenge.</p>
<h2>Problem Statement</h2>
<p>Millions of people around the world are forced to be dislocated due to war and other humanitarian crises, thus becoming refugees. Due to many different obstacles, such as language or cultural barriers, refugees are not only burdened with the disease but also compromised access to healthcare. However, knowledge of the living experience of this demographic is very limited. In addition, the strategies that people may develop to access health care, which is very essential to peer support and the establishment of community-based programs, have not been studied in detail.</p>
<p><img src="/assets/img/articles/2022-04-14-Information-System-for-Storing-and-Analysing-Qualitative-Studies-of-Refugees/image_1.webp" alt=""></p>
<p><em>Image Source: <a href="https://reporting.unhcr.org/globalappeal2022?page=6#_ga=2.220820248.1411705371.1648888640-227061203.1648888640">UN Refugee Agency</a></em></p>
<h2>Proposed Solution</h2>
<p>A greater understanding of the different refugee statuses, such as gender, literacy, ethnicity, culture, etc can guide healthcare workers and policymakers in improving services for these refugees. Furthermore, it is important to enable seldom-heard, hard-to-reach populations and facilitate their research participants to understand how vectors of disadvantage intersect.</p>
<p>Here is MonstarHack 2022, what we are proposing is a means to store semi-structured data of challenges/experiences refugees face and how healthcare professionals around the world developed different strategies to overcome those challenges. These data will be available to explore the experiences of refugees, healthcare professionals, and administrators in the healthcare system.</p>
<h2>Case Study</h2>
<p>This is a thematic analysis^1 of semi-structured interviews with refugee participants who arrived in Germany between 2013 and 2018.</p>
<p><img src="/assets/img/articles/2022-04-14-Information-System-for-Storing-and-Analysing-Qualitative-Studies-of-Refugees/image_2.webp" alt=""></p>
<p><em>Image Source: <a href="https://bmcpublichealth.biomedcentral.com/articles/10.1186/s12889-021-10817-6">A qualitative study on resilience in adult refugees in Germany</a></em></p>
<blockquote>
<p>The above image shows the nine themes we identified, specific points within each, as well as the two broad categories into which we organized themes based on their function within resilience. The themes of Cognitive coping strategies, Behavioral coping strategies, Self-ascribed resilience as an enduring capacity, and Volunteering, activism, and work for refugee causes all capture ways in which participants manifest resilience as the “process of, capacity for, or outcome of successful adaptation despite challenging or threatening circumstances”. The themes of Social support, Experiencing migration as an opportunity for self-expression, belonging, and personal development, Experiencing migration as an opportunity for women, Being a parent, and Being young all cover factors that appear to facilitate successful adaptation. Within overarching categories, themes are in no particular order except that Cognitive and Behavioral coping strategies and Social support, the first themes in the two categories, were the most globally represented among our participants. The sections below include interview quotations with the individuals quoted represented as participants and a participant number.</p>
</blockquote>
<h2>Conclusion</h2>
<p>There has been a lot of effort researching the crises of refugees and how different strategies were developed over time to overcome those crises but yet there are no means to record these experiences.</p>
<p>Information from qualitative research organized into themes and subthemes can be stored and retrieved by storing it in a database. The themes and subthemes are based on an interview guide and the succeeding development of theory and new themes. A database's thorough sorting algorithms allow a researcher to keep track of his data while analyzing and reporting on it.</p>
<p>The comprehensive analysis of these data will help healthcare workers and policymakers working with refugees to take better measures by making them aware of potential strategies and sources of strength specific to this client group. This will also contribute to moving the academic literature as well as will ensure equity in health care for refugees by potentially improving clinical practice and integration processes.</p>
<h4>Reference:</h4>
<p>Walther, L., Amann, J., Flick, U. et al. A qualitative study on resilience in adult refugees in Germany. BMC Public Health 21, 828 (2021). <a href="https://doi.org/10.1186/s12889-021-10817-6">https://doi.org/10.1186/s12889-021-10817-6</a></p>
<p><em>Article Photo by <a href="https://www.amnesty.org/en/latest/news/2018/11/why-rohingya-refugees-shouldnt-be-sent-back-to-myanmar/">Amnesty International</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ARAnchors - Bringing virtual objects into the real world]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/04/07/ARAnchors-Bringing-Virtual-Objects-into-the-real-world</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/04/07/ARAnchors-Bringing-Virtual-Objects-into-the-real-world</guid>
            <pubDate>Thu, 07 Apr 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>At Monstarlab we have some incredible talent when it comes to building AR experiences. Some of the work I have seen my colleagues produce has simply blown me away. Really creative, innovative experiences that can often at times blur the line between augmented reality and reality so much so that it’s hard to know what’s real!</p>
<p>For me personally, I had always enjoyed using AR but had never really got my hands dirty with it. When I got the opportunity to do so, I jumped at the chance. I wanted to share my experience with working with ARKit for the first time and what I managed to create. I first completed some simple online tutorials and watched some videos from Apple to get a grasp of the basics. A really cool feature that really appealed to me was location anchoring. The possibilities of what you could do by combining real world locations using latitude, longitude &#x26; altitude, with AR experiences seems so exciting. For a movie nerd like me, the possibility of seeing AR objects interacting with the real world seemed like something only The Terminator could do up until recently!</p>
<p>My end goal was to create an AR experience that could locate &#x26; display all of the London TFL tube and train stations for you, what train lines were available and even the current service they were providing. Here goes…</p>
<h2>Step One - Adding an ARAnchor to my ARView</h2>
<p>I couldn’t believe how easy it was to add an ARAnchor to an ARKit ARView. I think a lot of people, myself included, see AR and presume its going to be really difficult and complicated but ARKit really does all the heavy lifting for you. My first goal I set myself was to add an AR label to our UK office which simply labelled our office in the sky.</p>
<p>With the latitude &#x26; longitude of the office, you can simply create an ARGeoAnchor like so...</p>
<pre><code class="hljs language-less">    <span class="hljs-selector-tag">let</span> <span class="hljs-selector-tag">location</span> = <span class="hljs-selector-tag">CLLocationCoordinate2D</span>(<span class="hljs-attribute">latitude</span>: <span class="hljs-string">"51.5113000646694"</span>, <span class="hljs-attribute">longitude</span>: <span class="hljs-string">"51.5113000646694"</span>)
    <span class="hljs-selector-tag">let</span> <span class="hljs-selector-tag">anchor</span> = <span class="hljs-selector-tag">ARGeoAnchor</span>(<span class="hljs-attribute">name</span>: name, <span class="hljs-attribute">coordinate</span>: location)
    <span class="hljs-selector-tag">arView</span><span class="hljs-selector-class">.session</span><span class="hljs-selector-class">.add</span>(<span class="hljs-attribute">anchor</span>: geoAnchor)
</code></pre>
<p>You have the option of also supplying altitude if you know how high you want your anchor to be, but I don't want to specify that for now.</p>
<p>This will in turn call a delegate method you should adhere to. You can then specify your AR Entity you wish to display at the location.</p>
<pre><code class="hljs language-swift">    <span class="hljs-keyword">func</span> <span class="hljs-title function_">session</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">session</span>: <span class="hljs-type">ARSession</span>, <span class="hljs-params">didAdd</span> <span class="hljs-params">anchors</span>: [<span class="hljs-type">ARAnchor</span>]) {
        <span class="hljs-keyword">for</span> geoAnchor <span class="hljs-keyword">in</span> anchors.compactMap({ <span class="hljs-variable">$0</span> <span class="hljs-keyword">as?</span> <span class="hljs-type">ARGeoAnchor</span> }) {
            <span class="hljs-keyword">let</span> entity <span class="hljs-operator">=</span> <span class="hljs-type">Entity</span>.placemarkEntity(for: geoAnchor)
            <span class="hljs-keyword">self</span>.arView.scene.addAnchor(entity)
        }
    }
</code></pre>
<p>You may notice here I have a method <code>placemarkEntity(:_)</code> that generates my entity for me. Lets have a quick look at what this is doing...</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">extension</span> <span class="hljs-title class_">Entity</span> {
    
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">placemarkEntity</span>(<span class="hljs-params">for</span> <span class="hljs-params">arAnchor</span>: <span class="hljs-type">ARAnchor</span>) -> <span class="hljs-type">AnchorEntity</span> {
        
        <span class="hljs-keyword">let</span> placemarkAnchor <span class="hljs-operator">=</span> <span class="hljs-type">AnchorEntity</span>(anchor: arAnchor)
        <span class="hljs-keyword">let</span> indicator <span class="hljs-operator">=</span> generateNameIndicator(text: arAnchor.name <span class="hljs-operator">??</span> <span class="hljs-string">"Untitled"</span>)
        placemarkAnchor.addChild(indicator)
        
        <span class="hljs-comment">//30 meters from the ground</span>
        indicator.setPosition(<span class="hljs-type">SIMD3</span>&#x3C;<span class="hljs-type">Float</span>>(<span class="hljs-number">0</span>, <span class="hljs-number">30</span>, <span class="hljs-number">0</span>),
                              relativeTo: placemarkAnchor)
        
        <span class="hljs-comment">//rotate to face where I want</span>
        <span class="hljs-keyword">let</span> radians <span class="hljs-operator">=</span> <span class="hljs-number">90.0</span> <span class="hljs-operator">*</span> <span class="hljs-type">Float</span>.pi <span class="hljs-operator">/</span> <span class="hljs-number">2</span>
        <span class="hljs-keyword">let</span> orientation <span class="hljs-operator">=</span> simd_quatf.<span class="hljs-keyword">init</span>(angle: radians, axis: <span class="hljs-type">SIMD3</span>&#x3C;<span class="hljs-type">Float</span>>(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>))
        indicator.setOrientation(orientation, relativeTo: placemarkAnchor)
        
        <span class="hljs-keyword">return</span> placemarkAnchor
    }
    
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">generateNameIndicator</span>(<span class="hljs-params">text</span>: <span class="hljs-type">String</span>) -> <span class="hljs-type">Entity</span> {
        <span class="hljs-keyword">let</span> indicatorEntity <span class="hljs-operator">=</span> <span class="hljs-type">Entity</span>()

        <span class="hljs-keyword">let</span> text <span class="hljs-operator">=</span> <span class="hljs-type">ModelEntity</span>.nameEntity(text.uppercased()).clone(recursive: <span class="hljs-literal">true</span>)
        indicatorEntity.addChild(text)
        
        <span class="hljs-keyword">return</span> indicatorEntity
    }
}

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">ModelEntity</span> {
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">nameEntity</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">text</span>: <span class="hljs-type">String</span>) -> <span class="hljs-type">ModelEntity</span> {
        <span class="hljs-type">ModelEntity</span>(mesh: <span class="hljs-type">MeshResource</span>.generateText(text,
                                                    extrusionDepth: <span class="hljs-number">0.2</span>,
                                                    font: .boldSystemFont(ofSize: <span class="hljs-number">4</span>),
                                                    containerFrame: <span class="hljs-type">CGRect</span>.zero,
                                                    alignment: .center,
                                                    lineBreakMode: .byCharWrapping),
                    materials: [<span class="hljs-type">UnlitMaterial</span>(color: .yellow)])
    }
}
</code></pre>
<p>So there's quite a lot going on here but I'll give it a quick run down;
Our first function is creating the AnchorEntity we display in our experience. It uses a function to generate the AR text entity, it then positions the entity 30 meters above the ground, that's around the height of our UK office. It then rotates the entity to face the front of the building.
The second function is what is called to generate the text entity, this uses the ModelEntity extension you can see which uses RealityKits' MeshResource static function to generate the 3D text with fonts, colours and other styling we desire.</p>
<p>When this is run in the app it displays something like this which is pretty neat!
<img src="/assets/img/articles/2022-04-07-ARAnchors-Bringing-Virtual-Objects-into-the-real-world/officeLabelImage.webp" alt="Office Label image"></p>
<h2>Step Two - Adding an ARAnchor for each Station</h2>
<p>Now I know how to add an ARAnchor for one location, adding them for all the others was simple. I went on to find some data online that supplied me all the stations throughout London and their latitude &#x26; longitude. An issue with this was, London is a rather large city, and it has a lot of train stations! Adding all of these entities to my ARView would cause performance issues and frankly just didn't make much sense for what I wanted as most wouldn't be local enough anyway. I proceeded to use CoreLocation to get the users current location and only generate ARAnchors if the station was within 1000 meters.</p>
<p>Using my previous code for labelling the office, combined with the data of the station locations, I could display all the station labels. One issue with my current implementation was currently all the 3D text entities were rotated facing the same way as our UK office which of course we didn't want. What I really wanted is for the labels to be rotated so the user could read them wherever they are.</p>
<p>Below, this piece of code does this using Combine subscribing to the ARView scene and transforming all the entities to the ARView camera.</p>
<pre><code class="hljs language-php">        <span class="hljs-comment">//rotate stations to look at camera</span>
        arView.scene.<span class="hljs-title function_ invoke__">subscribe</span>(<span class="hljs-attr">to</span>: SceneEvents.Update.<span class="hljs-built_in">self</span>) { [<span class="hljs-built_in">self</span>] _ in
            <span class="hljs-keyword">for</span> anchor in <span class="hljs-built_in">self</span>.arView.scene.anchors {
                <span class="hljs-keyword">if</span> let entity = anchor <span class="hljs-keyword">as</span>? AnchorEntity {
                    entity.<span class="hljs-title function_ invoke__">billboard</span>(<span class="hljs-attr">targetPosition</span>: arView.cameraTransform.translation)
                }
            }
        }.<span class="hljs-title function_ invoke__">store</span>(<span class="hljs-attr">in</span>: &#x26;cancellables)
</code></pre>
<pre><code class="hljs language-swift">        <span class="hljs-keyword">extension</span> <span class="hljs-title class_">Entity</span> {
            <span class="hljs-comment">/// Billboards the entity to the targetPosition which should be provided in world space.</span>
            <span class="hljs-keyword">func</span> <span class="hljs-title function_">billboard</span>(<span class="hljs-params">targetPosition</span>: <span class="hljs-type">SIMD3</span>&#x3C;<span class="hljs-type">Float</span>>) {
                look(at: targetPosition, from: position(relativeTo: <span class="hljs-literal">nil</span>), relativeTo: <span class="hljs-literal">nil</span>)
            }
        }
</code></pre>
<p>The end result meant all tube stations were now displayed with their name above them!
<img src="/assets/img/articles/2022-04-07-ARAnchors-Bringing-Virtual-Objects-into-the-real-world/eustonSquare.webp" alt="Euston Square image"></p>
<h2>Step Three - Adding custom Reality Composer Scenes &#x26; Service Info</h2>
<p>Now I had the station names, I really wanted it to look a bit more impressive. I decided to dive in to Reality Composer. It was so fun &#x26; easy to use with so many shapes and objects you can combine to create nearly anything you want. When designing your AR objects it's important to keep in mind sizing, these objects are going into the real world in our app, so you want to think about how large you want them to be compared to real life objects. After getting used to Reality Composer quickly, I soon felt like I was making Pixar's next big feature, making all the different train type logos that I would go on to use in our app.
<img src="/assets/img/articles/2022-04-07-ARAnchors-Bringing-Virtual-Objects-into-the-real-world/rcExample.webp" alt="Reality Composer Example image"></p>
<p>With my Reality Composer scenes finished, I used TFL's API to get all the train lines and their current service. I then updated my entity generation code slightly to include my Reality Composer scenes and laid the lines and their services out using dynamic height depending on the amount of information I needed to display including train lines, line statuses and status icons.</p>
<pre><code class="hljs language-swift">    <span class="hljs-keyword">static</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">placemarkEntity</span>(<span class="hljs-params">for</span> <span class="hljs-params">arAnchor</span>: <span class="hljs-type">ARAnchor</span>,
                                <span class="hljs-params">tubeLines</span>: [<span class="hljs-type">PlacemarkLineInfo</span>]) -> <span class="hljs-type">AnchorEntity</span> {
        
        <span class="hljs-keyword">let</span> meterSpacing:<span class="hljs-type">Float</span> <span class="hljs-operator">=</span> <span class="hljs-number">7</span>
        <span class="hljs-keyword">let</span> placemarkAnchor <span class="hljs-operator">=</span> <span class="hljs-type">AnchorEntity</span>(anchor: arAnchor)
        <span class="hljs-keyword">let</span> topHeightFromGround: <span class="hljs-type">Float</span> <span class="hljs-operator">=</span> <span class="hljs-number">15</span> <span class="hljs-operator">+</span> (meterSpacing <span class="hljs-operator">*</span> <span class="hljs-type">Float</span>(tubeLines.count))
        <span class="hljs-keyword">var</span> currentHeight <span class="hljs-operator">=</span> topHeightFromGround

        <span class="hljs-keyword">let</span> indicator <span class="hljs-operator">=</span> generateNameIndicator(text: arAnchor.name <span class="hljs-operator">??</span> <span class="hljs-string">"Untitled"</span>)
        placemarkAnchor.addChild(indicator)
        indicator.setPosition(<span class="hljs-type">SIMD3</span>&#x3C;<span class="hljs-type">Float</span>>(<span class="hljs-number">0</span>, currentHeight, <span class="hljs-number">0</span>),
                              relativeTo: placemarkAnchor)
        currentHeight <span class="hljs-operator">-=</span> meterSpacing
        
        <span class="hljs-comment">//rotate</span>
        <span class="hljs-keyword">let</span> radians <span class="hljs-operator">=</span> <span class="hljs-number">90.0</span> <span class="hljs-operator">*</span> <span class="hljs-type">Float</span>.pi <span class="hljs-operator">/</span> <span class="hljs-number">2</span>
        <span class="hljs-keyword">let</span> orientation <span class="hljs-operator">=</span> simd_quatf.<span class="hljs-keyword">init</span>(angle: radians, axis: <span class="hljs-type">SIMD3</span>&#x3C;<span class="hljs-type">Float</span>>(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>))
        indicator.setOrientation(orientation, relativeTo: placemarkAnchor)
        

        <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> tubeLines {
            <span class="hljs-keyword">switch</span> line.tubeLine.trainType {
            <span class="hljs-keyword">case</span> .tube:
                <span class="hljs-keyword">let</span> logoScene <span class="hljs-operator">=</span> <span class="hljs-keyword">try!</span> <span class="hljs-type">UndergroundLogo</span>.loadScene()
                placemarkAnchor.addChild(logoScene)
                logoScene.setPosition(<span class="hljs-type">SIMD3</span>&#x3C;<span class="hljs-type">Float</span>>(<span class="hljs-number">0</span>, currentHeight <span class="hljs-operator">+</span> <span class="hljs-number">1</span>, <span class="hljs-number">0</span>),
                                      relativeTo: placemarkAnchor)
            <span class="hljs-keyword">case</span> .overground:
                <span class="hljs-keyword">let</span> logoScene <span class="hljs-operator">=</span> <span class="hljs-keyword">try!</span> <span class="hljs-type">OvergroundLogo</span>.loadScene()
                placemarkAnchor.addChild(logoScene)
                logoScene.setPosition(<span class="hljs-type">SIMD3</span>&#x3C;<span class="hljs-type">Float</span>>(<span class="hljs-number">0</span>, currentHeight <span class="hljs-operator">+</span> <span class="hljs-number">1</span>, <span class="hljs-number">0</span>),
                                      relativeTo: placemarkAnchor)
            <span class="hljs-keyword">case</span> .nationalRail:
                <span class="hljs-keyword">let</span> logoScene <span class="hljs-operator">=</span> <span class="hljs-keyword">try!</span> <span class="hljs-type">NationalRailLogo</span>.loadScene()
                placemarkAnchor.addChild(logoScene)
                logoScene.setPosition(<span class="hljs-type">SIMD3</span>&#x3C;<span class="hljs-type">Float</span>>(<span class="hljs-number">0</span>, currentHeight <span class="hljs-operator">+</span> <span class="hljs-number">1</span>, <span class="hljs-number">0</span>),
                                      relativeTo: placemarkAnchor)
            <span class="hljs-keyword">case</span> .dlr:
                <span class="hljs-keyword">let</span> logoScene <span class="hljs-operator">=</span> <span class="hljs-keyword">try!</span> <span class="hljs-type">DLRLogo</span>.loadScene()
                placemarkAnchor.addChild(logoScene)
                logoScene.setPosition(<span class="hljs-type">SIMD3</span>&#x3C;<span class="hljs-type">Float</span>>(<span class="hljs-number">0</span>, currentHeight <span class="hljs-operator">+</span> <span class="hljs-number">1</span>, <span class="hljs-number">0</span>),
                                      relativeTo: placemarkAnchor)
            }
            <span class="hljs-keyword">let</span> lineIndicator <span class="hljs-operator">=</span> generateLineIndicator(line: line.tubeLine)
            placemarkAnchor.addChild(lineIndicator)
            lineIndicator.setPosition(<span class="hljs-type">SIMD3</span>&#x3C;<span class="hljs-type">Float</span>>(<span class="hljs-operator">-</span><span class="hljs-number">3</span>, currentHeight, <span class="hljs-number">0</span>),
                                      relativeTo: placemarkAnchor)
            lineIndicator.setOrientation(orientation, relativeTo: placemarkAnchor)
            
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> status <span class="hljs-operator">=</span> line.status {
                <span class="hljs-keyword">switch</span> status.status {
                <span class="hljs-keyword">case</span> .good:
                    <span class="hljs-keyword">let</span> iconScene <span class="hljs-operator">=</span> <span class="hljs-keyword">try!</span> <span class="hljs-type">Check</span>.loadScene()
                    placemarkAnchor.addChild(iconScene)
                    iconScene.setPosition(<span class="hljs-type">SIMD3</span>&#x3C;<span class="hljs-type">Float</span>>(<span class="hljs-operator">-</span><span class="hljs-number">4</span>, currentHeight <span class="hljs-operator">-</span> <span class="hljs-number">2</span>, <span class="hljs-number">0</span>),
                                          relativeTo: placemarkAnchor)
                <span class="hljs-keyword">case</span> .disrupted:
                    <span class="hljs-keyword">let</span> iconScene <span class="hljs-operator">=</span> <span class="hljs-keyword">try!</span> <span class="hljs-type">Warning</span>.loadScene()
                    placemarkAnchor.addChild(iconScene)
                    iconScene.setPosition(<span class="hljs-type">SIMD3</span>&#x3C;<span class="hljs-type">Float</span>>(<span class="hljs-operator">-</span><span class="hljs-number">4</span>, currentHeight <span class="hljs-operator">-</span> <span class="hljs-number">2</span> , <span class="hljs-number">0</span>),
                                          relativeTo: placemarkAnchor)
                <span class="hljs-keyword">case</span> .notRunning:
                    <span class="hljs-keyword">let</span> iconScene <span class="hljs-operator">=</span> <span class="hljs-keyword">try!</span> <span class="hljs-type">Cross</span>.loadScene()
                    placemarkAnchor.addChild(iconScene)
                    iconScene.setPosition(<span class="hljs-type">SIMD3</span>&#x3C;<span class="hljs-type">Float</span>>(<span class="hljs-operator">-</span><span class="hljs-number">4</span>, currentHeight <span class="hljs-operator">-</span> <span class="hljs-number">2</span>, <span class="hljs-number">0</span>),
                                          relativeTo: placemarkAnchor)
                }
                <span class="hljs-keyword">let</span> lineIndicator <span class="hljs-operator">=</span> generateStatusIndicator(status: status)
                placemarkAnchor.addChild(lineIndicator)
                lineIndicator.setPosition(<span class="hljs-type">SIMD3</span>&#x3C;<span class="hljs-type">Float</span>>(<span class="hljs-operator">-</span><span class="hljs-number">5</span>, currentHeight <span class="hljs-operator">-</span> <span class="hljs-number">1.5</span>, <span class="hljs-number">0</span>),
                                          relativeTo: placemarkAnchor)
                lineIndicator.setOrientation(orientation, relativeTo: placemarkAnchor)
            }
            
            currentHeight <span class="hljs-operator">-=</span> meterSpacing
        }

        <span class="hljs-keyword">return</span> placemarkAnchor
    }
</code></pre>
<p>With my entity generation code in my place, my AR experience was looking a lot more like I wanted.</p>
<p><img src="/assets/img/articles/2022-04-07-ARAnchors-Bringing-Virtual-Objects-into-the-real-world/lineInfoOne.webp" alt="Line Info image">
<img src="/assets/img/articles/2022-04-07-ARAnchors-Bringing-Virtual-Objects-into-the-real-world/lineInfoTwo.webp" alt="Line Info Two image"></p>
<h3>Conclusion</h3>
<p>Overall, I was really pleased with all that I had learnt about both ARKit and Reality Composer within a few days. It was much easier to use than I ever would've imagined and you can get impressive results rather quickly. I look forward to having plenty more encounters with ARKit in the future as I am sure the technology &#x26; demand for these types of apps will continue to grow. Keep your eyes peeled to the Monstarlab Engineering blog, because just like The Terminator, I'll be back.</p>
<p>Below you can see the application in action.</p>
<p><code><div class="embed-container">
      <iframe
          src="https://www.youtube.com/embed/upBFPjHIkuU"
          width="700"
          height="480"
          frameborder="0"
          allowfullscreen="">
      </iframe>
    </div>
    </code></p>
<p>If you want to check out the whole project, you can do so <a href="https://github.com/andrewlloyd100/TubeSpotterAR">here</a></p>
<h4></h4>
<h2>Related Articles:</h2>
<ul>
<li><a href="https://developer.apple.com/videos/play/wwdc2020/10611/">Apple's introduction to ARAnchors</a></li>
<li><a href="https://api.tfl.gov.uk/">TFL API</a></li>
</ul>
<p><em>Article Photo by <a href="https://www.bbc.co.uk/news/uk-england-london-48531800">Getty Images</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Assuring Equitable Healthcare for Everybody]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/04/07/Assuring-equitable-health-care-for-everybody</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/04/07/Assuring-equitable-health-care-for-everybody</guid>
            <pubDate>Thu, 07 Apr 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>April 1st 2022 marked the kickoff of the second Monstarlab internal hackathon, <a href="https://www.linkedin.com/showcase/monstarhacks">MonstarHacks</a>, where each team built a project to tackle one of the three Life Science focused challenges:</p>
<ol>
<li>Assure equitable health care for everybody</li>
<li>Keep the “human touch” in digital healthcare solutions in isolating times</li>
<li>Connect refugees with medical support in times of crisis (with a focus on low energy consuming solutions)</li>
</ol>
<p>Our team decided to tackle the first challenge: “Assure equitable healthcare for everybody” by building a mobile application that will connect healthcare professionals with individuals seeking medical assistance through a centralized interface. This application can also be integrated with different healthcare providers (hospitals/clinics), for the individuals’ history to be available on the app. All patient history is to be stored on a permissioned blockchain, with individuals providing consent for healthcare professionals to access their history on a as-needed basis.</p>
<h3>The proposed solution</h3>
<ul>
<li>The mobile application will allow users to login using their government single-sign-on provider, or via username and password</li>
<li>Logging in with a government SSO will allow healthcare providers to upload their credentials, and have them verified by the relevant authorities/regulatory bodies</li>
<li>It will also allow individuals to consent to their medical history to be shared with the healthcare professional as needed</li>
<li>An individual, once logged in, can request medical assistance and go through the flow of describing their symptoms</li>
<li>The application will then match them with relevant medical professionals, allowing them to either navigate to their location or have a live video call with them</li>
</ul>
<h2>Solution Architecture</h2>
<figure>
<img src="/assets/img/articles/2022-04-07-Assuring-equitable-health-care-for-everybody/10.architecture.svg">
<figcaption>1. Solution Architecture</figcaption>
</figure>
<h2>Working with spatial data in .NET 6 and EF Core</h2>
<p>Spatial data represents the physical location and the shape of objects. Many databases provide support for this type of data so it can be indexed and queried alongside other data. Our application currently uses spatial data to calculate distance from a user to a healthcare professional, or even provide the capability to filter based on distance.</p>
<p>NetTopologySuite (NTS) is a spatial library for .NET that allows EF Core to map spatial data with in the database by using NTS types in your model.</p>
<p>To use NTS and spatial data with EF Core, you need to install the appropriate NuGet package, there are NuGet packages to support the following providers:</p>
<ul>
<li><a href="https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite">SQL Server</a></li>
<li><a href="https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite">SQLite</a></li>
<li><a href="https://www.nuget.org/packages/NetTopologySuite">In Memory</a></li>
<li><a href="https://www.nuget.org/packages/Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite">PostgreSQL</a></li>
<li><a href="https://www.nuget.org/packages/Pomelo.EntityFrameworkCore.MySql.NetTopologySuite">MySQL</a></li>
<li><a href="https://www.nuget.org/packages/Teradata.EntityFrameworkCore.NetTopologySuite">Teradata</a></li>
</ul>
<p>Our backend connects to an SQL Server, so we'll be installing the <a href="https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite">SQL Server</a> package.</p>
<p>The types available in the <code>NetTopologySuite.Geometries</code> namespace:</p>
<ul>
<li>Geometry
<ul>
<li>Point</li>
<li>LineString</li>
<li>Polygon</li>
<li>GeometryCollection
<ul>
<li>MultiPoint</li>
<li>MultiLineString</li>
<li>MultiPolygon</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>For our implementation, we'll be adding the <code>NetTopologySuite.Geometries.Point</code> type to the User model</p>
<pre><code class="hljs language-cs"><span class="hljs-keyword">public</span> Point? LastKnownLocation { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
</code></pre>
<p>To create a point object, you'll need the coordinates of the user, the latitude and longitude, in degrees. However, the constructor for accepts (x,y), which means you'll need to provide in (longitude,latitude) instead of the usual (latitude,longitude) you might get when interacting with Google Maps or similar. So, to create a point for Monstarlab's office in Dubai (25.0720589,55.1414239) you would use the following constructor.</p>
<pre><code class="hljs language-cs">LastKnownLocation = <span class="hljs-keyword">new</span> Point(<span class="hljs-number">55.1414239</span>, <span class="hljs-number">25.0720589</span>) { SRID = <span class="hljs-number">4326</span> };
</code></pre>
<p>The SRID field defines the spatial reference system for Earth's surface, 4326 is the reference system used by GPS satellite navigation systems and for NATO military geodetic surveying.</p>
<p>Querying users within a certain distance is straightforward, you'll need the origin latitude, longitude, as well as the maximum distance.</p>
<pre><code class="hljs language-cs"><span class="hljs-keyword">var</span> currentLocation = <span class="hljs-keyword">new</span> Point(longitude.Value, latitude.Value) { SRID = <span class="hljs-number">4326</span> };
result = DbContext.HealthcareProfessionals.Where(x => x.User.LastKnownLocation.Distance(currentLocation) &#x3C;= distance);
</code></pre>
<p>Another type that can be used for ensure your search is within a specific region, which can be beneficial if you want to search within a city or neighborhood, is the <code>NetTopologySuite.Geometries.Polygon</code>.</p>
<p>To create a polygon representing the JLT West district, where our Dubai office is located, you can use the below code snippet:</p>
<pre><code class="hljs language-cs"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">City</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-built_in">int</span> Id { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-built_in">string</span> Name { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> Polygon border { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}
</code></pre>
<pre><code class="hljs language-cs"><span class="hljs-keyword">var</span> JLTCoords = <span class="hljs-keyword">new</span> List&#x3C;Coordinate>() {
    <span class="hljs-keyword">new</span> Coordinate(<span class="hljs-number">55.142068</span>, <span class="hljs-number">25.073812</span>),
    <span class="hljs-keyword">new</span> Coordinate(<span class="hljs-number">55.136795</span>, <span class="hljs-number">25.068180</span>),
    <span class="hljs-keyword">new</span> Coordinate(<span class="hljs-number">55.137779</span>, <span class="hljs-number">25.061550</span>),
    <span class="hljs-keyword">new</span> Coordinate(<span class="hljs-number">55.146201</span>, <span class="hljs-number">25.070782</span>),
    <span class="hljs-keyword">new</span> Coordinate(<span class="hljs-number">55.142289</span>, <span class="hljs-number">25.073899</span>),
    <span class="hljs-keyword">new</span> Coordinate(<span class="hljs-number">55.142068</span>, <span class="hljs-number">25.073812</span>),
    }.ToArray();
<span class="hljs-keyword">var</span> city = <span class="hljs-keyword">new</span> City()
{
    Name = <span class="hljs-string">"JLT"</span>,
    border = <span class="hljs-keyword">new</span> Polygon(<span class="hljs-keyword">new</span> LinearRing(JLTCoords)) { SRID = <span class="hljs-number">4326</span> }
};
</code></pre>
<p>This Polygon can be visualized on <a href="https://www.google.com/maps/d/u/0/edit?mid=1EODatXqNYrAbtOdmmUnmJIXwCvBg0dPL&#x26;usp=sharing">Google Maps</a>.</p>
<p>Subsequently, you can query the users within a region as follows:</p>
<pre><code class="hljs language-cs">DbContext.HealthcareProfessionals.Where(x => x.User.LastKnownLocation.Intersects(city.border));
</code></pre>
<h2>Design Ideas</h2>
<p>Finally, we built a sample app to demonstrate the application:</p>
<figure>
<img src="/assets/img/articles/2022-04-07-Assuring-equitable-health-care-for-everybody/01.login.svg">
<figcaption>2. App login</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2022-04-07-Assuring-equitable-health-care-for-everybody/02.hcp-confirm.svg">
<figcaption>3. Healthcare Professional Confirmation Screen</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2022-04-07-Assuring-equitable-health-care-for-everybody/03.symptoms-1.svg">
<figcaption>4.a. Individual symptoms flow part 1</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2022-04-07-Assuring-equitable-health-care-for-everybody/04.symptoms-2.svg">
<figcaption>4.b. Individual symptoms flow part 2</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2022-04-07-Assuring-equitable-health-care-for-everybody/05.symptoms-3.svg">
<figcaption>4.c. Individual symptoms flow part 3</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2022-04-07-Assuring-equitable-health-care-for-everybody/06.symptoms-3.svg">
<figcaption>4.d. Individual symptoms flow part 4</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2022-04-07-Assuring-equitable-health-care-for-everybody/07.hcp-list.webp">
<figcaption>5. List of Healthcare Professionals</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2022-04-07-Assuring-equitable-health-care-for-everybody/08.hcp-profile.webp">
<figcaption>6. Healthcare Professional Profile</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2022-04-07-Assuring-equitable-health-care-for-everybody/09.hcp-call.webp">
<figcaption>7. Video Call with Healthcare Professional</figcaption>
</figure>
<h3>Related Articles</h3>
<ul>
<li><a href="https://docs.microsoft.com/en-us/ef/core/modeling/spatial">Spatial Data - EF Core</a></li>
<li><a href="https://spatialreference.org/ref/epsg/4326/">WGS 84: EPSG Projection</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Best practices for doing code reviews]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/04/06/Best-practices-for-doing-code-reviews</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/04/06/Best-practices-for-doing-code-reviews</guid>
            <pubDate>Wed, 06 Apr 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>In my early career, when I was asked for the first time by one of my seniors to review his code, I was honored but confused at the same time. What could a junior developer have anything to say about his senior’s years of experience writing code? What I did not understand at that time was that there is no best code. The best code is no code <a href="https://blog.codinghorror.com/the-best-code-is-no-code-at-all">1</a>.</p>
<p>All new lines of code added to your project should be built, tested, reviewed, debugged and later, if necessary load tested. Moreover, being the most experienced developer of a team should not mean that you are given carte blanche to add whatever solution you bring without having it approved.</p>
<p>Writing code can simply be put as proposing an optimal solution that is tightly driven by the level of understanding of the problem to be solved and the skills of the person solving it. Given that we are only humans, there is always room for mistakes, misjudgments and even simply ignorance. Hence, the importance of conducting an effective code review.</p>
<p>In this article, we will first lay out some best practices for a code review. Then briefly discus the specific case of a candidate code challenge review.</p>
<h2>Take your time but don't waste it</h2>
<p>You should not review code swiftly, just as you should not review code for too long. Sometimes we are so overwhelmed by the pace reviews are requested that we are tempted to just do basic checks. Code review time should be included in the task estimation time. Don't fall into the <strong>“this will be manually tested anyway”</strong> trap.</p>
<p>Long review time can be avoided with a good task breakdown. Enforce division of the work into smaller tasks with specific logic to deal with. Those are easier to review than a full feature at once.</p>
<h2>Use a checklist</h2>
<p>Having a review checklist will ensure your code consistency. Code will be evaluated with the same criteria, and you will make sure nothing is left out. The list should be kept up to date as your team experience grows.</p>
<p><img src="/assets/img/articles/2022-04-06-Best-practices-for-doing-code-reviews/spring-boot-java-checklist.webp" alt=""></p>
<h2>Use automated code review tools</h2>
<p>There are plenty of efficient and customizable code review and analyzer tools that your team can use to help assist the code review process <a href="https://www.softwaretestinghelp.com/tools/top-40-static-code-analysis-tools">2</a>.</p>
<p><img src="/assets/img/articles/2022-04-06-Best-practices-for-doing-code-reviews/sonar-cloud.webp" alt=""></p>
<p><img src="/assets/img/articles/2022-04-06-Best-practices-for-doing-code-reviews/linting.webp" alt=""></p>
<h2>Build it locally and test it when necessary</h2>
<p>Sometimes, just looking at the code might not be enough to spot crucial errors. For example when reviewing a batch process, running the code build locally can be more telling than just doing visual inspections. Any findings at this point could however mean poor test code quality.</p>
<h2>How to conduct a code review?</h2>
<p>Each team has its own way of organizing a code review. Most of us, I presume, are mainly just writing comments on GitHub or sending feedback messages over Slack.</p>
<p>But when time permits, do pair programming or schedule a review meeting with the developer. These methods are more effective, more interactive than just leaving comments, and are a good mentoring means.</p>
<p>Don’t give orders, give suggestions and always give the reason why you think it is a better way. This will help the developer understand better.</p>
<p>Don’t just focus on finding mistakes, positive comments are always welcome. When you come across that clever and magical piece of code that impresses you, give a thumbs up or write a compliment. It makes a developer feel appreciated and can lead to a better working relationship.</p>
<h2>What to look for when reviewing?</h2>
<ul>
<li>Are the requirements fulfilled?</li>
<li>Is the code following good design principles?</li>
<li>How complex is the code? Is it readable?</li>
<li>What about performance and scalability issues? Always remember that data is the most important part of your system, and you should always think about how things would perform at a large scale.</li>
<li>What about security issues? Look for potential security threats such as SQL injection, XSS, personal data handling, etc.</li>
<li>Is the code well commented?</li>
<li>What about maintainability? Is the code DRY?</li>
<li>Is the code style following your predefined guidelines?</li>
<li>What about reusability, naming conventions, etc.?</li>
</ul>
<h2>Code challenge review</h2>
<p>Most of the statements above also apply when reviewing a candidate code challenge. Even though the problem to solve is usually simple, there are interesting points that can help you decide whether to move the candidate to the next hiring step.</p>
<p>Review criteria include, but are not limited to,</p>
<ul>
<li>the ability to understand the given requirements,</li>
<li>the problem-solving approach,</li>
<li>the core architecture and design of the proposed solution,</li>
<li>the initial setup complexity: it should not be OS dependent and should run out of the box,</li>
<li>the choice of technology stack used,</li>
<li>the quality of code proposed, ...</li>
</ul>
<p>One thing to always keep in mind when doing a candidate code review is that these code challenges are not a contest. A candidate should not be penalized for proposing a solution that is less impressive than another candidate code that you have reviewed before.</p>
<h2>Conclusion</h2>
<p>A good code review will save you time and money by spotting potential problems before moving to production. Beyond that it can help you build a good team collaboration by sharing techniques and tips. Especially in these pandemic days, when most developers spend their time isolated, let's avoid just leaving comments on GitHub and take the time to meet for code review.</p>
<h2>References</h2>
<p>[1] <a href="https://blog.codinghorror.com/the-best-code-is-no-code-at-all">The Best Code is No Code At All</a></p>
<p>[2] <a href="https://www.softwaretestinghelp.com/tools/top-40-static-code-analysis-tools">TOP 40 Static Code Analysis Tools</a></p>
<p>[3] <a href="https://google.github.io/eng-practices/review/reviewer">How to do a code review</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building next generation applications using voice]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/04/06/Building-Next-Generation-Applications-Using-Voice</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/04/06/Building-Next-Generation-Applications-Using-Voice</guid>
            <pubDate>Wed, 06 Apr 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Back in 2019, there was a presentation made at <a href="https://youtu.be/CFIxLnqEifs?t=3443">Google India</a>, where it was showcased how Indians can use a phone number, toll-free, and talk to Google Assistant in their language. The idea was just mind-blowing and too good to be true, as features like this would ensure folks who have only 2G phones around the world, not part of the internet, can suddenly have empowerment to take any internet services while talking to the Google Assistant using a simple phone call. When MonstarHacks 2022 edition was announced, Team Bowlers comprised of Saad Bin Amjad and Ahmed Naseef Chowdhury, we knew exactly what we would be doing!</p>
<p>The goal for this <a href="https://www.linkedin.com/showcase/monstarhacks">MonstarHacks</a> edition was to come up with an awesome product prototype over two days that can positively impact the lives of people, especially in the field of Life Science and healthcare. We registered and decided we will try to solve 2 challenges. Primarily to connect refugees with medical support in times of crisis (with a focus on low energy consuming solutions) and secondarily to assure equitable health care for everybody.</p>
<p>Our team strongly believes healthcare is a right for everyone, it should be especially protected for the most vulnerable groups in our societies. Unlike other commercial domains, like arts and business, we see a lack of innovation in medicare, as often systems are black-boxed for both caregivers and takers. So we wanted to have a solution where what works in other domains can be contextualized in the healthcare domain and used for similar meaningful impact. Through technology, we can impact people’s lives faster and efficiently, and medicare needs innovative ideas to do just that, to protect the most vulnerable folks, like senior citizens, expecting mothers, children, etc.</p>
<h2>Problem Statement</h2>
<p>In Bangladesh, there is an ongoing Rohingya Refugee Crisis. There are now 860,000 Rohingya living in refugee camps in Cox’s Bazar, Bangladesh, over half of whom are children. The refugees still have inadequate water, sanitation, healthcare, education, nutrition, protection, and communication tools.</p>
<p>The recent Ukraine refugee crisis also put spotlight on the turmoils that can be faced even in developed countries. For the survival of refugees, the foremost need is to have a functioning medical health care system.</p>
<p>Our team believes in Tech For Good and wants to do our part to help ease the crisis, and make it a bit better than what it is right now.</p>
<p>Apart from political will, the major risks/obstacles to connecting refugees with medical support are the overburdened medical workforce and lack of funded facilities, visible/invisible barriers for refugees to seek out help from proper authorities without fearing deportation.</p>
<p><img src="/assets/img/articles/2022-04-06-Building-Next-Generation-Applications-Using-Voice/team.webp" alt=""></p>
<h2>Case Study: CareBooth</h2>
<p>2G Voice-Based Medical Assistant For Refugees</p>
<p><div class="embed-container">
      <iframe
          src="https://www.youtube.com/embed/ihrWeQ0V7g8"
          width="700"
          height="480"
          frameborder="0"
          allowfullscreen="">
      </iframe>
    </div>
    </p>
<p>CareBooth, is a 2G Voice-Based Medical Assistant For Refugees. To get various assistance like asking for emergency medical help, registering for vaccination, learning about prevention of diseases and updates of local news, etc, one does not need any internet connection! They just need a 2G Phone that can make a call. A phone call giving a gateway to internet services to refugees of different age groups!</p>
<p>This solution enables refugees of different age groups to have greater ease of access to health info and systems from a technical, UX perspective, as everything is abstracted behind simple voice commands in their local languages.</p>
<p>The use of Intent - Entity Matching, Knowledge Bank, Natural Language Processing, and Machine Learning on the assistant (Bot Cognition) ensures efficient automation of logical processes in refugee medicare flow, meaning most of the tasks would now not require a human workforce, thus reducing constraints on the medical/community workforce. The care flows now will have Machine Learning and AI inclusion, which will be trained and verified by credible authorities, ensuring efficacy and scalability of the solution. For example, when it comes to pre-medical screening for a vaccination drive, instead of health workers going door to door, a simple phone call would help the refugees to fill up the form and also get registered.</p>
<p>Phone calls are an example of low consuming energy from the user’s end and automation, and the use of internet services generates equitable healthcare as it removes any sort of censorship or bias from the manual workflow.</p>
<p>Ease of access, since voice commands are quite UX friendly than multiple forms, flows on webpages. Also, the consumption of information becomes authentic (since peer-reviewed FAQs) as well as it is done in local languages (the ones that are available in NLP units).</p>
<p>In the future, for CareBooth to be really practically viable for refugees, we would need corporations that host AI / Bot Cognition to come up with reduced prices for NLP or sponsor it on the humanitarian ground for refugees, and phone companies to give the phone numbers toll-free for the refugees access, just as we have seen how it was rolled out in India with the help of a phone company.</p>
<p>Medical Agent intent and entity modeling is quite tough, as there are no easy, available resources up for developer consumption, so we would also require different stakeholders to make it on the design board while designing the conversations effectively.</p>
<h2>Technologies used</h2>
<p>(including 3rd party frameworks and services):</p>
<ul>
<li><a href="https://cloud.google.com/gcp/">Google Cloud Console</a></li>
<li><a href="https://developers.google.com/assistant">Actions On Google</a></li>
<li><a href="https://firebase.google.com/">Firebase</a></li>
<li><a href="https://firebase.google.com/products/firestore?">Firestore</a></li>
<li><a href="https://cloud.google.com/dialogflow/docs">DialogFlow</a></li>
<li><a href="https://cloud.google.com/dialogflow/es/docs/integrations/phone-gateway">DialogFlow Phone Gateway</a></li>
<li>Node.js Express Server</li>
<li>HTML</li>
<li><a href="https://whimsical.com/">Whimsical</a> for diagrams</li>
</ul>
<p>Conversational Design and Basic Architecture can be found <a href="https://whimsical.com/carebooth-FAQxXgCP5hqtpXbofnC2tK">here</a>.</p>
<p><img src="/assets/img/articles/2022-04-06-Building-Next-Generation-Applications-Using-Voice/assistant.webp" alt=""></p>
<p>Voice applications are the next generation of applications, as billions of people who are still disconnected from the internet can avail internet services in 2G ways. Given the huge success of chatbots in CRM, it was a matter of time before voice would be adopted in the 4th Industrial Revolution. We were proud to spend our weekend creating something that can help ease the current refugee issue by empowering the refugees to avail of internet services via their 2G phones and not be dependent on internet connectivity or data packs.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/Ihi56-Qs39U">Nastya Dulhiier</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Working as a Tech Lead at Monstarlab]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/03/18/Work-at-ML</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/03/18/Work-at-ML</guid>
            <pubDate>Fri, 18 Mar 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>It’s been almost 6 months since I started working at Monstarlab. I want to share with you what it’s like “being a Tech Lead at Monstarlab” and hope that my insights and personal experiences will benefit anyone who has an interest in working at Monstarlab.</p>
<h2>Diversity</h2>
<p>I am sure many people working at Monstarlab share a similar view on this.</p>
<p>With Monstarlab offices all over the world, project teams are built across the globe and various languages other than Japanese are used to communicate. It’s inspiring to work in an environment without fixating on Japanese values and exchange ideas with people from different regions and countries.</p>
<p>And to better connect with Monstars - Monstarlab members - from other teams and countries, we are working on a “Culture Book” in which company culture is shared. The purpose of this platform is to build an organization and development teams in which members voluntarily build working relationships by acknowledging their strengths and make full use of those strengths.</p>
<p>On the other hand, language differences could sometimes be a challenge. Generally, Japanese and English are mainly used but the company supports English/Japanese learning through the benefit packages they offer so for anyone looking to improve their English/Japanese skills, it’s a great opportunity.</p>
<h2>Job of a Tech Lead</h2>
<p>In a nutshell, a Tech Lead is like a midfielder in soccer (or football.) More specifically, the biggest responsibility of a Tech Lead is assessing and selecting technology and communicating with team members of various functions. Actually, the scope of responsibility of a Tech Lead varies by project. The following image is an example that will give you an idea of what the job and responsibilities could include.</p>
<p><img src="/assets/img/articles/2022-03-18-Work-at-ML/image01_en.webp" alt=""></p>
<p>If you excel in coding, you may be in charge of implementation; if architecture is your speciality, then you could be creating the specification. A lot of times, Tech Lead acts as the control tower to ensure the project moves forward smoothly from a technical aspect. It’s a critical role in charge of discussions with clients and deciding specifications which sets the foundation for the key technology in the upstream process. This would be an exciting role for anyone who is looking into technological support from a business perspective or creating better products with new technology.</p>
<h2>Offshore development</h2>
<p>Monstarlab has offshore offices within the group and proactively engages them in projects.<br>
I was anxious in my first Tech Lead role, but with the support of an assigned communicator in the project team who acted as an interpreter, the project went smoothly. Offshore development is a way in which high-quality products could be provided at a low cost, so we develop from a global perspective. And this is one of the contributing factors to the significant number of requests we receive from clients to work on projects together.</p>
<h2>Working with clients</h2>
<p>At Monstarlab, there are many opportunities to work directly with clients.</p>
<p>In a corporate digital transformation project, our involvement sometimes begins with building a business strategy. There are many opportunities to propose new technology and take on the challenge to find a solution for a problem.<br>
“Do as you’re told” work hardly exists; you are expected to make technical suggestions and be capable of quick learning. While enjoying freedom in the workplace, you experience more decision making in a broad spectrum. And because of the nature of the job, you have the opportunity to encounter various industries and acquire different technological skills. Every time I work on a project, I see my abilities and skills expand and that’s really exciting.</p>
<h2>Full remote work (Work from anywhere in Japan)</h2>
<p>Tasks and communication can be accomplished while working fully remote. There are a number of Monstars who don’t work at the main office. If we need to speak to each other, we simply huddle on Slack or use Google Meet. It’s great to be able to work and take breaks at my own discretion.</p>
<h2>Great people, open communication</h2>
<p>There’s an openness in communication between workers and that creates a healthy environment which allows you to talk to your superior if something is on your mind.</p>
<p>There’s also a mentoring program that helps you get your concerns off your mind, whether it’s about work or life in general. I received helpful advice on managing workload and skill enhancement which helped me figure out the work-life balance and assess my career plan.<br>
For issues that come up on the job that you can’t figure out on your own, there are Slack channels dedicated to the specific topic where you could ask questions and Monstars across the globe provide you with thorough tips and advice.</p>
<h2>Express your opinion from day 1</h2>
<p>The company encourages Monstars to thrive which creates an environment that allows everyone to freely express their opinions and incorporate their ideas. And there are opportunities where you can dive into discussions and unleash your potential in many areas.</p>
<h2>Family-friendly workplace</h2>
<p>Monstarlab is very generous to anyone raising children.<br>
The company implements the Discretionary Labor System which enables Monstars to manage our family life with flexible working hours so we could manage work schedules according to our personal situations. I was startled to see both men and women taking childcare leave. It certainly reflects the needs of our times and it wouldn’t have been possible without the diversity the company achieves.</p>
<h1>Thanks for reading!</h1>
<p>I hope this gave you a better picture of what Monstarlab is all about; a look inside the company and an idea of what it’s like to work at Monstarlab.<br>
Monstarlab is looking for talent who share the mission to “Empower talent everywhere to engineer awesome products, services and ecosystems; building a brighter world for us all.” If you are interested, visit Careers on our website to find out more information.</p>
<h2>Reference</h2>
<ul>
<li><a href="https://www.join.monstar-lab.com/">Monstarlab Japan hiring page</a></li>
</ul>
<p><em><a href="https://www.pexels.com/@fauxels">Article Photo by fauxels</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[iOS: Automating the upload of DSYM files to Firebase using Fastlane & Bitrise]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/03/03/Automating-The-Upload-Of-iOS-Firebase-DSYM-files</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/03/03/Automating-The-Upload-Of-iOS-Firebase-DSYM-files</guid>
            <pubDate>Thu, 03 Mar 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>What was the issue?</h2>
<p>Crash reporting is something that's hugely important for all our apps. It allows us to easily find issues within our apps and can give us helpful advice on where to fix them. For our crash reporting, we use <a href="https://firebase.google.com/products/crashlytics">Firebase Crashlytics</a>. For the crash reporting to work in Crashlytics, it needs dSYM files (debug symbol files) but our issue was our application has bitcode enabled.</p>
<p>Bitcode was first introduced by Apple with the release of Xcode 7, it allows Apple to re-optimize your app binary to tailor the app to the capabilities of the device used. For our particular use case, our application had WatchOS support and for both WatchOS &#x26; tvOS apps, bitcode is required, meaning simply turning it off wasn’t an option. And hey, it’s a cool feature that we didn’t want to turn off anyway. <a href="https://help.apple.com/xcode/mac/current/#/devbbdc5ce4f">You can read more about Bitcode here</a>.</p>
<p>With bitcode enabled, the dSYM files only become available to you after your app is done processing on App Store Connect. Previously we were downloading these files manually from App Store Connect and then manually uploading them to firebase, which as you can imagine, was a very tedious task.</p>
<p>We wanted to find a way to automate this within our current CI using Bitrise so we wouldn’t ever have to suffer that tedium again!</p>
<h2>Using Fastane to solve it</h2>
<p>Currently we use <a href="https://fastlane.tools">Fastlane</a> mostly to handle the signing of our apps, but it’s proved our main saviour here also.</p>
<p>These are the steps required to upload your dSYM files automatically using Fastlane &#x26; Bitrise together. Here we go…</p>
<h2>1. Add Fastlane to your project</h2>
<p>First of all, you're going to need to install Fastlane and add it to your project. This is as easy as just installing it and running <code>fastlane init</code> in the terminal at the root of your project. Fastlane may ask you what lanes you might want to setup automatically but you can just select a custom setup. The main result should be it will generate a folder in the root of your project called "Fastlane" with two files; <code>Appfile</code> &#x26; <code>Fastfile</code>. Don't worry too much about whats in the files as we will update that. If you see this result, congratulations! It worked!</p>
<pre><code class="hljs language-csharp">fastlane <span class="hljs-keyword">init</span>
</code></pre>
<p><img src="/assets/img/articles/2022-03-03-Automating-The-Upload-Of-iOS-Firebase-DSYM-files/fastlaneSetup.webp" alt="Fastlane setup image"></p>
<p>If you need more help getting Fastlane setup in your project <a href="https://docs.fastlane.tools/getting-started/ios/setup/">see here</a></p>
<h2>2. Getting the Firebase upload script where you need it.</h2>
<p>The Firebase SDK includes a script that we can use to upload the dSYM files. This is what we are going to use to upload them using Fastlane…</p>
<p>Side note; If you’re using Cocoapods, you wont have to do this or specify the binary path in your Fastfile. The script is already present in the Crashlytics pod folder and fastlane can find it. If you go this route, make sure to either commit your pod dependencies into your repo or install the pods in your Bitrise workflow before you run the fastlane step.</p>
<ol>
<li>
<p>Add a folder to the root of your project and call it "scripts".</p>
</li>
<li>
<p>Next navigate to where your Firebase SDK is located. Then go into the folder Crashlytics. If using SPM, your Firebase SDK should be able to be located via right clicking and selecting "Show In Finder" on the package from within Xcode.</p>
</li>
<li>
<p>Here you should find the script "upload-symbols". Copy/paste this script into the scripts folder you just created in step 1. Note; you shouldn't need to add these or a reference to them into your Xcode project. Being in your project finder/repository is what we want.</p>
</li>
</ol>
<h2>3. Updating the <code>Appfile</code></h2>
<p>Now let's configure our <code>Appfile</code> as required. Open that file up from within your "Fastlane" folder.</p>
<p>We don't need to add much information here, we just need to confirm our platform and our team id. This team id relates to your App Store Developer team and can be found in your account details <a href="https://developer.apple.com/account">there</a>.</p>
<p>Then for each environment your project has, add some code like the following, specifying your environment in the lane name and which app identifier that environment uses.</p>
<pre><code class="hljs language-ruby">  for_lane <span class="hljs-symbol">:refresh_dsyms_prod</span> <span class="hljs-keyword">do</span>
    app_identifier <span class="hljs-string">'com.Monstarlab.myExampleApp.Prod'</span>
  <span class="hljs-keyword">end</span>
</code></pre>
<p>In the end you should have done it for each environment you wish to support and your app file should look something like this</p>
<pre><code class="hljs language-ruby">for_platform <span class="hljs-symbol">:ios</span> <span class="hljs-keyword">do</span>
  team_id <span class="hljs-string">'69ABZ3N123'</span>
  for_lane <span class="hljs-symbol">:refresh_dsyms_staging</span> <span class="hljs-keyword">do</span>
    app_identifier <span class="hljs-string">'com.Monstarlab.myExampleApp.Staging'</span>
  <span class="hljs-keyword">end</span>
  for_lane <span class="hljs-symbol">:refresh_dsyms_prod</span> <span class="hljs-keyword">do</span>
    app_identifier <span class="hljs-string">'com.Monstarlab.myExampleApp.Prod'</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<h2>3. Configuring your <code>Fastfile</code></h2>
<p>Next up is to configure our <code>Fastfile</code> with the lanes we need to run to do this job.</p>
<p>We're going to show this in reverse as it will be easier to explain. Your <code>Fastfile</code> will look something like this in the end. In this example, we are supporting the upload of two different environments (Firebase projects) dSYM uploads.</p>
<pre><code class="hljs language-ruby">update_fastlane

default_platform(<span class="hljs-symbol">:ios</span>)

platform <span class="hljs-symbol">:ios</span> <span class="hljs-keyword">do</span>
  desc <span class="hljs-string">"Downloads DSYM Files from ASC and upload them to firebase"</span>
  lane <span class="hljs-symbol">:refresh_dsyms_prod</span> <span class="hljs-keyword">do</span>
     download_dsyms(<span class="hljs-symbol">version:</span> <span class="hljs-string">"latest"</span>)
     upload_symbols_to_crashlytics(
      <span class="hljs-symbol">gsp_path:</span> <span class="hljs-string">"./App/Resources/Firebase/GoogleService-Info.plist"</span>,
          <span class="hljs-symbol">binary_path:</span> <span class="hljs-string">'./scripts/upload-symbols'</span>
    )
     clean_build_artifacts
   <span class="hljs-keyword">end</span>
   lane <span class="hljs-symbol">:refresh_dsyms_staging</span> <span class="hljs-keyword">do</span>
     download_dsyms(<span class="hljs-symbol">version:</span> <span class="hljs-string">"latest"</span>)
     upload_symbols_to_crashlytics(
      <span class="hljs-symbol">gsp_path:</span> <span class="hljs-string">"./App/Resources/Firebase/GoogleService-Info-Staging.plist"</span>,
          <span class="hljs-symbol">binary_path:</span> <span class="hljs-string">'./scripts/upload-symbols'</span>
    )
     v
   <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>The first few lines just assure to update fastlane if required and defines the platform.</p>
<p>Then below that, you can see each lane is defined with the same names we used in our Appfile. Fastlane uses these names to relate them together to use the correct Bundle ID for the related lane.</p>
<p>Let's look at the three steps in our lanes.</p>
<ol>
<li></li>
</ol>
<pre><code class="hljs language-less"><span class="hljs-selector-tag">download_dsyms</span>(<span class="hljs-attribute">version</span>: <span class="hljs-string">"latest"</span>)
</code></pre>
<p>This step downloads the dsym files from App Store Connect. Currently we're only downloading the latest available builds files. You can leave this parameter off and it will download the files for every version available on App Store Connect but beware, if you have a lot of builds, this could take a while! There are some other options for specifying specific build versions or dates.</p>
<p>You can find out more info on the action and it's options <a href="https://docs.fastlane.tools/actions/download_dsyms/">here</a></p>
<ol start="2">
<li></li>
</ol>
<pre><code class="hljs language-less"><span class="hljs-selector-tag">upload_symbols_to_crashlytics</span>(
      <span class="hljs-attribute">gsp_path</span>: <span class="hljs-string">"./App/Resources/Firebase/GoogleService-Info-Staging.plist"</span>,
          <span class="hljs-attribute">binary_path</span>: <span class="hljs-string">'./scripts/upload-symbols'</span>
    )
</code></pre>
<p>Here we are uploading the symbols we just downloaded to Firebase Crashlytics. We need to specify the location of the <code>GoogleService-Info.plist</code> file within our repository. This is used so we know which Firebase project to upload them to.</p>
<p>You will also need to specify the path to that script with <code>binary_path</code>. As you can see, it points to the <code>scripts</code> folder we created earlier.</p>
<p>More info on this fastlane action can be found <a href="https://docs.fastlane.tools/actions/upload_symbols_to_crashlytics/">here</a>.</p>
<ol start="3">
<li></li>
</ol>
<pre><code class="hljs">clean_build_artifacts
</code></pre>
<p>This then simply cleans all the dSYM files we have downloaded.</p>
<p>More info <a href="https://docs.fastlane.tools/actions/clean_build_artifacts/">here</a>.</p>
<h2>4. Setting up Bitrise</h2>
<p>Ok, that is all that is needed in terms of Fastlane setup in our project. Now let's see how we can run those Fastlane lanes daily using Bitrise to keep our Firebase crash reporting up to date and returning useful crash reports.</p>
<p>For the sake of keeping this blog size less than the size of a novel, we assume you have your Bitrise project already set up and are already using it to upload your application to App Store Connect using the <code>deploy-to-itunesconnect-deliver</code> workflow with API Keys.</p>
<ol>
<li>Go into your projects bitrise.yml and add this workflow</li>
</ol>
<pre><code class="hljs language-yaml"><span class="hljs-attr">deploy-dsyms:</span>
  <span class="hljs-attr">after_run:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">prepare</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">dsym-upload</span>
</code></pre>
<p>What this is doing is creating a workflow called "deploy-dsyms". It then runs two flows;
<code>prepare</code>; which activates required ssh keys &#x26; clones our project. You'll most likely already have a workflow that does something like this.
<code>dsym-upload</code>; is then the workflow that handles the upload</p>
<ol start="2">
<li>Add this second <code>dsym-upload</code> workflow to your birise.yml</li>
</ol>
<pre><code class="hljs language-kotlin">dsym-upload:
    steps:
    - <span class="hljs-symbol">fastlane@</span><span class="hljs-number">3</span>:
        inputs:
        - connection: <span class="hljs-string">'off'</span>
        - api_key_path: <span class="hljs-string">"<span class="hljs-variable">$BITRISEIO_ASC_API_KEY_URL</span>"</span>
        - api_issuer: <span class="hljs-string">"<span class="hljs-variable">$ASC_ISSUER_ID</span>"</span>
        - verbose_log: <span class="hljs-string">'yes'</span>
        - work_dir: <span class="hljs-string">"./"</span>
        - lane: refresh_dsyms_prod
    - <span class="hljs-symbol">fastlane@</span><span class="hljs-number">3</span>:
        inputs:
        - connection: <span class="hljs-string">'off'</span>
        - api_key_path: <span class="hljs-string">"<span class="hljs-variable">$BITRISEIO_ASC_API_KEY_URL</span>"</span>
        - api_issuer: <span class="hljs-string">"<span class="hljs-variable">$ASC_ISSUER_ID</span>"</span>
        - verbose_log: <span class="hljs-string">'yes'</span>
        - work_dir: <span class="hljs-string">"./"</span>
        - lane: refresh_dsyms_staging
</code></pre>
<p>What is happening here is it is running a Fastlane workflow, twice for each of our environments we want to support (Staging &#x26; Production). You will have to add a flow for each of your environments.</p>
<p>You may notice it looks very similar to the <code>deploy-to-itunesconnect-deliver</code> workflow. It requires the <code>api_key_path</code> and <code>api_issuer</code> just the same as that.</p>
<p>The two important differing properties are;</p>
<ul>
<li>The <code>work_dir</code> which should just be the root of your project</li>
<li>The <code>lane</code> which should match the name of the lane you specified in your Fastfile for that environment.</li>
</ul>
<h2>5. Automating your workflow</h2>
<p>OK, now we</p>
<ul>
<li>have our fastlane lanes all setup to do the job</li>
<li>have our bitrise workflows ready to run</li>
<li>call them</li>
</ul>
<p>All that is left is for us to schedule them.</p>
<p>As we need to wait for the app to finish processing on App Store Connect before these files are available, we decided we were just going to run this daily late at night. We didn't want to take our company's limited Bitrise spaces as these could be required by other developers throughout the day to upload builds etc and waiting for the app to finish processing in an upload workflow would eat up a lot of unused time.</p>
<p>If you go to your projects Builds page in Bitrise, you can select on the right hand side to "start/schedule a build". Select this.</p>
<p><img src="/assets/img/articles/2022-03-03-Automating-The-Upload-Of-iOS-Firebase-DSYM-files/bitriseButton.webp" alt="Schedule Button image"></p>
<p>You can then select which days you want the workflow to run and at what time. We chose to run them on weekdays at 11pm as we rarely upload builds on weekends and 11pm is a quiet time for us.
You then select which branch you are using mainly to upload builds from but this shouldn't really matter. As long as that branch has the fastlane setup in it, we're all good. Then select your <code>deploy-dsyms</code> workflow as the flow you wish to run and select "Schedule Build".</p>
<p><img src="/assets/img/articles/2022-03-03-Automating-The-Upload-Of-iOS-Firebase-DSYM-files/scheduleABuild.webp" alt="Schedule Build image"></p>
<p>Thats it you're all done! Bitrise will now download and upload your DSYMS at the times you just set.</p>
<h3>Conclusion</h3>
<p>Hopefully this tutorial will help you automate the upload of dSYM files to Crashlytics and save you those annoying 5 minutes whenever you want to deduce why your app is crashing.</p>
<h4></h4>
<h2>Related Articles:</h2>
<ul>
<li><a href="https://blog.bitrise.io/post/adding-missing-dsyms-to-crashlytics-with-fastlane">Adding missing dSYMs to Crashlytics with Fastlane</a></li>
<li><a href="https://help.apple.com/xcode/mac/current/#/devbbdc5ce4f">Bitcode &#x26; App Thinning</a></li>
</ul>
<p><em>Article Photo by <a href="https://firebase.google.com/products/crashlytics">Firebase</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introduction to GPUs with OpenGL]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/03/01/Introduction-To-GPUs-With-OpenGL</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/03/01/Introduction-To-GPUs-With-OpenGL</guid>
            <pubDate>Tue, 01 Mar 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Nowadays, we have amazing engines like <a href="https://unity.com/">Unity</a> and <a href="https://www.unrealengine.com/en-US/">Unreal</a>. But how does this work exactly? Learning 3D rendering or building your own engine is a daunting task.</p>
<p>While there are some tutorials out there, I think they are actually quite difficult if you want to fully understand the logic of it.
So today, I would like to try to explain how it works at a lower level, without going too much in the details or the mathematics.</p>
<p>I will not show how to build a clean, production-ready app following the best practices, but rather taking a lot of shortcuts and simplifying things to make this easy to follow.</p>
<h2>What is a GPU?</h2>
<p>CPUs are highly specialized in computational speed and have a broad range of low-level instructions.
GPUs are quite the opposite, because they are slower and simpler, but focuses on parallelization (high number of cores), and have a more limited set of instructions.</p>
<p>While a typical CPU today will have anywhere between 2 and 16 cores, a GPU typically have thousands of cores running operations in parallel.
Both have their own independent memory and (depending on the architecture) only communicate by exchanging data via a dedicated bus.</p>
<figure>
    <img src="/assets/img/articles/2022-03-01-Introduction-To-GPUs-With-OpenGL/cpu-vs-gpu.webp" alt="Difference between CPU and GPU">
    <figcaption>
        Image by
        <a href="https://developer.nvidia.com/blog/cuda-refresher-reviewing-the-origins-of-gpu-computing/">
            Nvidia
        </a>
    </figcaption>
</figure>
<h2>What is OpenGL?</h2>
<p>GL stands for Graphics Library. It is a framework that makes it easier to interact with the GPU and display graphics. There are well-used alternatives like DirectX and Vulkan as well, but we'll focus on OpenGL.</p>
<p>OpenGL comes into two parts:</p>
<ul>
<li>The library itself, which you can use with pretty much any programming language. This is what runs on the CPU and sends instructions and data to the GPU.</li>
<li>The shaders. Shaders are pieces of instructions (code) that are first compiled by the CPU, then sent to the GPU. This is what gets executed on the GPU side. In OpenGL, shaders are written using the GLSL language (Graphics Library Shading Language).</li>
</ul>
<p>OpenGL also has an alternative edition called OpenGL ES (Embedded Systems). It has less features and is simplified, so that it is able to run on most mobile devices and architectures nowadays. It is also known as WebGL, and because browsers makes it easy to get started with no other setup, this is what I will use demonstrate here (with plain JavaScript). But don't worry! You can transpose the code in this article to pretty-much any programming language that you like.</p>
<h2>Setup</h2>
<p>I will use a very basic and single-page HTML file for all the code shown in this article.
You can also find a link to the whole result at the bottom of this page.</p>
<pre><code class="hljs language-html"><span class="hljs-meta">&#x3C;!DOCTYPE <span class="hljs-keyword">html</span>></span>
<span class="hljs-tag">&#x3C;<span class="hljs-name">html</span>></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">body</span>></span>
        <span class="hljs-tag">&#x3C;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span>></span><span class="javascript">
            <span class="hljs-keyword">const</span> canvas = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">createElement</span>(<span class="hljs-string">'canvas'</span>);
            canvas.<span class="hljs-property">width</span> = <span class="hljs-number">640</span>;
            canvas.<span class="hljs-property">height</span> = <span class="hljs-number">480</span>;
            <span class="hljs-variable language_">document</span>.<span class="hljs-property">body</span>.<span class="hljs-title function_">appendChild</span>(canvas);

            <span class="hljs-keyword">const</span> gl = canvas.<span class="hljs-title function_">getContext</span>(<span class="hljs-string">'webgl'</span>);
            <span class="hljs-keyword">if</span> (!gl) {
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">'Unable to use WebGL. Your device may not support it.'</span>);
            }

            gl.<span class="hljs-title function_">clearColor</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0.5</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>);
            gl.<span class="hljs-title function_">clear</span>(gl.<span class="hljs-property">COLOR_BUFFER_BIT</span>);

            <span class="hljs-comment">// &#x3C;-- Here we will add more code</span>
        </span><span class="hljs-tag">&#x3C;/<span class="hljs-name">script</span>></span>
    <span class="hljs-tag">&#x3C;/<span class="hljs-name">body</span>></span>
<span class="hljs-tag">&#x3C;/<span class="hljs-name">html</span>></span>
</code></pre>
<p>This initializes a canvas of 640x480 pixels and a WebGL context inside of it.
All the future draw operations from OpenGL will then be rendered into this canvas.</p>
<p>OpenGL is entirely procedural. Here we start by using <code>clearColor</code> which defines the color that will be used by all the future <code>clear</code> function calls.</p>
<p>Colors are defined using 4 floating-points between 0 and 1 that represents the RGBA components.
In other words, the color we are using here (<code>0, 0.5, 1, 1</code>) is equivalent to <code>rgba(0, 128, 255, 1.0)</code> (it's plain, opaque blue).</p>
<p><code>gl.clear</code> is our first draw operation: this resets anything that might preexist in our canvas, and uniformly applies our blue color onto it.</p>
<p>I will not explain the meaning of the <code>COLOR_BUFFER_BIT</code> argument in this article because it's out of scope, but you may read the technical details <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glClear.xhtml">here</a>.</p>
<p>If you try to display this page in a browser, you will see a plain, blue canvas.</p>
<h2>Defining a shape</h2>
<p>We will be drawing a single rectangle covering the screen.
The base element in OpenGL to draw shapes is the triangle, and every shape is made of it.</p>
<p>We will be using the <a href="https://en.wikipedia.org/wiki/Triangle_strip">triangle strip</a> method to represent our rectangle. It means that we have to define our triangles with a sequence of vertices, where all triangles are connected by one vertex.</p>
<p>As it make things easier to understand, here is a diagram.</p>
<p><img src="/assets/img/articles/2022-03-01-Introduction-To-GPUs-With-OpenGL/coordinates.svg" alt="Picture showing the coordinates and direction of the vertices of our rectangle"></p>
<p>The rendering area (our canvas) is bound to the coordinates <code>[-1 .. +1]</code> on both the X and Y axes and is centered to the origin <code>(0, 0)</code>. Shapes in OpenGL are represented using decimal numbers (floating points), not in pixels.</p>
<p>To draw our rectangle, we will define a first triangle <code>(ABC)</code>, then a second one <code>(CAD)</code>. Using a triangle strip method, we get the sequence of vertices <code>(ABCAD)</code>.</p>
<p>Let's define this with a bit of code.</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">const</span> positionBuffer = gl.<span class="hljs-title function_">createBuffer</span>();
gl.<span class="hljs-title function_">bindBuffer</span>(gl.<span class="hljs-property">ARRAY_BUFFER</span>, positionBuffer);
gl.<span class="hljs-title function_">bufferData</span>(gl.<span class="hljs-property">ARRAY_BUFFER</span>, <span class="hljs-keyword">new</span> <span class="hljs-title class_">Float32Array</span>([
    -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, <span class="hljs-comment">// A</span>
    +<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, <span class="hljs-comment">// B</span>
    +<span class="hljs-number">1</span>, +<span class="hljs-number">1</span>, <span class="hljs-comment">// C</span>
    -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, <span class="hljs-comment">// A</span>
    -<span class="hljs-number">1</span>, +<span class="hljs-number">1</span>, <span class="hljs-comment">// D</span>
]), gl.<span class="hljs-property">STATIC_DRAW</span>);
</code></pre>
<p>This creates a buffer array stored in the GPU's memory, and returns us a reference to it, as <code>positionBuffer</code>.</p>
<p><code>bindBuffer</code> changes the state of OpenGL so that the following instructions (including our subsequent call to <code>bufferData</code>) have to use <code>positionBuffer</code>.</p>
<p>Our rectangle is then defined as a Sequence of X and Y coordinates <code>(Xa, Ya, Xb, Yb...)</code> as 32 bits floating-point numbers and sent to this buffer in the GPU's memory.</p>
<p>I will not explain what the <code>ARRAY_BUFFER</code> and <code>STATIC_DRAW</code> constants means because it is irrelevant for this article, but you may find some documentation <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBindBuffer.xhtml">here</a> and <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBufferData.xhtml">here</a>.</p>
<p>Now we yet have to define lots of things, but for the sake of being consistent, I will be jumping early to what will be the very last instruction of our program:</p>
<pre><code class="hljs language-javascript">gl.<span class="hljs-title function_">drawArrays</span>(gl.<span class="hljs-property">TRIANGLE_STRIP</span>, <span class="hljs-number">0</span>, <span class="hljs-number">5</span>);
</code></pre>
<p>This instruction tells OpenGL to actually perform a draw call using the currently defined state and data.</p>
<p>The first argument is for the draw method (algorithm), and the next ones defines the range of the data to be used from our buffers.
In our case, we have 5 vertices that are defined from the index 0, but this can be handy to optimize complex applications.</p>
<h2>Types of shaders</h2>
<p>An OpenGL <em>program</em> is made of two shaders:</p>
<ul>
<li>The <strong>vertex shader</strong> is (commonly) executed once for every vertex we want to draw. It receives some <em>attributes</em> as input, computes the position of this vertex in space and returns it in a variable called <code>gl_Position</code>. It also defines some <em>varyings</em>.</li>
<li>The <strong>fragment shader</strong> is executed once for each pixel to be rendered. It receives some <em>varyings</em> as input, computes the color of this pixel and returns it in a variable called <code>gl_FragColor</code>.</li>
</ul>
<figure>
    <img src="/assets/img/articles/2022-03-01-Introduction-To-GPUs-With-OpenGL/pipeline.webp" alt="Rendering pipeline of OpenGL">
    <figcaption>
        Image by
        <a href="https://glumpy.github.io/modern-gl.html">
            Glumpy
        </a>
    </figcaption>
</figure>
<p>OpenGL automatically takes care for us of everything else in the flow below!</p>
<h2>Vertex shader</h2>
<p>Let's start by defining a basic vertex shader.</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">const</span> vertexShader = gl.<span class="hljs-title function_">createShader</span>(gl.<span class="hljs-property">VERTEX_SHADER</span>);
gl.<span class="hljs-title function_">shaderSource</span>(vertexShader, <span class="hljs-string">`
    attribute vec2 position;

    void main(void) {
        gl_Position = vec4(position, 0.0, 1.0);
    }
`</span>);
gl.<span class="hljs-title function_">compileShader</span>(vertexShader);

<span class="hljs-comment">// Error handling only</span>
<span class="hljs-keyword">if</span> (!gl.<span class="hljs-title function_">getShaderParameter</span>(vertexShader, gl.<span class="hljs-property">COMPILE_STATUS</span>)) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(gl.<span class="hljs-title function_">getShaderInfoLog</span>(vertexShader));
}
</code></pre>
<p>Here we are creating and compiling a vertex shader from it's source code (which is here hard-coded in a JavaScript string).
We first declare a <code>position</code> <em>attribute</em>. It is a <code>vec2</code> (vector of two floating-point numbers) and will receive the <code>(X, Y)</code> coordinates of each vertex in our rectangle. Since we have 5 vertices in our buffer, this shader will be executed 5 times by the GPU (once per vertex)! We can also expect all 5 instances of the shader to be executed in parallel, since a GPU have so many cores.</p>
<p><code>gl_Position</code> is always defined as a <code>vec4</code> (vector of 4 floating-point numbers), but why is that? The third member is for the Z coordinate, which we set at 0 because we only have a 2D shape here.
But otherwise we could indeed define a 3D shape by having a <code>vec3</code> position and adding the Z coordinate in our buffer.
The last argument is <code>W</code>, and this fourth dimension is used for <em>clipping</em>. You can read more about it <a href="https://www.haroldserrano.com/blog/what-is-clipping-in-opengl">here</a>.</p>
<p>By default, WebGL does not output errors in the browser's console, and that's the only thing the last 3 lines are doing for us.</p>
<p>ℹ️ Vectors can be easily combined in GLSL. <code>vec4(vec2(1, 2), 3, 4)</code> being equivalent to <code>vec4(1, 2, 3, 4)</code> (or even <code>vec4(vec3(1, 2, 3), 4)</code> for 3 dimensions).</p>
<h2>Fragment shader</h2>
<p>Our fragment shader will be even easier.</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">const</span> fragmentShader = gl.<span class="hljs-title function_">createShader</span>(gl.<span class="hljs-property">FRAGMENT_SHADER</span>);
gl.<span class="hljs-title function_">shaderSource</span>(fragmentShader, <span class="hljs-string">`
    void main(void) {
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }
`</span>);
gl.<span class="hljs-title function_">compileShader</span>(fragmentShader);

<span class="hljs-comment">// Error handling only</span>
<span class="hljs-keyword">if</span> (!gl.<span class="hljs-title function_">getShaderParameter</span>(fragmentShader, gl.<span class="hljs-property">COMPILE_STATUS</span>)) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(gl.<span class="hljs-title function_">getShaderInfoLog</span>(fragmentShader));
}
</code></pre>
<p>All we do here is telling OpenGL to fill every pixel in our rectangle with a red color.
The color is defined by a <code>vec4</code> of 4 RGBA components in <code>0.0..1.0</code>.</p>
<h2>Creating an OpenGL program</h2>
<p>Now that our shaders are created and compiled, we can create an OpenGL <em>program</em> by linking them together!</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">const</span> program = gl.<span class="hljs-title function_">createProgram</span>();
gl.<span class="hljs-title function_">attachShader</span>(program, vertexShader);
gl.<span class="hljs-title function_">attachShader</span>(program, fragmentShader);
gl.<span class="hljs-title function_">linkProgram</span>(program);
gl.<span class="hljs-title function_">useProgram</span>(program);
</code></pre>
<p>If it's not clear, you could compare this to building a binary executable using packages (shaders) compiled in the previous steps.</p>
<p>The last <code>useProgram</code> instruction tells OpenGL to set it's internal state so that all subsequent operations will be using this program.</p>
<p>ℹ️ A single application can declare and use multiple OpenGL programs. For example, it's common in a video game to have one program that renders the solid shapes, one program that renders the particles and one that renders the 2D interface (HUD/UI) on top of it.</p>
<h2>Putting it altogether</h2>
<p>Now that our program is created, we also need to tell it where the attribute values comes from!</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">const</span> positionAttribute = gl.<span class="hljs-title function_">getAttribLocation</span>(program, <span class="hljs-string">"position"</span>);
gl.<span class="hljs-title function_">enableVertexAttribArray</span>(positionAttribute);
gl.<span class="hljs-title function_">vertexAttribPointer</span>(positionAttribute, <span class="hljs-number">2</span>, gl.<span class="hljs-property">FLOAT</span>, <span class="hljs-literal">false</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
</code></pre>
<p>The first row allows us to get a reference to the <code>position</code> attribute in our OpenGL <em>program</em>.</p>
<p>By then using <code>enableVertexAttribArray</code>, we tell OpenGL that we want this attribute to point to the value of the vertex array (which we previously bound with <code>bindBuffer</code>).</p>
<p>The last call to <code>vertexAttribPointer</code> allows us to tell OpenGL about the structure of our data. The parameters means that we want each instance of the vertex shader to receive <code>2</code> elements with a <code>float</code> type from our buffer.
In other words, one <code>(X, Y)</code> pair from our array will be automatically bound to the components of our <code>vec2</code> (<code>position</code> attribute).</p>
<p>The last arguments (<code>false, 0, 0</code>) are not relevant here, but you can find more information about it <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glVertexAttribPointer.xhtml">here</a>,</p>
<figure style="width: 50%; margin: auto;">
    <img src="/assets/img/articles/2022-03-01-Introduction-To-GPUs-With-OpenGL/result_1.webp" alt="Result of the red rectangle covering all the canvas">
</figure>
<p>And we can now see a filled, red canvas. But this is boring, how are we supposed to know that we drew the rectangle properly?</p>
<p>We should reduce the size of the rectangle so that it does not take-up all the space. Let's see how we could do that.</p>
<h3>Reduce the size of our rectangle?</h3>
<p>By changing the X and Y values of each vertex to something smaller than 1, we could reduce the size of our rectangle.</p>
<p>That would indeed work in our case, but what if we were in a real application, and our rectangle were a 3D model with millions of vertices?</p>
<p>With this strategy, modifying the vertices would have to be done by the CPU (and the updated data sent again to the GPU). That would be too inefficient.</p>
<h3>Move the rectangle away?</h3>
<p>Let's remember what we wrote in our vertex shader.</p>
<pre><code class="hljs language-ini"><span class="hljs-attr">gl_Position</span> = vec4(position, <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>)<span class="hljs-comment">;</span>
</code></pre>
<p>If we changed the value of Z (here <code>0.0</code>) to make the rectangle far away from our viewpoint, surely it would appear smaller?</p>
<p>You can give it a try, and notice that Z does not actually do anything here.</p>
<p>For objects to appear far-away, we would need to define a perspective first.</p>
<figure>
    <img src="/assets/img/articles/2022-03-01-Introduction-To-GPUs-With-OpenGL/perspective.webp" alt="Image showing the difference of perspective">
    <figcaption>
        Image by
        <a href="https://glumpy.github.io/modern-gl.html">
            Glumpy
        </a>
    </figcaption>
</figure>
<p>Doing that would require to set a projection matrix, which I will keep out of the scope of this article.
You may however follow the tutorial on <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial">MDN</a> to learn more about it.</p>
<h3>Scale down the rectangle?</h3>
<p>There is a last option in our case: make the shader scale down the vertices of our rectangle.</p>
<p>While this may sound similar to the first option, it's a trivial operation for a GPU, and can be done very easily without any performance issue.</p>
<p>I also want to take this as an opportunity to introduce <em>uniforms</em>.</p>
<h2>Uniforms</h2>
<p>We said earlier that the vertex shader receives <em>attributes</em> while the fragment shader receives <em>varyings</em>. But there is a third category of inputs in OpenGL, called <em>uniforms</em>. Uniforms are very much like constants, except that the values are not hard-coded, but programmatically defined before the execution of the OpenGL program.</p>
<figure>
    <img src="/assets/img/articles/2022-03-01-Introduction-To-GPUs-With-OpenGL/variables.webp" alt="Diagram showing the different kinds of inputs in OpenGL">
    <figcaption>
        Image by
        <a href="https://sbfl.net/blog/2014/07/25/webgl-tutorials-with-dart-basics/">
            Subterranean Flower
        </a> (in Japanese)
    </figcaption>
</figure>
<p>The value of uniforms are always ... uniform among all instance of the vertex or fragment shaders (but can still be different for each instance of the OpenGL program itself.</p>
<p>Let's now modify our vertex shader:</p>
<pre><code class="hljs language-glsl">attribute vec2 position;

uniform float scale;

void main(void) {
    gl_Position = vec4(position * scale, 0.0, 1.0);
}
</code></pre>
<p>Our <code>scale</code> uniform will receive a scaling factor (for example <code>0.5</code>), which is then multiplied to the X and Y coordinates of each vertex.</p>
<p>Note: In GLSL, <code>(vec2(x, y) * a)</code> is equivalent to <code>vec2(x * a, y * a)</code>.</p>
<p>All remains to do is assign a single floating-point (<code>1f</code>: '1 float') value to this uniform:</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">const</span> scaleUniform = gl.<span class="hljs-title function_">getUniformLocation</span>(program, <span class="hljs-string">"scale"</span>);
gl.<span class="hljs-title function_">uniform1f</span>(scaleUniform, <span class="hljs-number">0.5</span>);
</code></pre>
<p>And voilà! You should now be able to see a red rectangle in the middle of the blue canvas!</p>
<figure style="width: 50%; margin: auto;">
    <img src="/assets/img/articles/2022-03-01-Introduction-To-GPUs-With-OpenGL/result_2.webp" alt="Screenshot showing our scaled-down rectangle">
</figure>
<h2>Gradient colors</h2>
<p>We are going to replace the red color with a gradient of different colors by using <em>varyings</em>.</p>
<p><em>Varyings</em> are values passed from the vertex shader to the fragment shader. While there is no direct mapping between a vertex and a pixel, OpenGL can automatically interpolate the values for us!</p>
<p><img src="/assets/img/articles/2022-03-01-Introduction-To-GPUs-With-OpenGL/varyings.svg" alt="Picture showing the interpolation of varyings between the vertex and fragment shaders"></p>
<p>This property of varyings is very useful, and applies to each member of a vector. In other words, if we assign a color to each vertex, the components will be interpolated for each pixel, thus creating a gradient.</p>
<p>This technique is commonly used to get and display a specific pixel from a texture, or to apply per-vertex <a href="https://en.wikipedia.org/wiki/Shading">shading</a>.</p>
<p>Let's have a look at our rectangle again.</p>
<p><img src="/assets/img/articles/2022-03-01-Introduction-To-GPUs-With-OpenGL/coordinates.svg" alt="Picture showing the coordinates and direction of the vertices of our rectangle"></p>
<p>We are going to set A to red, B to green, C to blue and D to yellow. The data structure is almost identical to our vertices, but we now have vectors of 3 values (R, G and B components between 0 and 1) for each vertex instead of two X and Y values.</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">const</span> colorBuffer = gl.<span class="hljs-title function_">createBuffer</span>();
gl.<span class="hljs-title function_">bindBuffer</span>(gl.<span class="hljs-property">ARRAY_BUFFER</span>, colorBuffer);
gl.<span class="hljs-title function_">bufferData</span>(gl.<span class="hljs-property">ARRAY_BUFFER</span>, <span class="hljs-keyword">new</span> <span class="hljs-title class_">Float32Array</span>([
    <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-comment">// A = red</span>
    <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-comment">// B = green</span>
    <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-comment">// C = blue</span>
    <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-comment">// A = red</span>
    <span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-comment">// D = yellow</span>
]), gl.<span class="hljs-property">STATIC_DRAW</span>);
<span class="hljs-keyword">const</span> colorAttribute = gl.<span class="hljs-title function_">getAttribLocation</span>(program, <span class="hljs-string">"color"</span>);
gl.<span class="hljs-title function_">enableVertexAttribArray</span>(colorAttribute);
gl.<span class="hljs-title function_">vertexAttribPointer</span>(colorAttribute, <span class="hljs-number">3</span>, gl.<span class="hljs-property">FLOAT</span>, <span class="hljs-literal">false</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
</code></pre>
<p>In our vertex shader, we receive the color as a <code>vec3</code> attribute, and assign it's value to a varying.</p>
<pre><code class="hljs language-glsl">attribute vec2 position;
attribute vec3 color;

varying mediump vec3 vColor;

uniform float scale;

void main(void) {
    gl_Position = vec4(position * scale, 0.0, 1.0);
    vColor = color;
}
</code></pre>
<p>Varyings must always specify a precision (<code>lowp</code>, <code>mediump</code>, <code>highp</code>). This controls the number of bits that will be used internally for each value. It can greatly impact the performance (depending on the complexity of the program and the hardware). But in our example, it does not matter, so I'm using a medium precision.</p>
<p>Since OpenGL does all the work for us, the fragment shader only adds an alpha channel (1 = opaque) and returns the color.</p>
<pre><code class="hljs language-glsl">varying mediump vec3 vColor;

void main(void) {
    gl_FragColor = vec4(vColor, 1.0);
}
</code></pre>
<p>You should now see our rectangle with smooth gradient colors like this:</p>
<p><img src="/assets/img/articles/2022-03-01-Introduction-To-GPUs-With-OpenGL/result_3.webp" alt="Screenshot of the final result of our example"></p>
<h2>Conclusion</h2>
<p>I tried to focus on what I consider to be the fundamentals to understand how all of this works.
There are many things I wish I could explain further, like animations, shading (light effects), particles, camera (viewpoint and projection matrices...), and more broadly the architecture necessary to handle this at large scale (like a video game or 3D engine) But this could not possibly fit in the format of a blog article.</p>
<p>You can see the complete result of this article <a href="/assets/img/articles/2022-03-01-Introduction-To-GPUs-With-OpenGL/example.html">here</a> and debug or experiment by yourself.</p>
<p>Also, you can have a look at the resources linked below if you want to explore further.</p>
<h2>Links and credits</h2>
<ul>
<li>An article from Nvidia about the <a href="https://developer.nvidia.com/blog/cuda-refresher-reviewing-the-origins-of-gpu-computing/">history of GPUs</a>.</li>
<li>Another <a href="https://glumpy.github.io/modern-gl.html">great article</a> from which I took the OpenGL pipeline and projections images.</li>
<li>You can find a more advanced WebGL tutorial on <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial">MDN</a>.</li>
<li><a href="https://webglreport.com/">webglreport.com</a> gives you some insights on what is supported by your device.</li>
</ul>
<p><em><a href="https://unsplash.com/photos/nIEHqGSymRU">Article Photo by Laura Ockel</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What is Quality Assurance in an Agile Process?]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/02/10/What-is-Quality-Assurance-in-an-Agile-Process</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/02/10/What-is-Quality-Assurance-in-an-Agile-Process</guid>
            <pubDate>Thu, 10 Feb 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>In this article we will have a look at testing in an Agile process, with reference to the ISTQB Foundation Level Agile Tester (*1) syllabus, and will be covering the following three topics:</p>
<ol>
<li>The differences from testing in a Waterfall process</li>
<li>Improving the efficiency in testing</li>
<li>The dilemma between Agile Testing and Independence</li>
</ol>
<p>*1 ISTQB (International Software Testing Qualifications Board) certification for Agile testing</p>
<h2>The differences from testing in a Waterfall process</h2>
<h4>Testing in a Waterfall process</h4>
<p>In a Waterfall process, each process of requirements analysis, design, development, and testing proceeds to the next process after the deliverables are fixed.</p>
<p>In terms of testing, the basic principle is to start component testing, follow up by integration testing, and upon completion begins system testing.</p>
<p>As shown in figure 1, there is a co-relation between each design-to-testing process. For example, once the Requirement Design is completed, then used to perform Acceptance Testing. The same goes for System Design to System Testing and so forth.</p>
<figure>
<img src="/assets/img/articles/2022-02-10-What-is-QA-in-an-Agile-Process/fig1_vmodel-en.webp">
<figcaption>Fig.1 V-model</figcaption>
</figure>
<p>The client can only confirm the product when it reaches the acceptance testing stage. Depending on the scale of the project, it is not uncommon for the entire process to take more than a year.</p>
<h3>Agile: testing is repeated in a short period of time.</h3>
<p>In Agile development, a working product is released every <strong>short development cycle, iteration,</strong> of a few weeks and is improved with feedback.</p>
<p>Testing is also conducted within each iteration. In order to complete the process from the test design to the test execution in a short period of time, <strong>high efficiency</strong> is required.</p>
<figure>
<img src="/assets/img/articles/2022-02-10-What-is-QA-in-an-Agile-Process/fig2_agile-model.webp">
<figcaption>Fig.2 Agile Model</figcaption>
</figure>
<h3>Test Levels for Agile Testing</h3>
<p>While Waterfall has clearly defined test levels for integration testing, system testing, etc., Agile testing is a little different. The table below is a summary of the syllabus description from the ISTQB Foundation Level Agile Tester with a few of my own interpretations.</p>









































<table><thead><tr><th>Test Level</th><th>Executor</th><th>Execution Timing</th><th>Execution Content.</th></tr></thead><tbody><tr><td>Unit Testing</td><td>Developer</td><td>In Iteration</td><td>Component Level Verification</td></tr><tr><td>Feature Validation Testing</td><td>Developer or QA</td><td>In Iteration</td><td>Confirmation that user story acceptance criteria are met</td></tr><tr><td>Feature Validation Testing</td><td>Developer, QA and Business Stakeholder</td><td>In Iteration</td><td>Verify that the feature is suitable for use</td></tr><tr><td>System testing</td><td>QA</td><td>Outside of iteration</td><td>Functional and non-functional testing with user stories as test basis(*2)</td></tr><tr><td>Acceptance testing</td><td>Users</td><td>Each iteration or after all iterations are completed</td><td>Alpha/Beta testing, user acceptance testing, etc.</td></tr></tbody></table>
<p>It is not necessary to implement all of the above without fail, but rather, to consider and plan the level of testing according to the needs of the project.</p>
<p>In some cases, <strong>regression testing</strong>(*3) may be conducted prior to a release by building up the test cases conducted in each iteration.</p>
<p>*2 Test basis: The documents or information used as the basis for test analysis and design.</p>
<p>*3 Regression testing: Testing to verify that changes to a part of a program do not have an unintended impact on the system.</p>
<h2>Improving the efficiency in testing</h2>
<h3>Exploratory testing: Test technique that can easily adapt to specification changes</h3>
<p>One of the "Twelve Principles of Agile Software" is as follows:</p>
<blockquote>
<p>"Welcome changing requirements, even late in development." - <a href="https://Agilemanifesto.org/iso/ja/principles.html">The principles behind the Agile Manifesto</a></p>
</blockquote>
<p>In Waterfall, specification changes in the late stages of the development cycle are unacceptable, but in an Agile development cycle, changes are acceptable as long as they increase the value of the product. This requires a testing strategy that can easily adapt to changes.</p>
<p>When specification changes occur in the late stage of development, we need to revise the test design and redo the tests. In the worst case scenario, all the test cases you have worked so hard to create may be wasted, resulting in unnecessary costs.</p>
<p><strong>Exploratory testing</strong> is an effective testing technique for such situations.</p>
<p>Traditional <strong>script testing</strong> uses test cases that specify test procedures, expectations, etc., whereas exploratory testing does not use test cases.</p>
<p>Exploratory testing, however, is slightly different from ad-hoc testing or monkey testing, which is perform blindly.
The table below summarizes the differences between script testing and exploratory testing.</p>























<table><thead><tr><th>Test technique</th><th>Test case</th><th>Pros</th><th>Cons</th></tr></thead><tbody><tr><td>Script testing</td><td>Yes</td><td>・Slight individual difference in results.<br>・Coverage is ensured.</td><td>・Large rework in case of specification change.</td></tr><tr><td>Exploratory testing</td><td>No</td><td>・Can detect bugs efficiently by experience.<br>・No man-hour required for test case creation.</td><td>・A certain level of skill required for executors.<br>・Difficult to ensure coverage.</td></tr></tbody></table>
<p>There are several approaches to exploratory testing. We, at TestarLab, often prepare a document called a <strong>test charter</strong>, which contains testing guidelines, and execute tests according to it. Using the test charter allows us to give hints to less experienced testers and also allows for quantitative analysis.</p>
<p>Exploratory testing seems to be a good fit for Agile development, but it also has its disadvantages, as shown in the table above. Therefore, it is necessary to distinguish between script testing and exploratory testing by using script testing for important functions.</p>
<h3>The necessity of test automation</h3>
<p>In a normal Agile development flow, regressions are more likely to occur due to frequent code changes; therefore, the importance of <strong>regression testing</strong> increases.</p>
<p>On many occasions, regression testing involves the usage of prior test cases in the past and is repeatedly executed. Thus, automated regression testing is more than likely to reduce the workload. Another benefit from automated test is that this allows QA to <strong>concentrate on new feature testing or other changes</strong>.</p>
<p>In addition, running automated tests at the timing of a pull request to a deployment, defects are usually found earlier.</p>
<p>In conclusion, knowledge about test automation is necessary for QA in an Agile team.</p>
<h2>The Dilemma between Agile Testing and Independence</h2>
<p>Generally speaking, the more independent a QA team is, the more efficiently bugs can be detected. In an environment where they are separated from the developers, they can evaluate the product from a more objective and unbiased point of view.</p>
<p>On the other hand, in Agile development, the QA team works in collaboration with business stakeholders and developers in an Agile team to improve the quality of the product's process. There is a trade-off for being an independent QA.</p>
<h3>The ISTQB approach</h3>
<p>How do you maintain independence while collaborating within an Agile team?</p>
<p>The ISTQB Foundation Level Agile Tester syllabus describes another approach to this dilemma:</p>
<blockquote>
<p>"A third option is to have an independent, separate test team where testers are assigned to Agile teams on a long-term basis, at the beginning of the project, allowing them to maintain their independence while gaining a good understanding of the product and strong relationships with other team members."</p>
</blockquote>
<p>As a division of roles, QA within an Agile team is responsible to understand the product and deepening the relationships with all team members, while the QA outside the Agile team is responsible for iteration-independent activities such as automation and non-functional testing.</p>
<figure>
<img src="/assets/img/articles/2022-02-10-What-is-QA-in-an-Agile-Process/fig3_qa-team-en.webp">
<figcaption>Fig.3 QA inside and outside of an Agile Team</figcaption>
</figure>
<p>As a QA in an Agile team, certain abilities are still required, such as being able to think objectively and critically in any situation, otherwise, having a QA in an Agile team would be meaningless.</p>
<h2>Summary</h2>
<p>As for the characteristics of Agile testing, I hope the following are clear to you through the article.</p>
<ul>
<li>Compared to Waterfall, there is no uniform definition, and project-specific planning is required</li>
<li>Use of exploratory testing and test automation improves efficiency</li>
<li>Be careful not to compromise the independence of QA</li>
</ul>
<p>Most of the projects in which TestarLab participates are Agile-type development projects. There are other development models besides Waterfall and Agile, but our goal is to provide optimal quality assurance in any development model.</p>
<h2>References</h2>
<ul>
<li><a href="https://www.istqb.org/certification-path-root/Agile-tester.html">ISTQB Foundation Level Agile Tester</a></li>
<li><a href="https://www.istqb.org/certification-path-root/foundation-level-2018.html">ISTQB Foundation Level 2018</a></li>
</ul>
<p>The images used in this article may not be reproduced without permission.</p>
<h2>What is "TESTAR LAB"?</h2>
<p>TestarLab is a new service specialized in testing the quality of application functions and services. We offer cost-optimized and speedy proposals based on our abundant human resources in 29 cities in 17 countries and our extensive network of domestic and overseas partner companies.
Testarlab provides an independent third-party verification team that optimizes the cost and detection efficiency for customers who are experiencing quality issues within their services, as well as customers who frequently upgrade versions, which makes testing costly and time-consuming.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/ETRPjvb0KM0">Patrick Perkins</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Solving gender inequality and saving lives with data driven approach]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/02/03/Solving-Gender-Inequality-with-data-driven-platform</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/02/03/Solving-Gender-Inequality-with-data-driven-platform</guid>
            <pubDate>Thu, 03 Feb 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>At the end of 2021, we attended Monstarlab's internal hackathon <a href="https://www.linkedin.com/showcase/monstarhacks">MonstarHacks</a>, where each team built a project on the theme of 3 of the <a href="https://sdgs.un.org/goals">17 UN Sustainable Development Goals</a>.</p>
<ol>
<li>Gender equality</li>
<li>Good Health and Well-being</li>
<li>Quality education</li>
</ol>
<p>Each of the goal is quite diverse, and there are lot things yet to be done under each of the goal. After hours of team discussions, we picked gender equality as our main focus, which is a very sensitive topic.</p>
<h3>Female Feticide</h3>
<p>It's shocking that, when most of the people hear this word, it seems like a third world problem, but surprisingly it's not a problem only in third world.</p>
<h5>Problem study</h5>
<p>In our study we found that:</p>
<ol>
<li>Female feticide is a very diverse and complex topic.</li>
<li>From place to place and time to time the cause changes drastically.</li>
<li>In some places it's becoming a problem, where historically we don’t have any data.</li>
<li>The whole internet seems to be a read-only platform for this particular issue.</li>
<li>It seems like a taboo topic inside some societies.</li>
<li>In some cases where <code>Right to have abortion</code> seems to be a symbol of gender-equality, then particular topic is somewhat conflicting.</li>
<li>Whereas this practice is not a disgrace to females, but also a threat to humanity, and collective initiatives are totally absent.</li>
</ol>
<h4>The proposed solution</h4>
<p>In the modern society from the nation election process to household oven everything is designed and improved based on data. In our proposed solution we want to gather as much as data possible. And in the whole process three major components are involved:</p>
<ol>
<li>Collection of data</li>
<li>Process of the data</li>
<li>Actionable to-dos</li>
</ol>
<h4>Step 1: Collection of data</h4>
<p>In the collection phase we will focus on in these key areas:</p>
<ul>
<li>From whom we will collect this data?</li>
<li>Is there a priority that should be set on the data?</li>
<li>How can we ensure data privacy and not reveal or expose the identities of the parties involved?</li>
<li>In what frequency we will collect data?</li>
</ul>
<h4>Step 2: Process of data</h4>
<p>In the data processing part we decided to make the outputs in the form of:</p>
<ul>
<li>Geological hotspot.</li>
<li>Clustering based on root causes.</li>
<li>Organisations that are currently working inside each of the locations (or if they even exists or not).</li>
<li>Historical time periods.</li>
</ul>
<p>That will help us to understand:</p>
<ul>
<li>External factors/events that can hugely affect this problem.</li>
<li>Historical patterns.</li>
</ul>
<h4>Step 3: Actions</h4>
<p>So the actions should be derived from data analysis. These valuable outputs should be passed to the government organisations, NGOs and other social welfare organisations.</p>
<h3>Proposed Project Title: Womb Equality (WE)</h3>
<h4>Backend Data Sources</h4>
<ul>
<li>Doctors (Share their experiences and raise alarm of a possible of forced abortion)</li>
<li>Victims (Can share mental conditions and reasons)</li>
<li>Witnesses (Someone who witnessed an forced abortion)</li>
<li>General Public (With their perception of this problem)</li>
</ul>
<p>The backend system architecture would look like this:</p>
<figure>
<img src="/assets/img/articles/2022-02-03-Solving-Gender-Inequality-with-data-driven-platform/backend.webp">
<figcaption>Backend System Architecture</figcaption>
</figure>
<h3>Frontend design idea</h3>
<p>We built a sample Android app and demonstrated how data will be collected.</p>
<figure>
<img src="/assets/img/articles/2022-02-03-Solving-Gender-Inequality-with-data-driven-platform/01.splash.webp">
<figcaption>1. App opening splash screen</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2022-02-03-Solving-Gender-Inequality-with-data-driven-platform/02.roles.webp">
<figcaption>2. Role selection</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2022-02-03-Solving-Gender-Inequality-with-data-driven-platform/03.campaigns.webp">
<figcaption>3. Recent posts and campaign news</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2022-02-03-Solving-Gender-Inequality-with-data-driven-platform/04.inputs.webp">
<figcaption>4. Data input Section</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2022-02-03-Solving-Gender-Inequality-with-data-driven-platform/05.submit.webp">
<figcaption>5. Post submit screen</figcaption>
</figure>
<figure>
<img src="/assets/img/articles/2022-02-03-Solving-Gender-Inequality-with-data-driven-platform/06.confirmation.webp">
<figcaption>6. Submit Confirmation</figcaption>
</figure>
<h3>Conclusion</h3>
<p>And finally let's be honest. Female foeticide is a century old problem, and it’s not going anywhere anytime soon. In the aftermath of Covid 19, history tells us that it will make a strong comeback. If we are not prepared, then It will struck us hard and wipe all the achievements that world has made in the last several decades.</p>
<p>No single platform or initiative can prevent and eradicate female feticide from this world, we need collective initiatives, fund and cooperation among all the countries.</p>
<p>Until this is done, the Gender Equality will always remain as a chapter inside the book and the dream will never be a reality.</p>
<p>Without any doubt, technology and data driven approach can greatly help us to achieve this goal.</p>
<p><em><a href="https://crealet.com/2021/06/we-invest-in-the-future/">Article Photo</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What you need to know when using Firestore]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/02/03/rdb-to-firestore</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/02/03/rdb-to-firestore</guid>
            <pubDate>Thu, 03 Feb 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Some years ago, when storing data, we mainly used RDBMS (Relational DataBase Management System) such as MySQL and PostgreSQL. But recently, we started using <a href="https://firebase.google.com/products/firestore">Firestore</a> more. Although the role of storing data is the same, the usage and design concept of Firestore is very different from RDBMS.</p>
<p>In this article, we will check the limitations of Firestore, and what are the differences from RDB.</p>
<h2>No aggregate function</h2>
<p>In RDB, it is relatively easy to perform <code>COUNT(*)</code> or <code>SUM(amount)</code> for hundreds or thousands of records.<br>
However, this is not practical with Firestore.</p>
<p>For example, suppose we have a screen that displays a list of 20 slips, each of which has 10 details attached to it.<br>
If we want to display the "total amount" in the list, we can get the result quickly in RDB by doing <code>SUM</code> in a subquery.</p>
<p>On the other hand, if we want to do the same thing with Firestore, we need to load 200 documents on the client-side and aggregate them as a "process". This causes a problem in terms of cost and performance.<br>
In such a case, we update the "total amount" of the slip data when adding or updating the details data, and refer only to the slip data when displaying it on the screen.</p>
<p>For other constraints as well, it is necessary to think in terms of creating and updating data with reading situations in mind at the time of writing data.</p>
<h2>Not good at searching</h2>
<p>To search with two or more conditions, we need to create an index.<br>
In addition, some queries cannot be executed even if any indexes are created.</p>
<blockquote>
<p>In a compound query, range (<code>&#x3C;</code>, <code>&#x3C;=</code>, <code>></code>, <code>>=</code>) and not equals (<code>!=</code>, <code>not-in</code>) comparisons must all filter on the same field.</p>
</blockquote>
<p><a href="https://firebase.google.cn/docs/firestore/query-data/queries?hl=en">Perform simple and compound queries in Cloud Firestore | Firebase Documentation</a></p>
<p>For example, we cannot do a range search on two columns, such as "Created after A, modified after B". Also, we cannot do a range search and sort with two different columns.</p>
<p>We need a workaround, such as changing the screen specification or using a different search column to perform an exact match search.</p>
<p>For example, in the above example, if we can change the specification to "search data that was created in a specific month and updated after B", we add a new column that is the year-month of created (the string "yyyyMM"). Therefore we can change one of the conditions to an exact match.</p>
<h2>No full text search</h2>
<p>If we want to do a full-text search, such as searching for posts that contain a specific string, it is common to use an external service such as Algolia.</p>
<blockquote>
<p>Cloud Firestore doesn't support native indexing or search for text fields in documents.
Additionally, downloading an entire collection to search for fields client-side isn't practical.</p>
</blockquote>
<p><a href="https://firebase.google.cn/docs/firestore/solutions/search?hl=en">Full-text search | Firebase Documentation</a></p>
<p>We can also use Firebase Extensions called "Search with Algolia" to seamlessly sync our Firestore data to Algolia.</p>
<h2>No "offset"</h2>
<p>In RDBMS, paging can perform by specifying "offset".<br>
In Firestore, "limit" can be specified, but "offset" cannot be specified.<br>
Therefore, it is difficult to support specifications such as loading by page number.</p>
<p>On the other hand, loading continuous data such as infinite scrolling can be easy.</p>
<h2>Cannot update or delete multiple data by conditions</h2>
<p>It is not possible to update all the flags of the data that match the condition or delete all the related data when the parent data was deleted.<br>
It is necessary to retrieve the data by conditions and then update or delete them individually.</p>
<p>There are functions such as transactions and batches, it is possible to update and delete data atomically.<br>
However, only 500 writes can be done in one transaction.</p>
<blockquote>
<p>Maximum number of writes that can be passed to a Commit operation or performed in a transaction 500</p>
</blockquote>
<p><a href="https://firebase.google.com/docs/firestore/quotas?hl=en#writes_and_transactions">Usage and limits | Firebase Documentation</a></p>
<h2>Cannot apply security rule to per-field</h2>
<p>When using RDBs, an API is usually implemented between the client and the DB.<br>
Therefore, it is common to implement read restrictions as "processes" on the API side.</p>
<p>In the case of Firestore, read restrictions are defined as a security rule, it is common for clients to refer directly to Firestore.</p>
<p>By setting the security rule, various restrictions can be applied to each collection.<br>
However, it is not possible to set restrictions such as "only certain fields can be viewed" within a single data collection.<br>
Therefore, if there are two types of user information, one for public and the other for private, it is recommended to manage them in separate collections.</p>
<h2>Cannot write frequently</h2>
<blockquote>
<p>Maximum sustained write rate to a document 1 per second
Sustaining a write rate above once per second increases latency and causes contention errors. This is not a hard limit, and you can surpass the limit in short bursts.</p>
</blockquote>
<p><a href="https://firebase.google.com/docs/firestore/quotas?hl=en#soft_limits">Usage and limits | Firebase Documentation</a></p>
<p>As mentioned earlier, aggregation by queries is not possible, so to display the number of "likes" on a post, it needs to be counted up as an integer.<br>
However, "likes" may be done for a single post from many users, it is likely to be limited to the soft limit above.<br>
An answer is available on the official page.</p>
<blockquote>
<p>To support more frequent counter updates, create a distributed counter.
Each counter is a document with a subcollection of "shards," and the value of the counter is the sum of the value of the shards.</p>
</blockquote>
<p><a href="https://firebase.google.com/docs/firestore/solutions/counters?hl=en">Distributed counters | Firebase Documentation</a></p>
<p>Simply put, by storing the data in 10 counter documents, the soft limit is reduced to about 1/10. And by summing them when loading, the loading cost is reduced.</p>
<h2>Summary</h2>
<p>Firestore has a lot of advantages, such as relatively low cost of operation and easy implementation of real-time change detection.</p>
<p>However, the direction it aims to take and the situations in which it excels are different from those of the RDBMSs.<br>
I think it is important to understand the limitations and use them in the right place.</p>
<p><em><a href="https://unsplash.com/photos/lRoX0shwjUQ">Article Photo by Jan Antonin Kolar</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Gender Equality Detection in Written Text]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/01/31/Gender-Equality-Detection-in-Written-Text</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/01/31/Gender-Equality-Detection-in-Written-Text</guid>
            <pubDate>Mon, 31 Jan 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h3>Gender is a social construct</h3>
<p>Gender and sex are two different things. From a very young age, we see different roles played by our father and mother. We see society treating a boy and a girl differently. Children are told not to play certain games or wear certain dresses based on their biological sex. A child learns to differentiate between a man and a woman and associate different gender roles with them. Some of those associations are biological truths, but a lot of them are false biases that the child inherits from the environment and society.</p>
<h3>Language plays a vital role</h3>
<p>Language shapes our thoughts. We think about things through how language means things. Language has a direct influence over how people of a certain language think. How we speak and what we read affects how we think and how we interpret the world around us. Most languages commonly use the male gender as default, so our collective identity is understood as masculine. Certain languages associate different levels of respect with a different gender.</p>
<h3>Let's see an example</h3>
<figure>
  <img src="/assets/img/articles/2022-01-31-Gender-Equality-Detection-in-Written-Text/image_1.webp">
</figure>
<h3>Reducing gender inequality by reducing gender bias in language</h3>
<p>Language is biased. And that bias has an impact on people. As there is an association between a language and thought processing of the language speaking people, reducing negative biases will create an equal society.</p>
<p>Reading and writing gender-inclusive content can have far-reaching effects in creating an inclusive world. Detecting gender bias in a language can help us to generate content that is not biased against a particular gender. We can use algorithms to detect and reduce gender biases on a large scale all over the internet.</p>
<h3>Educate about gender bias and inequality</h3>
<p>Educating the population is very very important. A huge number of people in our country, Bangladesh, have no idea about gender inequality. They are very used to seeing gender roles played generation after generation.</p>
<p>Information needs to be spread to everyone. Good content needs to be created. As everyone has a mobile phone nowadays, we can take advantage of the digital power we have to spread information and education. Creating an online platform in the form of a mobile app or web app will be most effective.</p>
<h3>Our hackathon solution</h3>
<h4>A cloud service to detect gender bias in text</h4>
<p>Our team participated in Monstarlab's hackathon, <a href="https://www.linkedin.com/showcase/monstarhacks">MonstarHacks</a>, with the idea to build a cloud service to detect gender biases in written text and fix it with gender-neutral words. Below are some of the roles the service is supposed to do:</p>
<ol>
<li>Detect gender-biased words</li>
<li>Detect explicit and implicit gender bias, gender stereotypes using NLP and ML techniques</li>
<li>Detect gender-related misinformation</li>
<li>Fix with correct word/sentence</li>
</ol>
<figure>
  <img src="/assets/img/articles/2022-01-31-Gender-Equality-Detection-in-Written-Text/image_2.webp">
</figure>
<h3>A platform to teach gender equality</h3>
<p>An online learning platform based on mobile/web to teach people about gender inequality, gender bias in language, gender-related common misconceptions, false gender stereotypes, etc. The platform also shows news/blogs on gender empowerment to inspire gender equality.</p>
<h3>The sky's the limit but we are constrained by time and resources in the hackathon</h3>
<p>Due to time and resource constraints, we could not build as much as we dreamed about our project. Below are the functionalities we built:</p>
<ol>
<li>A backend service to detect gender-biased words and suggest gender-inclusive ones</li>
<li>A proof of concept Google chrome extension that highlights all gendered words in any webpage</li>
<li>A mobile editor to write gender unbiased paragraphs using our backend service</li>
<li>A mobile app prototype showing bias check editor and a learning platform</li>
</ol>
<h3>Future Direction</h3>
<p>Our cloud bias detector service can easily be extended by adding NLP, ML modules to detect more complex gender bias, gender stereotypes, implicit gender biases. We can extend our chrome extension to be like full-fledged <a href="https://www.grammarly.com/">Grammarly</a> for gender bias chrome extension.</p>
<p>Header Photo by <a href="https://unsplash.com/@alex_marchenko">Alex Marchenko</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Predicting Maternal Morbidity using Machine Learning Techniques]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/01/31/Predicting-Maternal-Morbidity-using-Machine-Learning-Techniques</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/01/31/Predicting-Maternal-Morbidity-using-Machine-Learning-Techniques</guid>
            <pubDate>Mon, 31 Jan 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Machine learning (ML) is expected to make the hunt for new health care quicker, cheaper, and more effective. It could be taught to identify concerning trends quickly, while also improving its models over time and potentially proposing courses of action.</p>
<blockquote>
<h3>Highlights</h3>
<ul>
<li>The reduction of child mortality is reflected in several of the United Nations' Sustainable Development Goals and is a key indicator of human progress.</li>
<li>The UN expects that by 2030 countries will end preventable deaths of newborns and children under 5 years of age, with all countries aiming to reduce under‑5 mortality to at least as low as 25 per 1,000 live births.</li>
<li>Parallel to the notion of child mortality is of course Maternal Mortality, which accounts for 295 000 deaths during and following pregnancy and childbirth (as of 2017). The vast majority of these deaths (94%) occurred in low-resource settings, and most could have been prevented.</li>
</ul>
</blockquote>
<p>ML is a computational field that over the last decades has been constantly showing its potential and gained relevance and importance in multiple disciplines, one being Health Care. There is a large number of scientific studies demonstrating and showing how ML can contribute to significantly improving aspects related to the health and wellbeing of groups and individuals. Ending preventable maternal death still remains at the top of the global agenda. ML can be trained to interpret and learn from real-time data such as fetal heart rate signals, contractions, and vital indicators. Mothers at risk of morbidity during the intrapartum and postpartum periods might be diagnosed by applying predictive analytics using ML.</p>
<h3>Introducing Maa</h3>
<figure>
  <img src="/assets/img/articles/2022-01-31-Predicting-Maternal-Morbidity-using-Machine-Learning-Techniques/logo.webp">
  <figcaption style="text-align: center;">
  	Maa Logo
  </figcaption>
</figure>
<p>As part of the <a href="https://www.linkedin.com/showcase/monstarhacks">MonstarHacks</a> 2021, our team decided to tackle the <a href="https://sdgs.un.org/goals/goal3">3rd UN Sustainable Development Goal - Good Health and Well-being</a>. Maa is the One-Stop service for Pregnancy and Childbirth. It is responsible for collecting health records and diagnosis reports from patients using Mobile apps which will then be analyzed using ML to take any preventive measures. In case of any critical health condition, Maa will suggest what necessary steps should be taken and even provide medical support.</p>
<figure>
  <div class="embed-container">
      <iframe
          src="https://www.youtube.com/embed/FmS3YCkX6qM"
          width="700"
          height="480"
          frameborder="0"
          allowfullscreen="">
      </iframe>
    </div>
    
  <figcaption style="text-align: center;">
  	Maa iOS App Prototype
  </figcaption>
</figure>
<h3>Overview</h3>
<p>Our system is aiming to play a vital role for women to receive health care before, during, and after pregnancy to decrease the risk of pregnancy complications. To maintain the service properly and keep the chain stable we need to monitor the system closely. Hence, the admin portal has been created with some key features such as:</p>
<ul>
<li>View the total number of patients, nurses (active and inactive), appointments, ambulances, and whatever else is needed.</li>
<li>Submit medical reports for the patients.</li>
<li>View and compare various types of data by date, time, and year.</li>
<li>Rewarding nurses based on their performances (e.g. nurses of the month).</li>
<li>Chat option with patients.</li>
</ul>
<p>It's hard to come up with all the necessary features at the very beginning of the development stage. As of right now, we have implemented the data visualization dashboards and system reports to get a high-level overview.</p>
<h3>Where we are now</h3>
<p>We have built a fully functional and deployed a REST API Service of “Fetal Health Predicting” to facilitate Maa app’s Prediction and Assistance features. In the mobile app, an expecting mother will fill the required fields (22 parameters of her cardiotocography result) and a prediction of the fetal health as “Normal”, “Suspicious” or “Pathological” will be returned by the Prediction API with ~95% accuracy.</p>
<p>Our dataset had 22 columns. 21 among those columns were various features of the <a href="https://en.wikipedia.org/wiki/Cardiotocography">Cardiotocography</a> result. The last one indicates the fetal health for the row values. Fetal health 1.0 means Normal, 2.0 means Suspicious, 3.0 means Pathological.</p>
<p>We did some simple data analysis on the dataset to find the mean, standard deviation, 1st quartile, and fetal health distribution analysis. We then found out the count plot of targets indicates an imbalance in data. This is a case that tends to provide misleading classification accuracy.</p>
<figure>
  <img src="/assets/img/articles/2022-01-31-Predicting-Maternal-Morbidity-using-Machine-Learning-Techniques/image_1.webp">
  <figcaption style="text-align: center;">
  	Plot indicating an imbalance in data
  </figcaption>
</figure>
<p>In this kind of scenario, the performance measures that would provide better insights are :</p>
<ul>
<li>Precision</li>
<li>F1 Score</li>
<li>Confusion Matrix</li>
<li>Recall</li>
</ul>
<p>Let's evaluate the result of the correlation matrix. Which is as follows:</p>
<figure>
  <img src="/assets/img/articles/2022-01-31-Predicting-Maternal-Morbidity-using-Machine-Learning-Techniques/image_2.webp">
  <figcaption style="text-align: center;">
  	Correlation matrix
  </figcaption>
</figure>
<p>Considering the results of our data analysis we chose 4 algorithms that might be the best fit to gain higher accuracy to predict fetal health better.
Then we ran these algorithms on our dataset to see which algorithms give us the best accuracy.</p>
<p>The result is as follows:</p>
<pre><code class="hljs language-yaml"><span class="hljs-attr">Logistic Regression:</span> <span class="hljs-number">0.897170</span>
<span class="hljs-attr">Decision Tree:</span> <span class="hljs-number">0.916683</span>
<span class="hljs-attr">RandomForest:</span> <span class="hljs-number">0.940205</span>
<span class="hljs-attr">SVC:</span> <span class="hljs-number">0.906594</span>
</code></pre>
<p>As Random Forest was the clear winner we trained a model using this algorithm.
Then we have converted the ML model into REST API Service using python-based framework <a href="https://fastapi.tiangolo.com/">FastAPI</a> and deployed it to <a href="https://www.heroku.com/">Heroku</a>.
Finally, we developed an iOS Application that consumes the API and shows results to the end-users.</p>
<p>We used React.js and Typescript with Ant design to build our dashboard. Since Ant Design provides us with a variety of UI components, we can enhance our web applications and keep improving our user experience. To visualize our data with a graph we used React Chart Js. The react components come with <a href="https://www.chartjs.org/">chart.js</a> and the most popular charting library.
We deployed our react app to <a href="https://www.netlify.com/">Netlify</a> as it is very convenient and easy to deploy any <code>create-react-app</code> with just a single click.</p>
<p>The dashboard shows us how the system works by displaying various metrics. Due to a lack of health workers and data, the dashboard allows us to know whether we are on track to achieve our goal. And it improves monitoring in a resource-constrained environment.</p>
<figure>
  <img src="/assets/img/articles/2022-01-31-Predicting-Maternal-Morbidity-using-Machine-Learning-Techniques/image_3.webp">
  <figcaption style="text-align: center;">
  	Maa system design
  </figcaption>
</figure>
<h3>Extensible Learnings</h3>
<p>Along the way, we went through different challenges and learned numerous new frameworks and tools such as:</p>
<ul>
<li><strong>Deploy API to Heroku</strong></li>
<li><strong>Hands-on Machine Learning</strong></li>
<li><strong>Converting an ML model to Python API</strong></li>
<li><strong>ML model evaluation using Confusion Matrix</strong></li>
<li><strong>Jupyter</strong></li>
<li><strong>FastAPI</strong></li>
<li><strong>CareKit</strong></li>
<li><strong>HealthKit</strong></li>
<li><strong>FHIRModels</strong></li>
</ul>
<p>One of the challenges worth mentioning was to train the ML model. It took over nine hours to train the model to work with. But eventually, we did it. We have taken some missteps and hopefully, we learned a lot from those experiences.</p>
<h3>Information is Key</h3>
<p>Patients' health data and medical records are invaluable for predicting maternal morbidity. With predictive analytics and ML, you’re able to unlock a new world of possibilities for reducing data rates related to pregnancy, and help better tailor severities and also reduce the treatment cost.</p>
<blockquote>
<p>Today the greatest risk of global catastrophe does not look like nuclear weapons or war. Instead, If anything kills over 10 million people in the next few decades, it is most likely to be a highly infectious virus rather than a war. - Bill Gates</p>
</blockquote>
<p>Even in the pinnacle of science and technology we are trembled by pandemics like Covid-19. One of the key reasons is the lack of relevant information. We can overcome this scarcity by utilizing ML based health care systems to simulate different environments. Covid-19 is an early warning, a wake-up call, to get ready. If we do not start now, it will be too late to get ready for the next epidemic.</p>
<h3>Closing Thoughts</h3>
<p>ML simplifies the lives of patients, doctors, and hospital administrators by performing tasks that are typically done by humans, but in less time and at a fraction of the cost. ML-based health care can help to improve the universal provision of health services, especially in the most disadvantaged areas, even during pandemics.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/typEuT_5Tzc">Eduardo Goody</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[UITableView DataSource Prefetching]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/01/14/TableView-DataSource-Prefetching</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/01/14/TableView-DataSource-Prefetching</guid>
            <pubDate>Fri, 14 Jan 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Apple has introduced an API for prefetching the data for a <em>UITableView</em> or <em>UICollectionView</em> in iOS 10. This is a short story about how to implement the <em>UITableViewDataSourcePrefetching</em> protocol. Let's learn how to make our apps buttery smooth, richer, and faster by using new features in <em>UITableView</em> and its sibling, <em>UICollectionView</em>.</p>
<h3>Overview</h3>
<p>To implement prefetching, we conform to the <em>UITableViewDataSourcePrefetching</em> protocol in our ViewController, just like <em>UITableViewDataSource</em> and <em>UITableViewDelegate</em>. That enables UITableView’s data source to begin loading data for cells before <em>tableView(_:cellForRowAt:)</em> data source method is called.</p>
<h3>Getting Started</h3>
<p>Set your ViewController to TableView prefetch datasource</p>
<pre><code class="hljs language-swift">tableView.prefetchDataSource <span class="hljs-operator">=</span> <span class="hljs-keyword">self</span>
</code></pre>
<p>Initiate asynchronous loading of the data required for the cells at the specified index paths in your implementation of <em>tableView(_:prefetchRowsAt:)</em></p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">tableView</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">tableView</span>: <span class="hljs-type">UITableView</span>, <span class="hljs-params">prefetchRowsAt</span> <span class="hljs-params">indexPaths</span>: [<span class="hljs-type">IndexPath</span>]) {
	<span class="hljs-keyword">for</span> indexPath <span class="hljs-keyword">in</span> indexPaths {
		<span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-keyword">_</span> <span class="hljs-operator">=</span> loadingOperations[indexPath] { <span class="hljs-keyword">return</span> }
		<span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> dataLoader <span class="hljs-operator">=</span> dataStore.loadImage(at: indexPath.row) {
			loadingQueue.addOperation(dataLoader)
			loadingOperations[indexPath] <span class="hljs-operator">=</span> dataLoader
		}
	}
}
</code></pre>
<p>Cancel pending data load operations when the TableView informs you that the data is no longer required in the <em>tableView(_:cancelPrefetchingForRowsAt:)</em> method</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">tableView</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">tableView</span>: <span class="hljs-type">UITableView</span>, <span class="hljs-params">cancelPrefetchingForRowsAt</span> <span class="hljs-params">indexPaths</span>: [<span class="hljs-type">IndexPath</span>]) {
	<span class="hljs-keyword">for</span> indexPath <span class="hljs-keyword">in</span> indexPaths {
		<span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> dataLoader <span class="hljs-operator">=</span> loadingOperations[indexPath] {
			dataLoader.cancel()
			loadingOperations.removeValue(forKey: indexPath)
		}
	}
}
</code></pre>
<h3>Loading Data Asynchronously</h3>
<p>Unlike <em>tableView(_:cellForRowAt:)</em>, the <em>tableView(_:prefetchRowsAt:)</em> method is not necessarily called for every cell in the TableView. It is called for cells that are not visible on the screen. Implementation of <em>tableView(_:cellForRowAt:)</em>, therefore, must be able to handle the following potential situations</p>
<ul>
<li>Data has been loaded via the prefetch request and is ready to be displayed.</li>
</ul>
<pre><code class="hljs language-swift"><span class="hljs-operator">...</span>  
<span class="hljs-comment">// Has the data already been loaded?  </span>
<span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> image <span class="hljs-operator">=</span> dataLoader.image {
	cell.updateAppearanceFor(image)
	loadingOperations.removeValue(forKey: indexPath)  
}  
<span class="hljs-operator">...</span>
</code></pre>
<ul>
<li>Data is currently being prefetched but is not yet available.</li>
</ul>
<pre><code class="hljs language-swift"><span class="hljs-operator">...</span>  
<span class="hljs-keyword">else</span> {
	<span class="hljs-comment">// No data loaded yet, so add the completion closure to update the cell once the data arrives</span>
	dataLoader.loadingCompleteHandler <span class="hljs-operator">=</span> updateCellClosure  
}  
<span class="hljs-operator">...</span>
</code></pre>
<ul>
<li>Data has not yet been requested.</li>
</ul>
<pre><code class="hljs language-swift"><span class="hljs-operator">...</span>
<span class="hljs-comment">// Need to create a data loaded for this index path  </span>
<span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> dataLoader <span class="hljs-operator">=</span> dataStore.loadImage(at: indexPath.row) {
	<span class="hljs-comment">// Provide the completion closure, and kick off the loading operation</span>
	dataLoader.loadingCompleteHandler <span class="hljs-operator">=</span> updateCellClosure
	loadingQueue.addOperation(dataLoader)
	loadingOperations\[indexPath\] <span class="hljs-operator">=</span> dataLoader  
}
<span class="hljs-operator">...</span>
</code></pre>
<p>To handle all of these situations Operation is used to load the data for each row. We create the Operation object and store it in the prefetch method. The data source method can then either retrieve the operation and result or create a new operation if doesn’t exist.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">class</span> <span class="hljs-title class_">DataLoadOperation</span>: <span class="hljs-title class_">Operation</span> {
	<span class="hljs-keyword">var</span> image: <span class="hljs-type">UIImage</span>?
	<span class="hljs-keyword">var</span> loadingCompleteHandler: ((<span class="hljs-type">UIImage</span>?) -> ())<span class="hljs-operator">?</span>
	<span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> imageModel: <span class="hljs-type">ImageModel</span>

	<span class="hljs-keyword">init</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">imageModel</span>: <span class="hljs-type">ImageModel</span>) {
		<span class="hljs-keyword">self</span>.imageModel <span class="hljs-operator">=</span> imageModel
	}

	<span class="hljs-keyword">override</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">main</span>() {
		<span class="hljs-keyword">if</span> isCancelled { <span class="hljs-keyword">return</span> }
		<span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> url <span class="hljs-operator">=</span> imageModel.url <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> }
		downloadImageFrom(url) { (image) <span class="hljs-keyword">in</span>
			<span class="hljs-type">DispatchQueue</span>.main.async() { [<span class="hljs-keyword">weak</span> <span class="hljs-keyword">self</span>] <span class="hljs-keyword">in</span>
				<span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> `self` <span class="hljs-operator">=</span> <span class="hljs-keyword">self</span> <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> }
				<span class="hljs-keyword">if</span> <span class="hljs-keyword">self</span>.isCancelled { <span class="hljs-keyword">return</span> }
				<span class="hljs-keyword">self</span>.image <span class="hljs-operator">=</span> image
				<span class="hljs-keyword">self</span>.loadingCompleteHandler<span class="hljs-operator">?</span>(<span class="hljs-keyword">self</span>.image)
			}
		}
	}
}
</code></pre>
<figure>
  <div class="embed-container">
      <iframe
          src="https://www.youtube.com/embed/aD1EKjRoDks"
          width="700"
          height="480"
          frameborder="0"
          allowfullscreen="">
      </iframe>
    </div>
    
  <figcaption style="text-align: center;">
  	Seamless Prefetching...!!!
  </figcaption>
</figure>
<h3>Conclusion</h3>
<p>I hope this blog helped you understand how to implement the prefetching protocol. I tried to be very brief and focused. If you want to know more about it, I encourage you to <a href="https://developer.apple.com/videos/play/wwdc2016/219/">watch Apple’s WWDC session</a> on the prefetching protocol.</p>
<p>👑 <a href="https://github.com/rokon-mlbd/TableViewDataSourcePrefetching">KEEP CALM AND HERE IS MY CODE</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Intro to AR Foundation - Cross-Platform AR Application Development]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/01/04/Cross-Platform-AR-Development</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/01/04/Cross-Platform-AR-Development</guid>
            <pubDate>Tue, 04 Jan 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><strong>I am glad to participate in the first edition of the <a href="https://www.linkedin.com/showcase/monstarhacks">MonstarHacks</a> hackathon organized by Monstarlab. In this event, I made an Augmented Reality Panda Demo, and I will take this opportunity to introduce ARFoundation to you and take you through a small AR Demo.</strong></p>
<h2>About ARFoundation</h2>
<p>In 2017, Apple and Google launched their respective AR development SDK toolkits, ARKit and ARCore, corresponding to AR development for iOS and Android platforms respectively. ARFoundation platform architecture is based on ARKit and ARCore, and its purpose is to use Unity's cross-platform capability to build ARFoundation. ARFoundation's goal is not limited to ARKit and ARCore, but to build a unified and open AR development platform.</p>
<p><strong>ARFoundation operating principle</strong>
<img src="/assets/img/articles/2022-01-04-Cross-Platform-AR-Development/image01.webp" alt=""></p>
<p><strong>Features supported by ARFoundation</strong>
<img src="/assets/img/articles/2022-01-04-Cross-Platform-AR-Development/image02.webp" alt=""></p>
<p><strong>In this tutorial, we will build a simple app to preview a 3D model in AR.</strong></p>
<h2>Preparation</h2>
<ol>
<li>Download <a href="https://unity.com/download">Unity 2020.3.14</a></li>
<li>An Android device that supports Google ARCore</li>
</ol>
<h2>Getting Started</h2>
<ol>
<li>
<p>Open Unity Hub and create an empty Unity project
<img src="/assets/img/articles/2022-01-04-Cross-Platform-AR-Development/image03.webp" alt=""></p>
</li>
<li>
<p>Select Window-> Package Manager in the toolbar
<img src="/assets/img/articles/2022-01-04-Cross-Platform-AR-Development/image04.webp" alt=""></p>
</li>
<li>
<p>Select Unity Registry in Package Manager
<img src="/assets/img/articles/2022-01-04-Cross-Platform-AR-Development/image05.webp" alt=""></p>
</li>
<li>
<p>Find the following Package and install it
<img src="/assets/img/articles/2022-01-04-Cross-Platform-AR-Development/image06.webp" alt=""></p>
</li>
<li>
<p>In the right Hierarchy view, delete the original MainCamera, right-click on the blank space and add ARSession and ARSessionOrigin respectively.
<img src="/assets/img/articles/2022-01-04-Cross-Platform-AR-Development/image07.webp" alt=""><img src="/assets/img/articles/2022-01-04-Cross-Platform-AR-Development/image08.webp" alt=""></p>
</li>
<li>
<p>Create a Cube, first set the scale of the cube to (0.2, 0.2, 0.2), then drag and drop the cube to the project view, set it to Prefab, and finally delete the Cube in Hierarchy
<img src="/assets/img/articles/2022-01-04-Cross-Platform-AR-Development/image09.webp" alt="">
Create another ARPlane and set it to Prefab, then delete the ARPlane from Hierarchy
<img src="/assets/img/articles/2022-01-04-Cross-Platform-AR-Development/image10.webp" alt=""></p>
</li>
<li>
<p>Right-click the blank space of Project view to create a C# script named PlaceOnPlane. After successful creation, double-click the script to add the following code and save it.</p>
</li>
</ol>
<pre><code class="hljs language-csharp"><span class="hljs-keyword">using</span> System.Collections.Generic;
<span class="hljs-keyword">using</span> UnityEngine;
<span class="hljs-keyword">using</span> UnityEngine.XR.ARFoundation;
<span class="hljs-keyword">using</span> UnityEngine.XR.ARSubsystems;


[<span class="hljs-meta">RequireComponent(typeof(ARRaycastManager))</span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">PlaceOnPlane</span> : <span class="hljs-title">MonoBehaviour</span>
{
   [<span class="hljs-meta">SerializeField</span>]
   [<span class="hljs-meta">Tooltip(<span class="hljs-string">"Instantiates this prefab on a plane at the touch location."</span>)</span>]
   GameObject m_PlacedPrefab;

   <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&#x3C;summary></span></span>
   <span class="hljs-comment"><span class="hljs-doctag">///</span> The prefab to instantiate on touch.</span>
   <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&#x3C;/summary></span></span>
   <span class="hljs-keyword">public</span> GameObject placedPrefab
   {
       <span class="hljs-keyword">get</span> { <span class="hljs-keyword">return</span> m_PlacedPrefab; }
       <span class="hljs-keyword">set</span> { m_PlacedPrefab = <span class="hljs-keyword">value</span>; }
   }

   <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&#x3C;summary></span></span>
   <span class="hljs-comment"><span class="hljs-doctag">///</span> The object instantiated as a result of a successful raycast intersection with a plane.</span>
   <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&#x3C;/summary></span></span>
   <span class="hljs-keyword">public</span> GameObject spawnedObject { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">private</span> <span class="hljs-keyword">set</span>; }

   <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Awake</span>()</span>
   {
       m_RaycastManager = GetComponent&#x3C;ARRaycastManager>();
   }

   <span class="hljs-function"><span class="hljs-built_in">bool</span> <span class="hljs-title">TryGetTouchPosition</span>(<span class="hljs-params"><span class="hljs-keyword">out</span> Vector2 touchPosition</span>)</span>
   {
       <span class="hljs-keyword">if</span> (Input.touchCount > <span class="hljs-number">0</span>)
       {
           touchPosition = Input.GetTouch(<span class="hljs-number">0</span>).position;
           <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
       }

       touchPosition = <span class="hljs-literal">default</span>;
       <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
   }

   <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Update</span>()</span>
   {
       <span class="hljs-keyword">if</span> (!TryGetTouchPosition(<span class="hljs-keyword">out</span> Vector2 touchPosition))
           <span class="hljs-keyword">return</span>;

       <span class="hljs-keyword">if</span> (m_RaycastManager.Raycast(touchPosition, s_Hits, TrackableType.PlaneWithinPolygon))
       {
           <span class="hljs-comment">// Raycast hits are sorted by distance, so the first one</span>
           <span class="hljs-comment">// will be the closest hit.</span>
           <span class="hljs-keyword">var</span> hitPose = s_Hits[<span class="hljs-number">0</span>].pose;

           <span class="hljs-keyword">if</span> (spawnedObject == <span class="hljs-literal">null</span>)
           {
               spawnedObject = Instantiate(m_PlacedPrefab, hitPose.position, hitPose.rotation);
           }
           <span class="hljs-keyword">else</span>
           {
               spawnedObject.transform.position = hitPose.position;
           }
       }
   }

   <span class="hljs-keyword">static</span> List&#x3C;ARRaycastHit> s_Hits = <span class="hljs-keyword">new</span> List&#x3C;ARRaycastHit>();

   ARRaycastManager m_RaycastManager;
}

</code></pre>
<ol start="8">
<li>Click ARSessionOrigin component in Hierarchy view, add ARPlaneManager, ARRaycastManager and the PlaceOnPlane script we created via Add Component button in Inspector view. And drag the cube prefab we created to the PlaceOnPlane script and then drag the ARPlane Prefab to the ARPlaneManager script, as shown in the figure.
<img src="/assets/img/articles/2022-01-04-Cross-Platform-AR-Development/image11.webp" alt=""></li>
</ol>
<h2>Now we finished editing, let's start building the APK next.</h2>
<ol>
<li>
<p>Open File-> Build Settings window, click Add Open Scenes button to add scene, Platform select Android, and finally click Switch Platform to switch platforms
<img src="/assets/img/articles/2022-01-04-Cross-Platform-AR-Development/image12.webp" alt="">
Click Player Settings and change the Company Name and Product Name.
<img src="/assets/img/articles/2022-01-04-Cross-Platform-AR-Development/image13.webp" alt="">
Modify Other setting options
<img src="/assets/img/articles/2022-01-04-Cross-Platform-AR-Development/image14.webp" alt=""></p>
</li>
<li>
<p>After the modification, take out the prepared Android phone, open the USB debugging mode, connect to the computer, and click Build And Run in the Build Settings window to start building the APK.
<img src="/assets/img/articles/2022-01-04-Cross-Platform-AR-Development/image15.webp" alt=""></p>
</li>
<li>
<p>When the build is completed, check the connected Android device, find the App which is installed automatically, click Run and you will be able to see the AR Demo we made.
<div class="embed-container">
      <iframe
          src="https://www.youtube.com/embed/k_8XoIhzs9U"
          width="700"
          height="480"
          frameborder="0"
          allowfullscreen="">
      </iframe>
    </div>
    
<strong>Now, our ARDemo can run successfully, we can identify the plane and place a cube on the
plane and can move the position of the cube.</strong></p>
</li>
</ol>
<h2>Summary</h2>
<p><strong>AR Foundation has a lot of functions, and we just used the relatively basic functions this time, surely you can build very interesting apps if you would like.</strong></p>
<h2>References</h2>
<ol>
<li><a href="https://blogs.unity3d.com/2018/12/18/unitys-handheld-ar-ecosystem-ar-foundation-arcore-and-arkit/">Unity’s Handheld AR Ecosystem</a></li>
<li><a href="https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@4.2/manual/index.html">ARFoundation</a></li>
</ol>
<p>Article photo by <a href="https://developer.unity.cn/projects/5c32ddfbedbc2a0020e732b9">Unity</a> &#x26; <a href="https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@4.2/manual/index.html">ARFoundation</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[AI in automated UI testing]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2022/01/03/AI-in-automated-UI-testing</link>
            <guid>https://engineering.monstar-lab.com/en/post/2022/01/03/AI-in-automated-UI-testing</guid>
            <pubDate>Mon, 03 Jan 2022 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>In 2000s, when I started working as a QA Engineer, there weren't good tools for UI automated testing, I didn't have enough experience and developers didn't think about automation.</p>
<p>The first tool I tried was TestComplete. We used Infragistics UI controls for our applications and TestComplete wasn't able to locate them properly. It performed clicks by coordinates and if the test was recorded on the one device they might fail on devices with another screen resolution.</p>
<p>We made the decision to test our applications manually.</p>
<p>I started working for another company (Sitecore).</p>
<p>A QA Engineer with good knowledge of programming languages joined our QA team. In cooperation with one of our best FE developers he built a framework for UI testing. The framework was based on WatiN (open-source tool). Together with unit and integration tests (QA Engineers took part in the creation too), this gave us good savings, and regression testing was faster.</p>
<p>However, there was still a lot of manual work.</p>
<p>After maternity leave, I went to work for another company.</p>
<p>Before that, I tried Selenium for self-study, but in Caspio I saw for the first time how it is used in real conditions, on real projects.</p>
<p>There was an Automation Team, they automated tests and it saved time for regression testing.</p>
<p>Also, we automated tests for the new features inside our Scrum teams:</p>
<ul>
<li>unit tests prevented commiting "bad" code to the repository</li>
<li>integration tests helped prevented deploying "bad" code to our servers</li>
<li>UI auto tests helped us understand if the build may be accepted for the testing or should be reverted</li>
<li>Additionally UI auto tests helped us run end-to-end scenarios faster and receive the results faster</li>
</ul>
<p>I fell in love with Selenium: its capabilities and community.</p>
<p>One of the challenges in automated testing is fragmentation in the testing ecosystem. Several different tools are used for creating tests, running them, creating reports etc.</p>
<p>Recently, there has been a confrontation between open-source tools and full-featured test automation platforms.</p>
<p>I was asked to compare two of the platforms: Testim and TestProject.</p>
<p>There are some differences between them:</p>
<ul>
<li><strong>TestProject</strong> is free - <strong>Testim</strong> has free plan but there are several paid plans</li>
<li><strong>TestProject</strong> can be used for automated testing of web and mobile applications - <strong>Testim</strong> is for web applications only</li>
<li><strong>Testim</strong> uses their own Grid for running the tests</li>
<li><strong>TestProject</strong> allows creating addons that can be used by anyone who has account</li>
</ul>
<p>The list isn't full.</p>
<p>There is one common thing - AI (Artificial Intelligence).</p>
<p>Testim:</p>
<blockquote>
<p><strong>AI learning</strong>
Smart <ins>Locators</ins> compare confidence scores from current to prior runs. When elements change, the <ins>locators</ins> improve and match your app.</p>
<p><strong>Control locators</strong>
Smart <ins>Locator</ins> properties are visible and adjustable. Increase the confidence requirement or alter the weighting of a specific attribute.</p>
<p><strong>Optimize reuse</strong>
Automatically scan all tests, identify repeated sequences, deduplicate, and replace them with reusable groups to reduce maintenance.</p>
</blockquote>
<p>TestProject:</p>
<blockquote>
<p><strong>Self Healing</strong>
One thing that many test automation teams have found is that test maintenance takes up a lot of time. Often tests will fail due to changing or moving <ins>locators</ins>. Humans can easily correct for this, but traditional test automation can't. This leads to a lot of time being spent on updating tests. This kind of work can be tedious and so TestProject has created self-healing capabilities. This AI-driven capability will identify one main way to identify an element, but will also find a number of other possible ways to locate the element. If something breaks such that the primary <ins>locator</ins> strategy does not work, it will automatically detect that it is broken and then use the fallback strategies to find the element for you.</p>
</blockquote>
<p>What is the <ins>"locator"</ins>?</p>
<p>Webpage source can be viewed in Web DevTools. If you press F12 or right-click and select "View page source" you will see an HTML code.</p>
<p>Each HTML-tag (a, li, div, form) is an HTML-element.</p>
<p><strong>Locator</strong> is a query that helps to find 1 or more HTML elements on the page.</p>
<p>If we are talking about an Android app something similar to the DevTools view can be seen in the Layout Inspector.</p>
<p>The simplest ways to find an element are by id, name, class name, link text, tag name. CSS selector and XPath are used in the locators too.</p>
<p><strong>Example</strong>: there is an application called <a href="https://debugle.com/">Debugle</a>. I used it to teach my students to create good bug reports. Also, we created test plans for it, checklists for cross-browser testing and automated smoke tests for it.</p>
<p><img src="/assets/img/articles/2022-01-03-AI-in-automated-UI-testing/pic1.webp" alt=""></p>
<p>It is possible to identify the highlighted element by its id, name, tag name, class name, type, or placeholder. Locators could look like the following (C#):</p>
<ul>
<li>By.Id("UserEmail")</li>
<li>By.Name("data[User][email]")</li>
<li>By.ClassName("input-text")</li>
<li>By.TagName("input")</li>
<li>By.CssSelector("input[placeholder='Email']")</li>
<li>By.CssSelector("input[type='email']")</li>
<li>By.XPath("//input[@type='email']")</li>
</ul>
<p>The list isn't full. These locators can be used for automation with Selenium WebDriver.</p>
<p>I propose to record the following test in Testim and in TestProject and compare the results:</p>
<ol>
<li>Open <a href="https://debugle.com/">Debugle</a>.</li>
<li>Click Login (in the top right corner).</li>
<li>Login as "<a href="mailto:debugle-demo@ua.fm">debugle-demo@ua.fm</a>", "Qwerty123!" (you can use this test account or create your own).</li>
<li>Check that "Hello, Inna" (or your account name) is on the top of the page.</li>
</ol>
<p>Instructions for Testim are <a href="https://help.testim.io/docs/creating-your-first-codeless-test">here</a>.</p>
<p>Instructions for TestProject are <a href="https://docs.testproject.io/tips-and-tricks/how-to-record-a-web-test-for-a-mobile-browser#creating-a-web-test">here</a>.</p>
<p>Let's start with Testim.</p>
<p>Work of Testim AI is to record all possible locators for the element and locators of all its children and parents:</p>
<p><img src="/assets/img/articles/2022-01-03-AI-in-automated-UI-testing/pic2.webp" alt=""></p>
<p>It is clear from the picture above that Testim recorded only properties of the "input" element (id, name, class, placeholder etc.)</p>
<p>When it is necessary, Testim will try to locate the element by these properties (and by the properties of its parents and children). It will calculate a score. If the score is “Very Low” the test will be failed because the element cannot be found on the page.</p>
<p><strong>Note</strong>: it is impossible to add more locators, it is only possible to hide some locators.</p>
<p>Let's take a look at two more examples to get a better understanding of how it works.</p>
<p><strong>Example</strong>: run the recorded test for the 2nd time.</p>
<p>The user is already logged in (no Login button in the top right corner of the page). Testim opens the Account page (instead of the Login page). After that it is trying to enter username and password, and it isn’t possible. Anyway the test is passed.</p>
<p>There are 3 ways to fix the problem:</p>
<ol>
<li>Clear cookies before the second step</li>
<li>Add a step to logout (if needed) before running the rest of the steps</li>
<li>Run the test in the Incognito mode</li>
</ol>
<p>To clear cookies:</p>
<ol>
<li>Add a new step with type "Add custom action" (from the "Testim predefined steps") after the "Setup" step.</li>
<li>Enter the following Javascript code:</li>
</ol>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">var</span> cookies = <span class="hljs-variable language_">document</span>.<span class="hljs-property">cookie</span>.<span class="hljs-title function_">split</span>(<span class="hljs-string">";"</span>);

<span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &#x3C; cookies.<span class="hljs-property">length</span>; i++) {
    <span class="hljs-keyword">var</span> cookie = cookies[i];
    <span class="hljs-keyword">var</span> eqPos = cookie.<span class="hljs-title function_">indexOf</span>(<span class="hljs-string">"="</span>);
    <span class="hljs-keyword">var</span> name = eqPos > -<span class="hljs-number">1</span> ? cookie.<span class="hljs-title function_">substr</span>(<span class="hljs-number">0</span>, eqPos) : cookie;
    <span class="hljs-variable language_">document</span>.<span class="hljs-property">cookie</span> = name + <span class="hljs-string">"=;expires=Thu, 01 Jan 1970 00:00:00 GMT"</span>;
}

location.<span class="hljs-title function_">reload</span>();
</code></pre>
<ol start="3">
<li>Save and run the test.</li>
</ol>
<p>It doesn't help (and it won't help in most cases) because the "HttpOnly" flag disables Javascript's access to the cookie.</p>
<p>I decided to use the 3rd way: to check the "Run in incognito mode" checkbox and allow the Testim Chrome extension to be enabled in the Incognito mode. It is simpler than the 2nd way. Also when the tests are run in the Grid no need to clear cookies.</p>
<p>Let's continue with the test above but before the beginning change the locator for the "Hello, Inna" element. Change "Inna" to "Maria".
Run the test.</p>
<p>It will be passed. It is because of the score I've written earlier.</p>
<p>Do the following:</p>
<ol>
<li>Open the step with assertion.</li>
<li>Open "Locators".</li>
<li>Click the "target" icon.</li>
<li>Change "The minimal confidence level required to consider the element as found" to "High" (it is "Low" by default).</li>
<li>Run the test again.</li>
</ol>
<p>The result will be the same.</p>
<p>It will fail only if "Very High" is selected in this case.</p>
<p><strong>Note</strong> : the test will fail in 30-40 seconds!!!</p>
<p>As far as all element locators and all locators of its children and parents are taken into account in calculation of the score it is impossible to know exactly which "The minimal confidence level required to consider the element as found" should be selected. In some cases it should be "High", in some cases it should be "Very High".</p>
<p>I believe the AI work of Testim needs to be improved.
It is really easy to record the tests, run them and receive reports. However, I cannot trust the results.</p>
<p>It's TestProject turn.</p>
<p>Here are the locators recorded by the TestProject for the "email" element:</p>
<p><img src="/assets/img/articles/2022-01-03-AI-in-automated-UI-testing/pic3.webp" alt=""></p>
<p>It is possible to define which locator is more preferred, delete or add a new locator for the element.
The first 3 locators look secure enough.</p>
<p>Let's change the fist locator to "#UserEmailTEST" to have a look at how TestProject AI works.</p>
<p>The test will be passed and the following will be in the report:</p>
<p><img src="/assets/img/articles/2022-01-03-AI-in-automated-UI-testing/pic4.webp" alt=""></p>
<p>This "heartbeat" sign means that the element wasn't recognized by the 1st locator and was found by another locator from the list:</p>
<blockquote>
<p>Don't sweat! This step was self-healed by TestProject's AI.
We'll keep looking for ways to speed up your executions.
The original locator was "CSSSELECTOR:#UserEmailTEST"</p>
</blockquote>
<p>When I changed "Inna" to "Maria" in the step with assertion the test failed as it was expected.</p>
<p>This, of course, is just my opinion. I like the TestProject more, I trust its results more.</p>
<p>It has a lot of good features. The TestProject community continuously develops new addons. One of them gives me the ability to clear cookies for example.</p>
<p>Also, it is possible to implement some kind of Page Object Pattern, well-known and bellowed by QA Automation Engineers. Elements can be added manually and placed to folders. Then they can be used when creating tests.</p>
<p>For those who are on Windows machines it is possible to automate tests for iOS apps. Visit this page for <a href="https://docs.testproject.io/getting-started/getting-started-with-ios-testing/ios-devices">details</a>. I tried and it worked for me.</p>
<p>To summarize, when I started to investigate TestProject and Testim I didn't expect anything amazing.</p>
<p>I am still sure that there are a lot of cases when it is impossible to automate tests without knowledge of any programming language. Two of them:</p>
<ul>
<li>when search for some entities you should be sure that all possible variants are present in the results and no any superfluous results</li>
<li>when tickets, rooms etc. should be selected for some dates in the future (it is not easy to use Calendar in this cases)</li>
</ul>
<p>TestProject SDK can be used: C#, JAVA, Python.</p>
<p>Testim Javascrip Dev Kit can be used.</p>
<p>It gives some flexibility in creating the tests.</p>
<p><strong>Testim AI disappoints me (I hope for a while).</strong></p>
<p><strong>I would like to learn TestProject deeper, I see potential here. I will try it for our applications.</strong></p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/0E_vhMVqL9g">Andy Kelly</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[An exercise in Web3 & DAOs]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2021/12/22/an-exercise-in-web3-daos</link>
            <guid>https://engineering.monstar-lab.com/en/post/2021/12/22/an-exercise-in-web3-daos</guid>
            <pubDate>Wed, 22 Dec 2021 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>There is always excitement about what the future should look like. As the year ends, tech corporations and developers attempt to look into the future, make bold predictions and try to learn from the past. Recently, there has been a new paradigm surfacing from the tech-sphere, with an ambition to challenge, and perhaps change the way we perceive the internet.
The message is clear and brief; the new net is here, and you may own it.</p>
<p>Web3 (originally “Web 3.0”), as the word was first coined by Ethereum co-founder Gavin Wood in 2014 as he had just recently left the Ethereum project, has been dominating the tech landscape in the recent months.</p>
<p>Our team, “Team BlockBhais”, was formed for <a href="https://www.linkedin.com/showcase/monstarhacks">MonstarHacks</a>, an internal hackathon event that happened between 10-12 December 2021 where teams from all the Monstarlab offices were invited to participate. This year's MonstarHacks theme was to make a solution for any of the 3 judge-selected goals out of the 17 <a href="https://sdgs.un.org/goals">UN Sustainable Goals</a>. Out of the selected goals for the hackathon, we picked the 3rd SDG goal (good health, and well-being) and took this opportunity to explore, learn and implement aspects of the Web3 in a real hybrid solution, bringing together the best of both worlds.</p>
<p>We identified that post-COVID era we realized that we need to work together to achieve any success and the government may lead but cannot solve problems like medicare alone.
The current problems of healthcare services are many, and the most pressing issues are the health care services are inadequate, underfunded, understaffed, and people with pre-existing medical conditions are the worst affected. Too often we see people opt-out of regular medical check-ups or services so as to save for emergencies. We want an alternative for people to invest in their health at an earlier age and consistently, so that they may reap the benefits of a healthy life and more affordable healthcare.</p>
<p>So, over the hackathon weekend, we created BlockHealth+ - a blockchain-powered Health DAO (Decentralized Autonomous Organization) that rewards good healthy behavior with healthcare. The next time you go for a run, you’ll gain tokens by losing calories! These BlockHealth Tokens (BHT) can be exchanged for medical services from one of the community-picked partners.</p>
<h2>Case Study: BlockHealth+</h2>
<h2>Building a health-focused DAO on the blockchain</h2>
<p><div class="embed-container">
      <iframe
          src="https://www.youtube.com/embed/1TiH4Qo8X7g"
          width="700"
          height="480"
          frameborder="0"
          allowfullscreen="">
      </iframe>
    </div>
    </p>
<h3>Introduction</h3>
<p>Imagine you are running a marathon event, you get a medal at the end of the race, but in addition you are also rewarded with sufficient social capital that can easily be redeemed with top healthcare providers for a 20-30% discount or greater.</p>
<p>Another scenario may be a not so strictly physical in nature, but assuming you participate in your government's vaccination drive and get your Covid-19 vaccine, you are then allocated tokens upon completion that can be spent with your local healthcare provider for, perhaps, large discounts in your next medicinal purchase. But, how do you facilitate this sort of cooperation among many different authorities, partners and people?</p>
<h3>Proposition</h3>
<p>We needed to create a community with a shared, trustless bank account dedicated to rewarding the health-related activities that people do regularly with health services in the longer run. This community will have transparent financial transactions and a direct democratic governance-structure.</p>
<p>For participating members, all of their good behaviors now serve a greater purpose. They can carry out their daily routine, track it in their Google Fit, registered gym or other mandated 3rd party health service, and later cash-out these activities in the health sector with entities that have a community-mandated partnership.</p>
<p>The partnerships can be suggested by anyone, but it is up to the members of the community (of the DAO), to vote for and against the different companies and/or NGOs, authorities etc.
Essentially, the community votes for the partnerships they want on their terms - cutting out the middleman, which has the potential to be predatory in nature, and wind up getting the better deal.</p>
<p>This greater cause and transparency on how the money is spent and in what activities, allows UN, WHO, governments, NGOs, philanthropists, etc. to step in to promote and preserve a healthy lifestyle and healthcare in society.</p>
<p>Business entities have an incentive to be part of a community to help individuals redeem the rewards with their health services since they do enjoy extra benefits from the government, as well as a monetary incentive from the group for various initiatives.</p>
<h3>What is a DAO?</h3>
<p>Web3 revolves around the idea of decentralization, which is in stark contrast to its predecessor, in which large amounts of data and personal information is owned by a few large tech companies, also known as “Big Tech”.</p>
<p>The main idea is that Web3 is governed by people through the use of decentralized blockchain technologies, in which large tech companies have little to no control over data or people.
There has been a resurgence of financial instruments (DeFi - Decentralized Finance), as well as gaming, art, music, and even organization forms such as the so-called DAO (Decentralized Autonomous Organization).</p>
<p>In this form of organization, decisions are made by stakeholders/members of said organization, usually through a vote.</p>
<h3>In practice</h3>
<p>So, to create a DAO or to use an OSS DAO platform after thorough investigation and be able to connect it to it via middleware whose documentation is not updated at all, to utilize blockchain and users digital wallet with Metamask integration, to write smart contracts, and to generate NFTs in a language like a Solidity that is not mainstream, all was daunting, to begin with, but with time we slowly could see how the pieces connect.</p>
<p><img src="/assets/img/articles/2021-12-22-an-exercise-in-web3-daos/model.webp" alt=""></p>
<p>This entire project currently relies on the web and mobile applications to interface with the DAO, we selected (Aragon) [<a href="https://aragon.org/">https://aragon.org/</a>] to build one, by use of middleware where this middleware also communicates with the Companion Smart Contract - that means whenever you participate, you will be able to be rewarded with their own redeemable NFT.</p>
<p>However, while members of the DAO are able to grant entities partnership roles and revoke them, they are also able to vote on the software solution required to facilitate activity validation - this means our current software (web &#x26; mobile) could be replaced in the future and are not irrevocably binding the DAO to it.</p>
<h3>System Flow</h3>
<ul>
<li>Onboarding</li>
<li>Import/Generate Wallet</li>
<li>Acquire BHT Tokens</li>
<li>Get Entry In DAO</li>
<li>Integrate FIT APP Data</li>
<li>Be Part GooD Healthy Activities</li>
<li>Be ACTIVE IN DAO Activities</li>
<li>Claim Rewards, NFTS</li>
<li>Redeem tokens</li>
</ul>
<h3>Technologies (including 3rd party frameworks and services)</h3>
<p><img src="/assets/img/articles/2021-12-22-an-exercise-in-web3-daos/infra.webp" alt=""></p>
<h4>Aragon DAO</h4>
<ul>
<li><a href="https://aragon.org/aragon-client">Aragon Client</a> (platform)</li>
<li><a href="https://help.aragon.org/article/30-create-a-new-company-organization">Aragon Company Template</a>(configuration-type)</li>
</ul>
<h4>Mobile</h4>
<ul>
<li><a href="https://flutter.dev/">Flutter</a> (framework)</li>
<li><a href="https://pub.dev/packages/webview_flutter/example">Webview_flutter</a> (3rd-party package for displaying web in-app)</li>
<li><a href="https://dart.dev/">Dart</a> (language)</li>
</ul>
<h4>Partnership Smart Contract Companion</h4>
<ul>
<li><a href="https://docs.soliditylang.org/en/v0.8.11/">Solidity</a> (language)</li>
<li><a href="https://eips.ethereum.org/EIPS/eip-721">ERC721</a> (ethereum token standard)</li>
<li>Access Control (smart contract administration standard)</li>
<li>Javascript (language)</li>
<li><a href="https://hardhat.org/">Hardhat</a> (dev environment)</li>
<li><a href="https://docs.ethers.io/v5/">ethers.js</a> (JS library for blockchain interaction)</li>
<li><a href="https://getwaffle.io/">Waffle</a> (Library for writing &#x26; testing smart contracts)</li>
<li><a href="https://www.chaijs.com/">Chai</a> (JS library for smart contract testing)</li>
</ul>
<h4>Web App</h4>
<ul>
<li><a href="https://github.com/angular/angular">Angular 12</a></li>
<li>Typescript</li>
<li>RxJS</li>
<li>Web3</li>
<li><a href="https://ant.design/">Ant Design</a></li>
</ul>
<h4>Laravel Middleware</h4>
<ul>
<li><a href="https://github.com/laravel/laravel">Laravel</a></li>
<li>PHP 8.0</li>
<li>Database</li>
<li>MySQL 8</li>
</ul>
<h4>Node.js Middleware</h4>
<ul>
<li>Typescript</li>
<li><a href="https://github.com/aragon/connect">Aragon Connect</a></li>
</ul>
<h4>Terraform IAC</h4>
<h3>Afterword</h3>
<p>We strongly believe this solution of DAO, powered by blockchain, that tries to reward
people for their activities, and allow people to have ownership stake from day one can
be used for other domains apart from health, like education, climate change etc.</p>
<p><img src="/assets/img/articles/2021-12-22-an-exercise-in-web3-daos/blockbhais.webp" alt=""></p>
<p>This hackathon helped us learn many things, among them:</p>
<ul>
<li>SDG Goals and their impact in society</li>
<li>How DAOs work</li>
<li>Hands on experience with creating and managing a DAO on Aragon</li>
<li>Better understanding of blockchain technology</li>
<li>General Web3 knowledge</li>
<li>Metamask wallet integration to web app</li>
<li>Smart Contract development using Solidity</li>
<li>Connecting to DAO via JS</li>
</ul>
<p>Surely Web3 looks daunting, but the potential it has is huge as power is redistributed from organizations and large tech companies, back into the hands of the people. Exciting days ahead for developers and users alike.</p>
<p><a href="https://unsplash.com/photos/oGv9xIl7DkY">Header Image</a> from Unsplash by fitsum-admasu.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Hackathon ideation through Miro]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2021/12/21/Ideation-through-Miro</link>
            <guid>https://engineering.monstar-lab.com/en/post/2021/12/21/Ideation-through-Miro</guid>
            <pubDate>Tue, 21 Dec 2021 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Restrictions on Face-to-Face meetings are always an archenemy of collaboration. Many of us facing this issue had to adapt and find other means to work around our limitations. And it came with no surprise that when it was our time to discuss and ideate for the Monstarlab's hackathon - <a href="https://www.linkedin.com/showcase/monstarhacks">MonstarHacks</a>, we turned to <a href="https://miro.com/index/">Miro</a>.</p>
<p>Miro gave us the opportunity to do live collaboration without meeting physically, and the best part of it is the ease and convenience to input our ideas together and structure them in a neat and wholesome way. Miro gives you the templates according to your preference of collaboration - in our case, it was brainstorming and ideation.</p>
<p>As we are brainstorming through our concept, we are able to leave notes in the platform and our ideas grew and gave birth to the flowchart of the application. From the overarching goals and recognising the problem statements, to the minute details of the app’s features, to the wire frames - we did it all on Miro. It allows extensive information to be stored on cloud so that we may go back to it anytime with just a click of the link.</p>
<img src="/assets/img/articles/2021-12-21-Ideation-through-Miro/image_1.webp">
<p>We can just select the stick notes in the left banner and select the color for categorisation purposes to make it easier to segmentise our ideas.</p>
<img src="/assets/img/articles/2021-12-21-Ideation-through-Miro/image_2.webp">
<p>Even with our research on the topic, all we have to do is just to copy and paste the link to the board and it will be instantaneously be reflected with the details of the webpage. From the start to the end of our ideation, it took us 2-3 hours to get our framework together and when we went on our separate ways to work on individual tasks, it became the foundation of our reference point and it helped to accelerate the process.</p>
<img src="/assets/img/articles/2021-12-21-Ideation-through-Miro/image_3.webp">
<p>You can also link Miro to other apps such as Slack, Zoom and Figma to integrate your work smoothly across the other platforms.</p>
<img src="/assets/img/articles/2021-12-21-Ideation-through-Miro/image_4.webp">
<p>There is much to explore for Miro but it is a starting point for everyone to try it out and use the platform to experience a more efficient and effective work process.</p>
<h3>Resources</h3>
<ul>
<li><a href="https://miro.com/index/">Miro</a></li>
<li><a href="https://miro.com/academy/">Miro Academy</a></li>
<li><a href="https://go.miro.com/webinars">Miro Webinars</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Swift: From Protocol to AssociatedType then Type Erasure]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2021/12/21/Swift-Type-Erasure</link>
            <guid>https://engineering.monstar-lab.com/en/post/2021/12/21/Swift-Type-Erasure</guid>
            <pubDate>Tue, 21 Dec 2021 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>When Swift was made, it had been the first protocol-oriented programming language. Though Swift is great for object-oriented programming, but from the way <em>for-loops</em> and <em>String</em> <em>literals</em> work to the emphasis in the standard library on <em>generics</em>, at its heart, Swift is protocol-oriented.</p>
<h2>Motivation</h2>
<p>We use classes to represent a symmetric operation, like Comparison, for example, if we want to write a generalized sort or binary search where we need to compare two elements, we end up this something as below:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Ordered</span> {
	<span class="hljs-keyword">func</span> <span class="hljs-title function_">precedes</span>(<span class="hljs-params">other</span>: <span class="hljs-type">Ordered</span>) -> <span class="hljs-type">Bool</span> {
		<span class="hljs-comment">/// To ensure this method is implemented by subclass</span>
		<span class="hljs-comment">/// we added an error and that is nothing but a trap.</span>
		<span class="hljs-built_in">fatalError</span>(<span class="hljs-string">"implement me!"</span>)
	}
}
</code></pre>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">binarySearch</span>(<span class="hljs-params">sortedKeys</span>: [<span class="hljs-type">Ordered</span>], <span class="hljs-params">forKey</span> <span class="hljs-params">k</span>: <span class="hljs-type">Ordered</span>) -> <span class="hljs-type">Int</span> {
  <span class="hljs-keyword">var</span> low <span class="hljs-operator">=</span> <span class="hljs-number">0</span>, high <span class="hljs-operator">=</span> sortedKeys.count
  <span class="hljs-keyword">while</span> high <span class="hljs-operator">></span> low {
  	<span class="hljs-keyword">let</span> mid <span class="hljs-operator">=</span> low <span class="hljs-operator">+</span> (high <span class="hljs-operator">-</span> low) <span class="hljs-operator">/</span> <span class="hljs-number">2</span>
  	<span class="hljs-keyword">if</span> sortedKeys[mid].precedes(k) { low <span class="hljs-operator">=</span> mid <span class="hljs-operator">+</span> <span class="hljs-number">1</span> }
  	<span class="hljs-keyword">else</span> { high <span class="hljs-operator">=</span> mid }
  }
  <span class="hljs-keyword">return</span> low
}
</code></pre>
<p>We don’t know anything about an arbitrary instance of <code>Ordered</code> yet. So if the method is not implemented by a subclass, well, there is nothing we can do other than the trap. Now, this is the first sign that we are fighting the type system. And if we fail to recognize that, it is also where we start lying to ourselves. Because we brush the issue aside, telling ourselves as long as each subclass of Order implements precedes, we will be okay. Making it the subclass’s problem. So we go ahead and implement an example of <code>Ordered</code> as below.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Number</span>: <span class="hljs-title class_">Ordered</span> {
    <span class="hljs-keyword">let</span> value: <span class="hljs-type">Double</span>
    <span class="hljs-keyword">init</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">value</span>: <span class="hljs-type">Double</span>) {
        <span class="hljs-keyword">self</span>.value <span class="hljs-operator">=</span> value
    }
    <span class="hljs-comment">/// We down-cast other to Number to get to the right type to compare.</span>
    <span class="hljs-comment">/// It is a static type safety hole. Classes don't let us express this</span>
    <span class="hljs-comment">/// crucial type-relationship between the type of self and type of other.</span>
    <span class="hljs-comment">/// It is a code smell.</span>
    <span class="hljs-keyword">override</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">precedes</span>(<span class="hljs-params">other</span>: <span class="hljs-type">Ordered</span>) -> <span class="hljs-type">Bool</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>.value <span class="hljs-operator">&#x3C;</span> (other <span class="hljs-keyword">as!</span> <span class="hljs-type">Number</span>).value
    }
}
</code></pre>
<p>It got <code>double</code> value and we override <code>precedes</code> to do the comparison. <code>other</code> is just arbitrary Ordered and not a number. So we don’t know that <code>other</code> has a value property. We down-cast <code>Other</code> to <code>Number</code> to get to the right type to compare. It is a static type safety hole. Classes don’t let us express this crucial type relationship between the type of <code>self</code> and type of <code>Other</code>. It is a code smell. So any time we see a force down-cast in our code, it’s a good sign that some important type-relationship has been lost, and often that’s due to classes for abstraction. Clearly, what we need is a better abstraction mechanism.</p>
<h2>Better Abstraction Mechanism</h2>
<p>An abstraction mechanism must have the following properties:</p>
<ul>
<li>Doesn’t force to accept implicit sharing or lost type relationships</li>
<li>Force to choose just one abstraction and do it at the time types are defined</li>
<li>Doesn’t force to accept unwanted instance data or the associated initialization complexity</li>
<li>Doesn’t leave ambiguity about what needs to override.</li>
</ul>
<p><strong>And yes…!!! Protocol has all of these properties.</strong></p>
<blockquote>
<p>Don’t start with a class. Start with a protocol…!!!</p>
</blockquote>
<figure>
 <p style="margin:30"></p>
 <div style="width:100%;height:0;padding-bottom:100%;position:relative;">
 	<iframe src="https://giphy.com/embed/a5auAyjyCOf1S" width="100%" height="100%" style="position:absolute" frameborder="0" class="giphy-embed" allowfullscreen>
 	</iframe>
 </div>
 <figcaption style="text-align: center;">
  	Time for POP, no more OOP
  	<a href="https://giphy.com/gifs/princess-leia-a5auAyjyCOf1S">via GIPHY</a>
 </figcaption>
</figure>
<h2>Protocol-Oriented Programming in Swift</h2>
<p>There is a saying in Swift: “Don't start with a class, start with protocol”. So let’s redo the binary search example with protocol</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">/// Protocol does not have a method body.</span>
<span class="hljs-comment">/// We are trading dynamic runtime check for the static check.</span>
<span class="hljs-comment">/// Self in a protocol is a placeholder for the type that is going</span>
<span class="hljs-comment">/// to conform to that protocol. It is called "Self" requirement.</span>
<span class="hljs-keyword">protocol</span> <span class="hljs-title class_">Ordered</span> {
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">precedes</span>(<span class="hljs-params">other</span>: <span class="hljs-keyword">Self</span>)  -> <span class="hljs-type">Bool</span>
}

<span class="hljs-comment">/// sortedKeys is now a homogeneous array of any single Ordered type T.</span>
<span class="hljs-comment">/// Original signature, heterogeneous array of Ordered was a lie.</span>
<span class="hljs-comment">/// We never really handled the heterogeneous case other than by trapping.</span>
<span class="hljs-keyword">func</span> <span class="hljs-title function_">binarySearch</span>&#x3C;<span class="hljs-type">T</span>: <span class="hljs-type">Ordered</span>>(<span class="hljs-params">sortedKeys</span>: [<span class="hljs-type">T</span>], <span class="hljs-params">forKey</span> <span class="hljs-params">k</span>: <span class="hljs-type">T</span>) -> <span class="hljs-type">Int</span> {
    <span class="hljs-keyword">var</span> low <span class="hljs-operator">=</span> <span class="hljs-number">0</span>
    <span class="hljs-keyword">var</span> high <span class="hljs-operator">=</span> sortedKeys.count

    <span class="hljs-keyword">while</span> high <span class="hljs-operator">></span> low {
        <span class="hljs-keyword">let</span> mid <span class="hljs-operator">=</span> low <span class="hljs-operator">+</span> (high <span class="hljs-operator">-</span> low) <span class="hljs-operator">/</span> <span class="hljs-number">2</span>
        <span class="hljs-keyword">if</span> sortedKeys[mid].precedes(other: k) {
            low <span class="hljs-operator">=</span> mid <span class="hljs-operator">+</span> <span class="hljs-number">1</span>
        } <span class="hljs-keyword">else</span> {
            high <span class="hljs-operator">=</span> mid
        }
    }
    <span class="hljs-keyword">return</span> low
}

<span class="hljs-comment">/// As Ordered is not class anymore,</span>
<span class="hljs-comment">/// we are not subclassing Ordered, instead confirming.</span>
<span class="hljs-comment">/// So, no more override keyword in preceds method.</span>
<span class="hljs-keyword">extension</span> <span class="hljs-title class_">Int</span>: <span class="hljs-title class_">Ordered</span> {
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">precedes</span>(<span class="hljs-params">other</span>: <span class="hljs-type">Int</span>) -> <span class="hljs-type">Bool</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span> <span class="hljs-operator">&#x3C;</span> other
    }
}

<span class="hljs-keyword">let</span> position <span class="hljs-operator">=</span> binarySearch(sortedKeys: [<span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>], forKey: <span class="hljs-number">5</span>)
</code></pre>
<figure>
 <div style="width:100%;height:0;padding-bottom:108%;position:relative;">
	<iframe src="https://giphy.com/embed/h2MLtoOjxtkGY" width="100%" height="100%" style="position:absolute" frameborder="0" class="giphy-embed" allowfullscreen>
	</iframe>
</div>
 <figcaption style="text-align: center;">
  	Wow…!!! “Protocol” rocks…!!!
  	<a href="https://giphy.com/gifs/impressed-bfd-h2MLtoOjxtkGY">via GIPHY</a>
  </figcaption>
</figure>
<h2>Protocol Self Requirement</h2>
<p>Once we have a Self-requirement to a protocol, it moves the protocol into a very different world, where the capabilities have a lot less overlap with classes.</p>
<ul>
<li>It stops being usable as a type</li>
<li>Collections become homogeneous instead of heterogeneous</li>
<li>An interaction between instances no longer implies an interaction between all model types.</li>
<li>We trade dynamic polymorphism for static polymorphism, but, in return for that extra type-information we are giving the compiler, it is more optimizable</li>
</ul>
<figure>
  <img src="/assets/img/articles/2021-12-21-Swift-Type-Erasure/image_1.webp">
  <figcaption style="text-align: center;">
  	Protocol Oriented Programming in Swift, session 408, #WWDC2015
  </figcaption>
</figure>
<h2>Protocol Extension</h2>
<p>In our current implementation, we need to implement <code>precedes</code> method for each type. e.g:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">extension</span> <span class="hljs-title class_">Int</span>: <span class="hljs-title class_">Ordered</span> {
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">precedes</span>(<span class="hljs-params">other</span>: <span class="hljs-type">Int</span>) -> <span class="hljs-type">Bool</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span> <span class="hljs-operator">&#x3C;</span> other
    }
}

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">Double</span>: <span class="hljs-title class_">Ordered</span> {
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">precedes</span>(<span class="hljs-params">other</span>: <span class="hljs-type">Double</span>) -> <span class="hljs-type">Bool</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span> <span class="hljs-operator">&#x3C;</span> other
    }
}
</code></pre>
<p>Here comes the power of protocol. One implementation to rule them all by using a constrained extension on Ordered.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">/// It says that, type that is Comparable</span>
<span class="hljs-comment">/// and also is declared to be Ordered will automatically</span>
<span class="hljs-comment">/// be able to satisfy the precedes requirement</span>
<span class="hljs-keyword">extension</span> <span class="hljs-title class_">Ordered</span> <span class="hljs-title class_">where</span> <span class="hljs-title class_">Self</span>: <span class="hljs-title class_">Comparable</span> {
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">precedes</span>(<span class="hljs-params">other</span>: <span class="hljs-keyword">Self</span>) -> <span class="hljs-type">Bool</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span> <span class="hljs-operator">&#x3C;</span> other
    }
}
<span class="hljs-comment">/// No more precedes method implementation</span>
<span class="hljs-keyword">extension</span> <span class="hljs-title class_">Int</span>: <span class="hljs-title class_">Ordered</span> {}
<span class="hljs-keyword">extension</span> <span class="hljs-title class_">Double</span>: <span class="hljs-title class_">Ordered</span> {}
</code></pre>
<p>Let’s take constrained extension a step further</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">protocol</span> <span class="hljs-title class_">Ordered</span> {
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">precedes</span>(<span class="hljs-params">other</span>: <span class="hljs-keyword">Self</span>)  -> <span class="hljs-type">Bool</span>
}

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">Collection</span> <span class="hljs-title class_">where</span> <span class="hljs-title class_">Self</span>.<span class="hljs-title class_">Index</span> == <span class="hljs-title class_">Int</span>, <span class="hljs-title class_">Element</span>: <span class="hljs-title class_">Ordered</span> {
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">binarySearch</span>(<span class="hljs-params">forKey</span> <span class="hljs-params">k</span>: <span class="hljs-type">Element</span>) -> <span class="hljs-type">Int</span> {
        <span class="hljs-keyword">var</span> low <span class="hljs-operator">=</span> <span class="hljs-number">0</span>
        <span class="hljs-keyword">var</span> high <span class="hljs-operator">=</span> <span class="hljs-keyword">self</span>.count

        <span class="hljs-keyword">while</span> high <span class="hljs-operator">></span> low {
            <span class="hljs-keyword">let</span> mid <span class="hljs-operator">=</span> low <span class="hljs-operator">+</span> (high <span class="hljs-operator">-</span> low) <span class="hljs-operator">/</span> <span class="hljs-number">2</span>
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">self</span>[mid].precedes(other: k) {
                low <span class="hljs-operator">=</span> mid <span class="hljs-operator">+</span> <span class="hljs-number">1</span>
            } <span class="hljs-keyword">else</span> {
                high <span class="hljs-operator">=</span> mid
            }
        }
        <span class="hljs-keyword">return</span> low
    }
}

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">Ordered</span> <span class="hljs-title class_">where</span> <span class="hljs-title class_">Self</span>: <span class="hljs-title class_">Comparable</span> {
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">precedes</span>(<span class="hljs-params">other</span>: <span class="hljs-keyword">Self</span>) -> <span class="hljs-type">Bool</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span> <span class="hljs-operator">&#x3C;</span> other
    }
}

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">Int</span>: <span class="hljs-title class_">Ordered</span> { }

<span class="hljs-comment">/// Call binarySearch just like the regular method.</span>
<span class="hljs-comment">/// No more angle-bracket blindness.</span>
<span class="hljs-keyword">let</span> index <span class="hljs-operator">=</span> [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>].binarySearch(forKey: <span class="hljs-number">3</span>) <span class="hljs-comment">// 2</span>
</code></pre>
<h2>Protocol Associated Types:</h2>
<p>By now we know what is Protocol Oriented Programming (POP). But POP without Protocol Associated Types (PAT) will never be completed.</p>
<p><code>associatedtype</code> is a protocol generic placeholder for an unknown <code>Concrete Type</code> that requires concretization on adoption at <strong>Compile</strong> time.</p>
<blockquote>
<p>Protocol Associated Types (PAT)= Type Alias + Generics</p>
</blockquote>
<p><br>
<br>
</p>
<p>At one stage of programming in Swift, many of us might have came across the below error:</p>
<figure>
  <img src="/assets/img/articles/2021-12-21-Swift-Type-Erasure/image_2.webp">
  <figcaption style="text-align: center;">
  	Error while trying to use Generic in Protocol
  </figcaption>
</figure>
<p>Swift does not allow us to use <code>Generic</code> parameters in <code>Protocol</code>. To bypass this limitation, Swift introduced <code>Protocol Associated Type</code>. The below example shows how to use <code>associatedType</code> in <code>Protocol</code></p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">protocol</span> <span class="hljs-title class_">ViewModelType</span> {
    <span class="hljs-keyword">associatedtype</span> myType
    <span class="hljs-keyword">var</span> anyProperty: myType { <span class="hljs-keyword">get</span> <span class="hljs-keyword">set</span> }
}

<span class="hljs-keyword">class</span> <span class="hljs-title class_">ViewModel</span>: <span class="hljs-title class_">ViewModelType</span> {
    <span class="hljs-keyword">typealias</span> myType <span class="hljs-operator">=</span> <span class="hljs-type">String</span>
    <span class="hljs-keyword">var</span> anyProperty: myType <span class="hljs-operator">=</span> <span class="hljs-string">"Hello, Protocol Associated Type"</span>
}
</code></pre>
<h2>Type Erasure</h2>
<p>Though <code>associatedType</code> solved one problem, it also introduced another problem. In Swift, we can not use <code>Protocol</code> with <code>associatedType</code> as a Type. What it means is, if we set a variable type to <code>ViewModelType,</code> <code>Swift</code> compiler will show the following error:</p>
<figure>
  <img src="/assets/img/articles/2021-12-21-Swift-Type-Erasure/image_3.webp">
  <figcaption style="text-align: center;">
  	Error while using Protocol with AssociatedType as Type
  </figcaption>
</figure>
<p><code>Type Erasure</code> is the only savior here to this error. There are three patterns that we can apply to solve the problem of generic constraints requirement.</p>
<ul>
<li><code>Constrained Type Erasure:</code> erases a type but keeps a constrain on it.</li>
<li><code>Unconstrained Type Erasure:</code> erases a type without a constrain on it</li>
<li><code>Shadow Type Erasure:</code> erases a type by camouflaging the type</li>
</ul>
<p><strong>Example</strong></p>
<p>The below code snippet shows how to implement multi-sectioned heterogeneous TableViewCell using <code>Unconstrained Type Erasure</code></p>
<pre><code class="hljs language-swift"><span class="hljs-comment">/// Cell `Interface`</span>
<span class="hljs-keyword">protocol</span> <span class="hljs-title class_">CellModel</span> {
    <span class="hljs-comment">/// PAT Placeholder for unknown Concrete Type `Model`</span>
    <span class="hljs-keyword">associatedtype</span> <span class="hljs-type">Cell</span>: <span class="hljs-type">UITableViewCell</span>
    <span class="hljs-comment">/// Recieves a parameter of Concrete Type `Model`</span>
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">tableViewCell</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">tableView</span>: <span class="hljs-type">UITableView</span>) -> <span class="hljs-type">Cell</span>
}

<span class="hljs-comment">/// Wrapper `AnyCell` erased the Type requirement</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">AnyCell</span> {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> _tableViewCell: (<span class="hljs-keyword">_</span> tableView: <span class="hljs-type">UITableView</span>) -> <span class="hljs-type">UITableViewCell</span>

    <span class="hljs-keyword">init</span>&#x3C;<span class="hljs-type">Model</span>: <span class="hljs-type">CellModel</span>>(<span class="hljs-keyword">_</span> <span class="hljs-params">model</span>: <span class="hljs-type">Model</span>)  {
        <span class="hljs-keyword">self</span>._tableViewCell <span class="hljs-operator">=</span> model.tableViewCell
    }

    <span class="hljs-comment">/// Conforming to `AnyCell` protocol</span>
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">tableViewCell</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">tableView</span>: <span class="hljs-type">UITableView</span>) -> <span class="hljs-type">UITableViewCell</span> {
        <span class="hljs-keyword">return</span> _tableViewCell(tableView)
    }
}

<span class="hljs-comment">/// `Concrete Type` of `CellModel`</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">ImageCellModel</span>: <span class="hljs-title class_">CellModel</span> {
    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">let</span> name: <span class="hljs-type">String</span>

    <span class="hljs-keyword">init</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">name</span>: <span class="hljs-type">String</span>) {
        <span class="hljs-keyword">self</span>.name <span class="hljs-operator">=</span> name
    }

    <span class="hljs-keyword">func</span> <span class="hljs-title function_">tableViewCell</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">tableView</span>: <span class="hljs-type">UITableView</span>) -> <span class="hljs-type">ImageTableViewCell</span> {
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> cell <span class="hljs-operator">=</span> tableView.dequeueReusableCell(withIdentifier: <span class="hljs-type">Cell</span>.id)
        <span class="hljs-keyword">as?</span> <span class="hljs-type">Cell</span> <span class="hljs-keyword">else</span> { <span class="hljs-built_in">fatalError</span>() }
        cell.titleLabel.text <span class="hljs-operator">=</span> <span class="hljs-keyword">self</span>.name.capitalized
        cell.thumbImageView.image <span class="hljs-operator">=</span> <span class="hljs-type">UIImage</span>(named: <span class="hljs-keyword">self</span>.name)
        <span class="hljs-keyword">return</span> cell
    }
}

<span class="hljs-comment">/// `Concrete Type` of `CellModel`</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">TextCellModel</span>: <span class="hljs-title class_">CellModel</span> {
    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">let</span> quote: <span class="hljs-type">String</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> author: <span class="hljs-type">String</span>

    <span class="hljs-keyword">init</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">quote</span>: <span class="hljs-type">String</span>, <span class="hljs-params">author</span>: <span class="hljs-type">String</span>) {
        <span class="hljs-keyword">self</span>.quote <span class="hljs-operator">=</span> quote
        <span class="hljs-keyword">self</span>.author <span class="hljs-operator">=</span> author
    }

    <span class="hljs-keyword">func</span> <span class="hljs-title function_">tableViewCell</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">tableView</span>: <span class="hljs-type">UITableView</span>) -> <span class="hljs-type">TextTableViewCell</span> {
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> cell <span class="hljs-operator">=</span> tableView.dequeueReusableCell(withIdentifier: <span class="hljs-type">Cell</span>.id)
        <span class="hljs-keyword">as?</span> <span class="hljs-type">Cell</span> <span class="hljs-keyword">else</span> { <span class="hljs-built_in">fatalError</span>() }
        cell.titleLabel<span class="hljs-operator">?</span>.text <span class="hljs-operator">=</span> <span class="hljs-keyword">self</span>.quote
        cell.subtitleLabel<span class="hljs-operator">?</span>.text <span class="hljs-operator">=</span> <span class="hljs-keyword">self</span>.author
        <span class="hljs-keyword">return</span> cell
    }
}
</code></pre>
<p>If you download the project shared below and run the app, you will see the app has two models <code>TextCellModel</code> and <code>ImageCellModel</code> displayed using two table cells <code>TextTableViewCell</code> and <code>ImageTableViewCell</code> respectively. As the app has different models and cells under a single TableView section, without <code>TypeErasure</code> we would face the following problems:</p>
<ul>
<li>Self or Associated Type Requirement</li>
</ul>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> items: [<span class="hljs-type">CellModel</span>] <span class="hljs-operator">=</span> [<span class="hljs-operator">&#x3C;</span>a text model<span class="hljs-operator">></span>,<span class="hljs-operator">&#x3C;</span>a image mode<span class="hljs-operator">></span>]
<span class="hljs-comment">/// Error explained above</span>
</code></pre>
<p><code>CellModel</code> has an associated type requirement to avoid spaghetti code while dequeuing TableCell. Without associated type requirement our code would be as:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">tableView</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">tableView</span>: <span class="hljs-type">UITableView</span>, <span class="hljs-params">cellForRowAt</span> <span class="hljs-params">indexPath</span>: <span class="hljs-type">IndexPath</span>) -> <span class="hljs-type">UITableViewCell</span> {  
   <span class="hljs-keyword">let</span> items <span class="hljs-operator">=</span> sections[indexPath.section].items  
   <span class="hljs-keyword">let</span> item <span class="hljs-operator">=</span> items[indexPath.row]
   <span class="hljs-comment">/// More the model types, more the if-else  </span>
   <span class="hljs-keyword">if</span> item <span class="hljs-keyword">is</span> <span class="hljs-type">TextCellModel</span> {  
   <span class="hljs-comment">/// load TextTableViewCell  </span>
   } <span class="hljs-keyword">else</span> {  
   <span class="hljs-comment">/// load ImageTableViewCell  </span>
   }  
}
</code></pre>
<ul>
<li>Heterogeneous Array</li>
</ul>
<p>As TableView has different models and cells under same section, without <code>TypeErasure</code> we had to populate different array for different model which will lead to lots of if-else. Instead let's do eligently:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> imageCell <span class="hljs-operator">=</span> <span class="hljs-type">AnyCell</span>(<span class="hljs-type">ImageCellModel</span>(<span class="hljs-string">"cyclamen"</span>))  
<span class="hljs-keyword">let</span> quoteCell <span class="hljs-operator">=</span> <span class="hljs-type">AnyCell</span>(<span class="hljs-type">TextCellModel</span>(<span class="hljs-string">"Hello World."</span>, author: <span class="hljs-string">"-"</span>))  
<span class="hljs-keyword">let</span> anyCells <span class="hljs-operator">=</span> [imageCell, quoteCell]
</code></pre>
<p>If we had just directly instantiated our <code>ImageCellModel</code> instance using the <code>ImageCellModel</code> initializer, it would be of type <code>ImageCellModel</code>. But because we have instantiated using this <code>AnyCell</code> wrapper class, <code>ImageCellModel</code> is now instantiated as type <code>AnyCell</code>. We have just erased type information (<em>this is what type erasure means</em>)</p>
<p>Wrapper classes are conventionally prefixed with the word <code>Any</code>, in order to guarantee that you will instantiate an object that implements our protocol and fills the generic type, without necessarily having the implementation on hand.</p>
<p>🎉 Using <code>AnyCell</code> wrapper we’ve erased the <code>Type</code> requirement when conforming to <code>CellModel</code> protocol. The <code>init</code> function is without clause and we now have a heterogeneous collection <code>Type</code> and dynamic dispatch at our disposition. 👍🏼</p>
<h2>Conclusion</h2>
<p>We often end up writing spaghetti code while implementing TableView or CollectionView with different types of cells and models. <code>TypeErasure</code> is the right choice to avoid spaghetti code, makes code much more organized, and increases readability. I hope that you have enjoyed this article.</p>
<h4></h4>
<p><a href="https://github.com/rokon-mlbd/SwiftTypeErasure">👑 KEEP CALM AND HERE IS MY CODE</a></p>
<h2>Related Articles:</h2>
<ul>
<li><a href="https://medium.com/dunnhumby-data-science-engineering/swift-associated-type-design-patterns-6c56c5b0a73a">Swift Associated Type Design Patterns</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2015/408/">Protocol-Oriented Programming in Swift</a></li>
<li><a href="https://www.natashatherobot.com/swift-type-erasure/">Swift: Attempting to Understand Type Erasure</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Receipt recognition using computer vision and deep learning]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2021/12/20/Receipt-recognition-using-computer-vision-and-deep-learning</link>
            <guid>https://engineering.monstar-lab.com/en/post/2021/12/20/Receipt-recognition-using-computer-vision-and-deep-learning</guid>
            <pubDate>Mon, 20 Dec 2021 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>In this article, we will demonstrate how computer vision and deep learning can achieve receipt recognition and enhancement of its observation. Moreover, we will present how training data was created in a semi-automated way using computer vision.</p>
<p>You should read this if you are interested in:</p>
<ul>
<li>Deep learning</li>
<li>Computer vision</li>
<li>Semi-automatic data generation</li>
</ul>
<h2>What's this about?</h2>
<p>Improvement of the usability and accessibility of AI-related technologies steadily increase their spread in our daily life tasks.
Thus, receipt recognition and understanding of its content has various applications such as human resource invoice check, accounting or document parsing and analysis.</p>
<p>In order to democratize its use, it is often easier to perform it using pictures from a smartphone. Unlike scanned receipts, which require a scanner machine, pictures taken from smartphones do have a lot of sources of noise (picture background) that makes the task of accurate receipt recognition harder.</p>
<img src="/assets/img/articles/2021-12-20-Receipt-recognition-using-computer-vision-and-deep-learning/figure0.webp">
<p>To achieve the goal of receipt recognition, two approaches are considered:</p>
<ul>
<li><strong>Computer vision</strong>, referred to as <strong>CV</strong> in this article: apply various transformations to the input image in order to retrieve receipt location in the image and enhance its observation.</li>
<li><strong>Deep learning</strong>, referred to as <strong>DL</strong> in this blog: retrieve receipt location in the image using a trained model to be able to enhance its observation.</li>
</ul>
<p>DL is sometimes overkill as CV can solve a problem much more efficiently and in fewer lines of code. However, CV does not work in every situation and use of DL can make the recognition of receipt more robust.</p>
<p>We will investigate both approaches in this blog to see their outcomes and conclude on how to achieve performant receipt recognition.</p>
<h2>Procedures</h2>
<p>The different steps of receipt recognition and enhancement of its observation are (technical terms are explained in the following section):</p>
<ol>
<li>Find the <strong>contour</strong> of the receipt in the image.</li>
<li>Apply <strong>affine transformation</strong> to vertically rotate the receipt.</li>
<li>Retrieve the corners of the receipt.</li>
<li>Apply <strong>homography transformation</strong> using corners and enclosing <strong>bounding box</strong> corners.</li>
<li>Compute the grid mapping of the receipt between its contour and the corresponding bounding box.</li>
<li>Apply <strong>thin plate spline transformation</strong> to undistort the receipt.</li>
</ol>
<p>Among the operations described above, step 1 is the most crucial as the contouring operation controls the success of the following operations.</p>
<p>Thus, step 1 will be achieved by using both CV and DL, while the following steps will be done using CV, only.</p>
<h2>Computer vision operations</h2>
<p>In the above section, the different steps for achieving receipt understanding and enhancement was shown. In this section, the technical explanation of each step is proposed.</p>
<p><strong>Contouring</strong> is a boundary detection operation around a shape that has edges. A contouring algorithm can calculate the gradient of surrounding pixel values to detect a closed shape. This is a well-known problem in both CV and DL. Using the contour of the receipt and enclosing bounding box, we will vertically rotate the receipt using affine transformation.
A <strong>bounding box</strong> is the smallest rectangle with vertical and horizontal edges that surrounds an object (a receipt in our use case).
An <strong>affine transformation</strong> in CV is a set of linear transformations such a translation or a rotation of an image.</p>
<figure>
<img src="/assets/img/articles/2021-12-20-Receipt-recognition-using-computer-vision-and-deep-learning/figure1.webp">
<figcaption>Figure 1. Original image, contouring &#x26; bounding boxes estimation, rotated receipt sentences.</figcaption>
</figure>
<p><strong>Homography operation</strong>, also referred as planar homography operation, is a transformation that maps points in one image so you can observe this image in a different point of view. This is used in this case to rectify the image and modify its yaw/pitch/roll angles, simply speaking, so you can observe it from the top.</p>
<figure>
<img src="/assets/img/articles/2021-12-20-Receipt-recognition-using-computer-vision-and-deep-learning/figure2.webp">
<figcaption>Figure 2. Original image and its modification of observation using homography.</figcaption>
</figure>
<p><strong>Thin plate spline operation</strong> is an operation that can be used to rectify image distortion by applying a spatial mapping from two sets of grids, while interpolating the values in these grids.</p>
<figure>
<img src="/assets/img/articles/2021-12-20-Receipt-recognition-using-computer-vision-and-deep-learning/figure3.webp">
<figcaption>Figure 3. Contour parsing analysis, source (blue dots) and destination (red dots) grids retrieval, and thin plate spline transformation to undistort the receipt.</figcaption>
</figure>
<p>Regarding the images above, it seems that CV could be sufficient to achieve ticket recognition and enhancement of its observation. However, Contouring depends on image background, receipt condition (crumple receipt) or bright spot of the image, among others.</p>
<p>Here is an example of missed contouring operations that leads to failure.</p>
<figure>
<img src="/assets/img/articles/2021-12-20-Receipt-recognition-using-computer-vision-and-deep-learning/figure4.webp">
<figcaption>Figure 4. Failed contouring operation (green) and bounding box estimation (red).</figcaption>
</figure>
<p>To overcome this issue, DL is considered as it can handle complex recognition tasks that CV could not perform.</p>
<h2>Deep learning operation</h2>
<h3>Instance segmentation</h3>
<p>Deep learning is a subset of artificial intelligence that aims to map input data through layered models to estimate variables as a classification or a regression problem. Input data can take the form of images, text, tabular data or time series signals. Output data can take any form we like it to take (segmentation mask, binary variable, vector, ...).</p>
<p>Instance segmentation is a subset of deep learning that consists in detecting distinct objects of interest appearing in an image. It treats multiple objects of the same class as distinct individual instances as it produces distinct per-pixel segmentation masks for each instance.</p>
<p>Its advantage in our case is that it gives a segmentation mask that can be used as a receipt contour even though we have multiple receipts in one image.</p>
<figure>
<img src="/assets/img/articles/2021-12-20-Receipt-recognition-using-computer-vision-and-deep-learning/figure5.webp">
<figcaption>Figure 5. Instance segmentation concept illustrated using COCO dataset samples.</figcaption>
</figure>
<p>Unlike CV, DL need training data to learn from, which we will discuss in the following section.</p>
<h3>Semi automated data generation</h3>
<p>The amount of data required to train a deep learning model widely varies depending on the task you aim to achieve. As receipts have a simple shape and are often contrasting with the background, we expect to get good results with a few hundred samples.</p>
<p>However, since there is no available online dataset for receipt segmentation, we should create these data from scratch. Labelling from scratch is a hassling task that most AI engineers prefer to delegate as it can be repetitive and time consuming.
In our situation, we could estimate the contour of a receipt in an image using CV so creation of segmentation masks will be based on this approach.</p>
<p>In case the CV contouring operation would not perform well, the segmentation mask would be edited using a polygon data labelling software.</p>
<p>Thus, creating training data still takes time (review data creation to ensure its quality) but making it semi-automatic alleviates most of the burden.</p>
<h2>Contouring via deep learning</h2>
<p>After training an instance segmentation model, problematic receipt images were tested.</p>
<figure>
<img src="/assets/img/articles/2021-12-20-Receipt-recognition-using-computer-vision-and-deep-learning/figure6.webp">
<figcaption>Figure 6. Instance segmentation results on problematic images.</figcaption>
</figure>
<p>Thanks to previously created training data that was used to train an instance segmentation model, receipt segmentation masks could be estimated where CV fails to do so. As the contouring step could be performed correctly, the following CV steps were applied successfully.</p>
<h2>Summary</h2>
<p>We demonstrated in this article how computer vision and deep learning could be combined or used separately to enhance recognition of a receipt from a picture taken from a smartphone.</p>
<p>Advantages of computer vision (straight forward use) could be used to remedy drawbacks of deep learning (need of training data) to create a robust receipt recognition solution.</p>
<p>From receipt recognition onward, computer vision steps were applied to enhance the observation of the receipt.</p>
<p>In order to improve the accuracy and robustness of our solution, the following tasks are considered for the deep learning approach:</p>
<ul>
<li>Add more data in our training dataset.</li>
<li>Benchmark different backbones for the instance segmentation model.</li>
<li>Refine the deep learning results with computer vision.</li>
</ul>
<p><em><a href="https://cocodataset.org/">Image source Figure 5: COCO dataset</a></em></p>
<p><em><a href="https://www.pexels.com/photo/person-using-a-computer-and-holding-a-credit-card-and-receipts-4968390/">Article Photo by Karolina Grabowska from Pexels</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Pen Testing The What, Why & When]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2021/12/17/Pen-Testing-The-What-Why-When</link>
            <guid>https://engineering.monstar-lab.com/en/post/2021/12/17/Pen-Testing-The-What-Why-When</guid>
            <pubDate>Fri, 17 Dec 2021 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>You've seen news stories of data breaches by mysterious hackers stealing information. That threatens to release sensitive information to the public unless they get paid. But did you know there is a proactive way of testing your security before the bad guys do?</p>
<p>Let's explore how you can protect yourself and dive into the what, why &#x26; when of pen-testing.</p>
<h2>What Is A Penetration Test?</h2>
<p>A penetration test, or pen test, is when a trained team of security experts tries to find your system's weak points.</p>
<p>Hackers are always trying to exploit vulnerabilities in cloud services and applications. Vulnerabilities may exist in insecure configurations, outdated dependencies, or even bad end-user behavior.</p>
<p>Security assessments help verify that your defenses are working as they should. It usually involves an attempt to compromise an app, servers, or IT infrastructure.</p>
<p>After finding a vulnerability, the security engineer will then try to escalate privileges. This means they will try to get access to sensitive or information they should not be able to access.</p>
<p>After the exploit succeeds, it then gets reported to the development team to patch.</p>
<h2>What Is the Difference Between Vulnerability Scans and Pen Tests?</h2>
<p>The terms “Vulnerability Scan” and “Pen Test” are sometimes used, but they refer to two different types of security tests.</p>
<p>Vulnerability scanners are just automated tools that examines an app's source code and generates a report upon completion.</p>
<p>Scanners often list these vulnerabilities based off CVE identifiers. CVE identifiers are a list of known weaknesses.</p>
<p>Scanners can discover vulnerabilities, some low to severe vulnerabilities. That's when prioritization of the vulnerabilities are needed.</p>
<p>Unfortunately, these scanners can result in false positives so just relying on them is not enough.</p>
<p>This is where penetration tests come in.</p>
<p>They are a great way to see if any issues may not have been caught by your routine security testing.</p>
<p>Penetration tests add additional context to vulnerability scans. By seeing if the discovered vulnerabilities could be leveraged to gain access.</p>
<p>Penetration tests help you prioritize remediation plans by identifying the areas to be protected.</p>
<h2>Why Is Pen Testing Even Important?</h2>
<h3>Finding and Prioritizing Vulnerabilities</h3>
<p>Pen testing exposes any weaknesses in your web apps and cloud infrastructure. This gives your the knowledge of where you weak points are and where you should be direct resources and in which order you should fix them.</p>
<h3>Managing Vulnerabilities In A Smart Way</h3>
<p>Pen testing gathers detailed information about security threats that can be exploited by actual hackers.</p>
<p>You can find out which vulnerabilities are most significant and worth addressing, which ones should be disregarded, and which ones may be false alarms by performing a penetration test.</p>
<p>This allows you to more thoroughly and efficiently evaluate risks, apply needed patches, and allocate resources to ensure that they are available when and where they are needed most.</p>
<h3>Taking A Proactive Approach To Your Security</h3>
<p>Today, it's clear that there is no one solution to prevent a security breach.</p>
<p>You must now have a portfolio of advanced security measures to protect against cyberattacks, including Firewalls, SIEM solutions, and IAM programs.</p>
<p>But even with these security tools, it can be difficult to find and eliminate every vulnerability in a network or web application.</p>
<p>Penetration testing takes a proactive approach, uncovering weaknesses so that organizations can know what needs to be remediated and if additional layers need to be implemented.</p>
<h3>See What's Working Vs What Is Not</h3>
<p>Too often, companies make changes to their security programs without first identifying the actual problems that need fixing.</p>
<p>Pen tests aren't only about finding vulnerabilities. You'll find out what policies are most effective and which tools give you the highest ROI.</p>
<p>These insights can help you allocate security resources wisely, ensuring that they are available when and where they are needed most.</p>
<h3>Be Confidence In Your Security Strategy</h3>
<p>How can you know your security posture is effective if you never test it?</p>
<p>You can avoid being blindsided by an attack when your security team puts your security infrastructure through its paces.</p>
<p>You'll have safely experienced teamwork and will know how to prepare a well-organized team for success with security.</p>
<h2>How Often Should You Test Your Security?</h2>
<p>You should perform penetration testing regularly to ensure a high level of application security.</p>
<p>Tests should also be performed if required by regulatory mandates, additional tests should be run whenever:</p>
<ul>
<li>You update a applications dependencies</li>
<li>You made major changes to your application's codebase</li>
<li>Adjustments are made to the infrastructure or hosting environments</li>
</ul>
<h2>What Should You Do After a Pen Test?</h2>
<p>When going through the results of pen tests, discuss plans for improving security and revisit your security posture overall.</p>
<p>Pen tests are important, but you shouldn’t just scan them for vulnerabilities and check them off a list.</p>
<p>Make time for a post-mortem to talk about what was discovered, share the results, and learn from them.</p>
<p>We suggest that you provide actionable information to decision-makers within your company. As this will help them understand the impact that vulnerabilities pose and the positive impact of remediation.</p>
<p>Review, evaluation, and leadership buy-in can transform pen test results into action items for improvements and takeaways that help shape larger security strategies.</p>
<h2>Want Your Security Tested?</h2>
<p>Here at Monstarlab, we have recently created a new cybersecurity division that can conduct web, API &#x26; mobile security tests.</p>
<p>If you would like to learn more about how Monstarlab could help you improve your security <a href="https://monstar-lab.com/global/contact/">please contact us here</a></p>
<p><em>Article Photo by <a href="https://pixabay.com/users/b_a-363247/">B_A</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Firebase Dynamic Links + Flutter]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2021/11/29/Firebase-Dynamic-Links-+-Flutter</link>
            <guid>https://engineering.monstar-lab.com/en/post/2021/11/29/Firebase-Dynamic-Links-+-Flutter</guid>
            <pubDate>Mon, 29 Nov 2021 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>This article explains how to make use of Dynamic Links in Flutter through a step by step guide.</p>
<h2>What are Dynamic Links?</h2>
<p>Dynamic Links are smart URLs that lead to a specific screen in our application. They can be easily used in an outsider website/app and are more commonly known as deep links.</p>
<p><img src="/assets/img/articles/2021-11-29-Firebase-Dynamic-Links-+-Flutter/image01.gif" alt=""></p>
<h2>Why Firebase Dynamic Links?</h2>
<p>Why Firebase? Could we not implement this by ourselves? Do we really need to rely on another service to do that? These were some of the questions we asked ourselves before we made a decision to go with Firebase. Luckily those questions were easily answered by watching the video below.</p>
<p><div class="embed-container">
      <iframe
          src="https://www.youtube.com/embed/LvY1JMcrPF8"
          width="700"
          height="480"
          frameborder="0"
          allowfullscreen="">
      </iframe>
    </div>
    </p>
<p>So, why Firebase Dynamic Links are a really good option to go with:</p>
<ul>
<li><strong>They work in and adapt to all platforms (iOS, Android, Web):</strong> one link works perfectly between all platforms.</li>
<li><strong>App not-installed behavior can be adjusted:</strong> we can lead the users to a landing page or the platform (iOS, Android) store in case they don’t have our app installed.</li>
<li><strong>The flow doesn’t die after app installation:</strong> even if the user does not have our app installed, after installation and opening the app for the first time; the Dynamic Link works.</li>
<li><strong>Easily used in combination with Firebase Analytics:</strong> we can use Firebase Analytics to count the number of times a Dynamic Link is used.</li>
</ul>
<h2>Getting started</h2>
<p>In this article we are only going to elaborate on how to implement Dynamic Links with Flutter. In other words, the below steps are necessary and we are not going to get into detail on how to do so.</p>
<ul>
<li>Create a Firebase project</li>
<li>Add the necessary iOS and Android apps</li>
<li>Configure the iOS and Android apps in Flutter to use Firebase</li>
</ul>
<p>Before going further, let’s make sure the above steps are finished!</p>
<h2>Step 1: Create a prefix URL</h2>
<p>A Dynamic Link is defined by a URL, and that is what we are going to do first. Define the URL that is going to be used in all our links.</p>
<p>To specify our URL, we go to:</p>
<p><em>Firebase Console > Dynamic Links > Get started</em></p>
<p><img src="/assets/img/articles/2021-11-29-Firebase-Dynamic-Links-+-Flutter/image02.webp" alt=""></p>
<p>Here is where we are going to set up our prefix URL. This prefix will be the one showing in all our Dynamic Links and should be explicit to our app. Therefore we should choose something clear and precise. For this example we decided on the prefix below:</p>
<p><em><strong></strong></em></p><p style="text-align: center;"><em><strong><a href="https://app.monstarlabdynamiclinksdemo.com/">https://app.monstarlabdynamiclinksdemo.com/</a></strong></em></p><p></p>
<p>Subsequent links from this prefix URL will look like the one below:</p>
<p><em><strong></strong></em></p><p style="text-align: center;"><em><strong><a href="https://app.monstarlabdynamiclinksdemo.com/home">https://app.monstarlabdynamiclinksdemo.com/home</a></strong></em></p><p></p>
<p>Along with an explicit prefix URL, we have to own that domain too, due to the fact that we need to prove that ownership on the <strong>Verify</strong> step.</p>
<p><img src="/assets/img/articles/2021-11-29-Firebase-Dynamic-Links-+-Flutter/image03.webp" alt=""></p>
<p>Once we get to the <strong>Finish</strong> step of the <strong>Get started</strong> guide, we should have a prefix URL ready to create Dynamic Links for our app.</p>
<h2>Step 2: Install</h2>
<p>To implement the Dynamic Links in our Flutter app, we are going to make use of the firebase_dynamic_links library.</p>
<p><a href="https://pub.dev/packages/firebase_dynamic_links">https://pub.dev/packages/firebase_dynamic_links</a></p>
<p>Running the command below in our terminal will install the library into our Flutter project.</p>
<pre><code class="hljs language-terminal">flutter pub add firebase_dynamic_links
</code></pre>
<p>After installation, to receive Dynamic Links on Android nothing more should be done. However, iOS devices need some more configuration. In the next sections we are going to explain the three procedures that made our app capable of opening Dynamic Links successfully.</p>
<h4>Step 2.1: Universal Link configuration</h4>
<p>The first configuration step will be to add the Associated Domains capability to our iOS app. To do so, we need to open our Runner.workshop in Xcode and go to:</p>
<p><em>Project root > App target > Signing &#x26; Capabilities > + Capability > Associated Domains</em></p>
<p><img src="/assets/img/articles/2021-11-29-Firebase-Dynamic-Links-+-Flutter/image04.webp" alt=""></p>
<p><img src="/assets/img/articles/2021-11-29-Firebase-Dynamic-Links-+-Flutter/image05.webp" alt=""></p>
<p>We will set the <strong>Associated Domains>Domains</strong> as <strong>applinks:(URL prefix)</strong>.</p>
<pre><code class="hljs language-terminal">applinks:app.monstarlabdynamiclinksdemo.com
</code></pre>
<p>After this change, we will need to update the configuration of our App ID (Apple dev center), adding the Associated Domains capability as well, and update our provisioning profile accordingly. Otherwise we will not be able to test our Dynamic Links in iOS.</p>
<h4>Step 2.2: Support Custom URL schemes</h4>
<p>For our second configuration step we need to support the custom URL schemes in our iOS app. We need to go to:</p>
<p><em>Project root > App target > Info > URL Types > +</em></p>
<p><img src="/assets/img/articles/2021-11-29-Firebase-Dynamic-Links-+-Flutter/image06.webp" alt=""></p>
<p>Here we need to set the <strong>Identifier</strong> field as <strong>Bundle ID</strong> and the <strong>URL Schemes</strong> field with our iOS app bundle ID.</p>
<pre><code class="hljs language-terminal">com.monstarlab.monstarlabDynamicLinksDemo
</code></pre>
<h4>Step 2.3: Info.plist configuration</h4>
<p>For the third and final configuration step we will add a <strong>FirebaseDynamicLinksCustomDomains</strong> Array into our app <strong>Info.plist</strong>.</p>
<p><img src="/assets/img/articles/2021-11-29-Firebase-Dynamic-Links-+-Flutter/image07.webp" alt=""></p>
<p>Once the array has been added, we will also need to insert a <strong>String item</strong> into the array with our URL prefix.</p>
<pre><code class="hljs language-terminal">https://app.monstarlabdynamiclinksdemo.com/
</code></pre>
<p>At this point we should be ready to go. Nevertheless, iOS configuration is one of the troublesome tasks dealing with Dynamic Links. If you have difficulties trying to configure the Dynamic Links for iOS, especially when opening them on a device later in this tutorial. Return here and check the link below, it was a great help for us.</p>
<p><div class="embed-container">
      <iframe
          src="https://www.youtube.com/embed/KLBjAg6HvG0"
          width="700"
          height="480"
          frameborder="0"
          allowfullscreen="">
      </iframe>
    </div>
    </p>
<h2>Step 3: Generate Dynamic Links (Immutable url)</h2>
<p>In the previous steps we created a prefix URL that works with our app, but that is not enough. As stated before, Dynamic Links are smart URLs that lead to a specific screen in our application. We need to define the last part of our URL that leads to that specific screen.</p>
<p>In this first part of step 3, we are going to focus on links that refer to a screen that does not need additional information to show. Examples of those links could be:</p>
<p><em><strong></strong></em></p><p style="text-align: center;"><em><strong><a href="https://app.monstarlabdynamiclinksdemo.com/feed">https://app.monstarlabdynamiclinksdemo.com/feed</a></strong></em></p><p></p>
<p><em><strong></strong></em></p><p style="text-align: center;"><em><strong><a href="https://app.monstarlabdynamiclinksdemo.com/premium">https://app.monstarlabdynamiclinksdemo.com/premium</a></strong></em></p><p></p>
<p><em><strong></strong></em></p><p style="text-align: center;"><em><strong><a href="https://app.monstarlabdynamiclinksdemo.com/profile">https://app.monstarlabdynamiclinksdemo.com/profile</a></strong></em></p><p></p>
<p>This kind of link can be easily created from the Firebase Console:</p>
<p><em>Firebase Console > Dynamic Links section > New Dynamic Link</em></p>
<p><img src="/assets/img/articles/2021-11-29-Firebase-Dynamic-Links-+-Flutter/image08.webp" alt=""></p>
<p>Clicking this button will trigger a window with a series of steps explained in the following sections.</p>
<h4>Step 3.1: Short URL</h4>
<p>In this first step we need to set up the last part of our short Dynamic Link URL.</p>
<p><img src="/assets/img/articles/2021-11-29-Firebase-Dynamic-Links-+-Flutter/image09.webp" alt=""></p>
<p>For an immutable URL a short link may not be that useful. Moreover, at this point it may be difficult to understand why they are used for. Thus, we recommend leaving the field with the random string as is and continuing to the next step.</p>
<h4>Step 3.2: Dynamic Link setup</h4>
<p><img src="/assets/img/articles/2021-11-29-Firebase-Dynamic-Links-+-Flutter/image10.webp" alt=""></p>
<p>In this second step we need to set up the last part of our <strong>Deep link URL</strong>. This will become our actual Dynamic Link. Anything should be fine, but we recommend it to be clear and explicit to the screen we are referring to.</p>
<p>In addition to the URL path, a <strong>Dynamic Link name</strong> is required. This Dynamic Link name is for internal use, it is used to help us identify links easier. This name will show on the Firebase console Dynamic Links list next to the URL.</p>
<h4>Step 3.3: iOS behavior</h4>
<p><img src="/assets/img/articles/2021-11-29-Firebase-Dynamic-Links-+-Flutter/image11.webp" alt=""></p>
<p>For the third step we will need to configure the behavior of our link when opening the smart link on an iOS device. We want our link to open a specific screen in our app, for that we need to specify <strong>Open the deep link in your iOS App</strong>.</p>
<p>Likewise, we also can set the behavior if the user does not have our app installed. In our case, we decided to show the <strong>App Store page for your app</strong>, and guide them to install our app. Similarly we could choose <strong>Custom URL</strong> and define a URL to fall through to instead.</p>
<h4>Step 3.4: Android behavior</h4>
<p><img src="/assets/img/articles/2021-11-29-Firebase-Dynamic-Links-+-Flutter/image12.webp" alt=""></p>
<p>For the fourth step we will need to configure the behavior of our link when opening the Dynamic Link on an Android device. As in the previous step, we want to open a specific screen in our app with the Dynamic Link, for that we need to select the <strong>Open the deep link in your Android App</strong>.</p>
<p>We also selected the <strong>Google Play page for your app</strong> to guide the users to the Play Store in case they don’t have the app installed.</p>
<h4>Step 3.5: Advanced options</h4>
<p>In the fifth and final step we are going to add a bunch of optional parameters to our Dynamic Link.</p>
<p><img src="/assets/img/articles/2021-11-29-Firebase-Dynamic-Links-+-Flutter/image13.webp" alt=""></p>
<p><strong>Meta tag parameters</strong> are easily used on social media, when presenting links in their interfaces. Setting up those parameters will allow us to show our link more appealingly, rather than a plain URL.</p>
<p>On the other hand, the <strong>UTM parameters</strong> (see image above) are set to be used along with Firebase Analytics.</p>
<h4>App preview page</h4>
<p>The app preview page is an html page between the generated Dynamic Link and the app screen. This html page is shown in the user device default browser and also presents the social meta tag parameters that were set in the last step (title, image and description).</p>
<p><img src="/assets/img/articles/2021-11-29-Firebase-Dynamic-Links-+-Flutter/image14.webp" alt=""></p>
<p>App preview pages are usually useful because they let the user decide if they really want to open the link or not. In case we don’t want to show it, we can skip it by checking the <strong>Skip the app preview page (not recommended)</strong> checkbox.</p>
<h2>Step 3: Generate Dynamic Links (Mutable links)</h2>
<p>Continuing with Dynamic Link generation, we learnt how to generate links that refer to a screen that does not need additional information to show. But what if they need some changing parameters to be specified? In this second part we are going to focus on mutating links and how they are generated.</p>
<p>Examples of those links would be:</p>
<p><em><strong></strong></em></p><p style="text-align: center;"><em><strong><a href="https://app.monstarlabdynamiclinksdemo.com/detail/9875">https://app.monstarlabdynamiclinksdemo.com/detail/9875</a></strong></em></p><p></p>
<p><em><strong></strong></em></p><p style="text-align: center;"><em><strong><a href="https://app.monstarlabdynamiclinksdemo.com/news/305">https://app.monstarlabdynamiclinksdemo.com/news/305</a></strong></em></p><p></p>
<p><em><strong></strong></em></p><p style="text-align: center;"><em><strong><a href="https://app.monstarlabdynamiclinksdemo.com/profile/monstarlab">https://app.monstarlabdynamiclinksdemo.com/profile/monstarlab</a></strong></em></p><p></p>
<p>For these cases we will need to generate our deep links with code.</p>
<h4>Coding with firebase_dynamic_links library</h4>
<p>To create a mutating Dynamic Link URL we are going to make use of the <strong>DynamicLinkParameters</strong> class of the firebase_dynamic_links library.</p>
<p>The code below is an example of a deep link creation using the DynamicLinkParameters.</p>
<pre><code class="hljs language-dart"><span class="hljs-keyword">final</span> DynamicLinkParameters parameters =
DynamicLinkParameters(
    uriPrefix: <span class="hljs-string">'https://app.monstarlabdynamiclinksdemo.com/'</span>,
    link: <span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'https://app.monstarlabdynamiclinksdemo.com/detail/<span class="hljs-subst">$id</span>'</span>),
    androidParameters: AndroidParameters(
      packageName: <span class="hljs-string">'com.monstarlab.monstarlab_dynamic_links_demo'</span>,
    ),
    iosParameters: IosParameters(
      bundleId: <span class="hljs-string">'com.monstarlab.monstarlabDynamicLinksDemo'</span>,
      appStoreId: <span class="hljs-string">'962194608'</span>,
    ),
);
<span class="hljs-keyword">final</span> <span class="hljs-built_in">Uri</span> dynamicUrl = <span class="hljs-keyword">await</span> parameters.buildUrl();
</code></pre>
<p>The parameters specified in the piece of code are mandatory in order to have a Dynamic Link working properly:</p>
<ul>
<li><strong>uriPrefix:</strong> specifies the URL prefix.</li>
</ul>
<pre><code class="hljs language-terminal">http://app.monstarlabdynamiclinksdemo.com/
</code></pre>
<ul>
<li><strong>link:</strong> specifies the Dynamic Link URL. The below “id” is a value that changes depending on each case/screen.</li>
</ul>
<pre><code class="hljs language-terminal">https://app.monstarlabdynamiclinksdemo.com/detail/$id
</code></pre>
<ul>
<li><strong>androidParameters > packageName:</strong> specifies the Android package name set on the Firebase console.</li>
</ul>
<pre><code class="hljs language-terminal">com.monstarlab.monstarlab_dynamic_links_demo
</code></pre>
<ul>
<li><strong>iosParameters > bundleId &#x26; appStoreId:</strong> specifies the iOS bundle id set on the Firebase console.</li>
</ul>
<pre><code class="hljs language-terminal">com.monstarlab.monstarlabDynamicLinksDemo
</code></pre>
<p>And the App Store id, also set on the Firebase console. In case you still don’t own an App Store id, you can use the one below. It is the Google Photos App Store id.</p>
<pre><code class="hljs language-terminal">962194608
</code></pre>
<h4>Short links</h4>
<pre><code class="hljs language-dart"><span class="hljs-keyword">final</span> <span class="hljs-built_in">Uri</span> dynamicUrl = <span class="hljs-keyword">await</span> parameters.buildUrl();
</code></pre>
<p>After setting the parameters following the previous section and executing the code above, our mutating Dynamic Link would look something like this:</p>
<p><em><strong></strong></em></p><p style="text-align: center;"><em><strong><a href="https://app.monstarlabdynamiclinksdemo.com/?link=https://app.monstarlabdynamiclinksdemo.com/detail/9234&#x26;apn=com.monstarlab.monstarlab_dynamic_links_demo&#x26;ibn=com.monstarlab.monstarlabDynamicLinksDemo&#x26;isi=962194608">https://app.monstarlabdynamiclinksdemo.com/?link=https://app.monstarlabdynamiclinksdemo.com/detail/9234&#x26;apn=com.monstarlab.monstarlab_dynamic_links_demo&#x26;ibn=com.monstarlab.monstarlabDynamicLinksDemo&#x26;isi=962194608</a></strong></em></p><p></p>
<p>A little bit too long for a handy deep link, huh!? Especially for a tweet.</p>
<p>Here is where we can make good use of a short link.</p>
<pre><code class="hljs language-dart"><span class="hljs-keyword">final</span> ShortDynamicLink shortDynamicLink = <span class="hljs-keyword">await</span> parameters.buildShortLink();
<span class="hljs-keyword">final</span> <span class="hljs-built_in">Uri</span> shortUrl = shortDynamicLink.shortUrl;
</code></pre>
<p>By using the previously generated parameters variable we can create a short url looking like the one below:</p>
<p><em><strong></strong></em></p><p style="text-align: center;"><em><strong><a href="https://t.co/9YPgIeGACb">https://t.co/9YPgIeGACb</a></strong></em></p><p></p>
<p>The deep link will work equally well as the first one and will have the same properties, but it is shorter.</p>
<h4>Platform additional parameters</h4>
<p>There is a series of additional parameters that we can set on the Android and iOS parameters.</p>
<pre><code class="hljs language-dart"><span class="hljs-keyword">final</span> DynamicLinkParameters parameters =
DynamicLinkParameters(...),
    androidParameters: AndroidParameters(
      packageName: <span class="hljs-string">'com.monstarlab.monstarlab_dynamic_links_demo'</span>,
      minimumVersion: <span class="hljs-number">21</span>
      fallbackUrl: <span class="hljs-string">'https://www.monstar-lab.com'</span>,
    ),
    iosParameters: IosParameters(
      bundleId: <span class="hljs-string">'com.monstarlab.monstarlabDynamicLinksDemo'</span>,
      appStoreId: <span class="hljs-string">'962194608'</span>,
      minimumVersion: <span class="hljs-string">'12.0'</span>
      fallbackUrl: <span class="hljs-string">'https://www.monstar-lab.com'</span>,
    ),
);

<span class="hljs-keyword">final</span> <span class="hljs-built_in">Uri</span> dynamicUrl = <span class="hljs-keyword">await</span> parameters.buildUrl();
</code></pre>
<p>Same as when we created the link through the Firebase console, we can specify a url to show in case the user does not have our app installed. If <strong>fallbackUrl</strong> is not set then the platform store (App store, Play store) will show instead.</p>
<p>Using the <strong>minimumVersion</strong> we are also able to specify the minimum OS version which our link should work with.</p>
<h4>Social meta tag parameters</h4>
<p>To make use of the meta tag parameters on a code generated Dynamic Link we will need to set the <strong>socialMetaTagParameters</strong>. Here an example of code:</p>
<pre><code class="hljs language-dart"><span class="hljs-keyword">final</span> DynamicLinkParameters parameters =
DynamicLinkParameters(...),
    androidParameters: AndroidParameters(...),
    iosParameters: IosParameters(...),
    socialMetaTagParameters: SocialMetaTagParameters(
      title: ‘title’,
      description: ‘description’,
      imageUrl: <span class="hljs-built_in">Uri</span>.parse(imageUrl),
    ),
);

<span class="hljs-keyword">final</span> <span class="hljs-built_in">Uri</span> dynamicUrl = <span class="hljs-keyword">await</span> parameters.buildUrl();
</code></pre>
<h2>Step 4: Open Dynamic Links</h2>
<p>Once we have our Dynamic Links properly generated, we will need to prepare our app to receive them.</p>
<p>Below is the needed code to make our app responsive when receiving a Dynamic Link.</p>
<pre><code class="hljs language-dart">FirebaseDynamicLinks.instance.onLink(onSuccess: (dynamicLink) <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">Uri</span> deepLink = dynamicLink?.link;
  <span class="hljs-keyword">if</span> (deepLink != <span class="hljs-keyword">null</span>) {
    <span class="hljs-comment">// Show desired screen</span>
  }
});
</code></pre>
<p>We should place it somewhere the app goes through when coming from the background, in our case we placed it in our root view.</p>
<p>As discussed previously, Firebase Dynamic Links permits the Dynamic Link flow to continue after installation. In order to make that possible, we need to make use of the code below.</p>
<pre><code class="hljs language-dart"><span class="hljs-keyword">final</span> PendingDynamicLinkData data = <span class="hljs-keyword">await</span> FirebaseDynamicLinks.instance.getInitialLink();
<span class="hljs-keyword">final</span> <span class="hljs-built_in">Uri</span> deepLink = data?.link;
<span class="hljs-keyword">if</span> (deepLink != <span class="hljs-keyword">null</span>) {
	<span class="hljs-comment">// Show desired screen</span>
}
</code></pre>
<p><strong>getInitialLink()</strong> will let us know if the user has clicked a link before installation and will provide us with it.</p>
<p>To finish with the link opening we just need to show the pertinent screen. Below an example of code showing a screen using our previous Dynamic Link.</p>
<p><em><strong></strong></em></p><p style="text-align: center;"><em><strong><a href="https://app.monstarlabdynamiclinksdemo.com/?link=https://app.monstarlabdynamiclinksdemo.com/detail/9234&#x26;apn=com.monstarlab.monstarlab_dynamic_links_demo&#x26;ibn=com.monstarlab.monstarlabDynamicLinksDemo&#x26;isi=962194608">https://app.monstarlabdynamiclinksdemo.com/?link=https://app.monstarlabdynamiclinksdemo.com/detail/9234&#x26;apn=com.monstarlab.monstarlab_dynamic_links_demo&#x26;ibn=com.monstarlab.monstarlabDynamicLinksDemo&#x26;isi=962194608</a></strong></em></p><p></p>
<pre><code class="hljs language-dart"><span class="hljs-keyword">if</span> (deepLink.contains(<span class="hljs-string">'detail'</span>)) {
  <span class="hljs-keyword">final</span> strings = deepLink.split(<span class="hljs-string">'detail/'</span>);
  navigatorKeys[TabPage.home].push(
    MaterialPageRoute(
      builder: (context) => LectureDetailView(strings.last),
    ),
  );
}
</code></pre>
<h2>Testing</h2>
<p>Arriving at this point we are practically done, we only need to check if the generated Dynamic Link properly opens on our app and shows the right screen.</p>
<p>Remember that Dynamic Links cannot be opened on the browser, that means we cannot copy paste them on the address bar and hit enter because that is not going to work. They need to be clicked. Our recommendation is to use a Notes app or a tweet on Twitter.</p>
<p><strong></strong></p><p style="color: indianred;"><strong>Besides that, deep links can be opened on Android emulators and Android devices. But they can only be tested on iOS devices, iOS simulators will not work.</strong></p><p></p>
<p>Last but not least, we found an issue while working with iOS 14 and a VPN that is worth mentioning here. Let’s make sure to enable “Associated Domains Development” on the “Developer menu” in “iOS Settings” if we are using a VPN connection. Otherwise the opening of deep links will generate no response at all. We leave the link to the bug discussion thread below.</p>
<p><u><a href="https://developer.apple.com/forums/thread/659156">https://developer.apple.com/forums/thread/659156</a></u></p>
<h2>Conclusion</h2>
<p>To sum up, generating links with Firebase is super powerful and kind of easy, the worst part is definitely the iOS configuration.</p>
<p>The Flutter firebase_dynamic_links library has also a very handy URL generation, but there are so many options that it is difficult to understand all at first glance.</p>
<p>Clearly it is a tool that brings a better experience to the end user and empowers developers. Making everybody happier.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[TESTERS + DEVELOPERS = LOVE FOREVER]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2021/10/28/Developers-Tester-Love-Forever</link>
            <guid>https://engineering.monstar-lab.com/en/post/2021/10/28/Developers-Tester-Love-Forever</guid>
            <pubDate>Thu, 28 Oct 2021 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Let’s imagine an IDEAL world where all Developers test their code and the level of coverage is 100%.</p>
<p>Why should QA Engineers test when each line in the code (each decision, each condition, each branch) is covered by unit and integration tests?</p>
<p>They could test characteristics that cannot be covered by unit/integration tests, for example:</p>
<ul>
<li>Usability</li>
<li>Performance</li>
<li>Portability</li>
<li>Etc.</li>
</ul>
<p>And this is true but frankly speaking this is not the whole truth.</p>
<p>No method for calculating the level of coverage can be used to fully guarantee that the application functions as expected. The percentage of coverage is to know how many code lines are executed during the testing.</p>
<p>The simplest example is when requirements have been understood incorrectly.</p>
<p>Here is one more example.</p>
<p>There are several methods of calculating the level of coverage. For example, to achieve 100% decision (branch) coverage, each decision in the code should have “true” and “false” value at least once.</p>
<p><em>Console application that determines if it is possible to build a triangle with sides entered by user:</em></p>
<pre><code class="hljs language-csharp"><span class="hljs-keyword">using</span> System;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">Is_it_a_Triangle</span>
{
    <span class="hljs-keyword">class</span> <span class="hljs-title">Program</span>
    {
        <span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Main</span>(<span class="hljs-params"><span class="hljs-built_in">string</span>[] args</span>)</span>
        {
            <span class="hljs-built_in">int</span> a = <span class="hljs-built_in">int</span>.Parse(Console.ReadLine());
            <span class="hljs-built_in">int</span> b = <span class="hljs-built_in">int</span>.Parse(Console.ReadLine());
            <span class="hljs-built_in">int</span> c = <span class="hljs-built_in">int</span>.Parse(Console.ReadLine());

            <span class="hljs-keyword">if</span> (Triangle.IsValidTriangle(a, b, c))
            {
                Console.WriteLine(<span class="hljs-string">$"It is possible to build a triangle with sides <span class="hljs-subst">{a}</span>, <span class="hljs-subst">{b}</span>, and <span class="hljs-subst">{c}</span>."</span>);
            }
            <span class="hljs-keyword">else</span>
            {
                Console.WriteLine(<span class="hljs-string">$"It is impossible to build a triangle with sides <span class="hljs-subst">{a}</span>, <span class="hljs-subst">{b}</span>, and <span class="hljs-subst">{c}</span>."</span>);
            }
        }
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Triangle</span>
    {

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Triangle</span>(<span class="hljs-params"><span class="hljs-built_in">int</span> a, <span class="hljs-built_in">int</span> b, <span class="hljs-built_in">int</span> c</span>)</span>
        {
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-built_in">bool</span> <span class="hljs-title">IsValidTriangle</span>(<span class="hljs-params"><span class="hljs-built_in">int</span> a, <span class="hljs-built_in">int</span> b, <span class="hljs-built_in">int</span> c</span>)</span>
        {
            <span class="hljs-keyword">return</span> ((a + b > c) &#x26;&#x26; (a + c > b) &#x26;&#x26; (a + c > b));
        }
    }
}
</code></pre>
<p>So, only two tests are necessary to achieve 100% coverage.</p>
<p><em>Tests:</em></p>
<pre><code class="hljs language-csharp"><span class="hljs-keyword">using</span> Xunit;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">Is_it_a_Triangle.tests</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">TriangleTests</span>
    {
        [<span class="hljs-meta">Fact</span>]
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">IsValidTriangleTest1</span>()</span>
        {
            <span class="hljs-built_in">int</span> a = <span class="hljs-number">2</span>;
            <span class="hljs-built_in">int</span> b = <span class="hljs-number">2</span>;
            <span class="hljs-built_in">int</span> c = <span class="hljs-number">3</span>;

            <span class="hljs-built_in">bool</span> isValidTriangle = Triangle.IsValidTriangle(a, b, c);

            Assert.True(isValidTriangle);           
        }

        [<span class="hljs-meta">Fact</span>]
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">IsValidTriangleTest2</span>()</span>
        {
            <span class="hljs-built_in">int</span> a = <span class="hljs-number">1</span>;
            <span class="hljs-built_in">int</span> b = <span class="hljs-number">1</span>;
            <span class="hljs-built_in">int</span> c = <span class="hljs-number">3</span>;

            <span class="hljs-built_in">bool</span> isValidTriangle = Triangle.IsValidTriangle(a, b, c);

            Assert.False(isValidTriangle);
        }
    }
}
</code></pre>
<p>What if we add one more test?</p>
<pre><code class="hljs language-csharp">        [<span class="hljs-meta">Fact</span>]
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">IsValidTriangleTest3</span>()</span>
        {
            <span class="hljs-built_in">int</span> a = <span class="hljs-number">3</span>;
            <span class="hljs-built_in">int</span> b = <span class="hljs-number">1</span>;
            <span class="hljs-built_in">int</span> c = <span class="hljs-number">1</span>;

            <span class="hljs-built_in">bool</span> isValidTriangle = Triangle.IsValidTriangle(a, b, c);

            Assert.False(isValidTriangle);
        }
</code></pre>
<p>It won’t pass.
The problem is in the following line:</p>
<pre><code class="hljs language-csharp"><span class="hljs-keyword">return</span> ((a + b > c) &#x26;&#x26; (a + c > b) &#x26;&#x26; (a + c > b));
</code></pre>
<p>“(a + c > b)” condition is added two times. Instead, it should be:</p>
<pre><code class="hljs language-csharp"><span class="hljs-keyword">return</span> ((a + b > c) &#x26;&#x26; (a + c > b) &#x26;&#x26; (b + c > a));
</code></pre>
<p>This is a very simple program, but even here the user could enter numbers like doubles or maybe not numbers at all. That will cause errors in addition to the one I described above.</p>
<p>Kudos to Developers who test their code. My hat is off to them. And this is all because we live in a REAL world where Developers don’t have enough time for creating and maintaining tests, they take care of more than 1 project and have several meetings per day, where requirements are changing too often.</p>
<p>I like to be on the same project with Developers who want to produce qualitative products and not only to complete their tasks.</p>
<p>There are so many opportunities how Testers can support Developers:</p>
<ul>
<li>to prepare test data</li>
<li>to advice if they have knowledge about similar feature on another project</li>
<li>to use different techniques to prepare tests (to not skip most of bugs)</li>
<li>to give an overview of the functionality which is going to be fixed or increased</li>
<li>to see light at the end of the tunnel and let everyone else see it too</li>
</ul>
<p>There are a lot of test design techniques (specification-based and experience-based) in our arsenal.</p>
<p>For example, Equivalence Partitioning is the best for smoke testing. Usually it is used in pair with Boundary Value Analysis.
Decision Table Testing is good not only for creating tests but it also helps to determine gaps in the requirements.
State Transition Testing is the best when you need to perform security testing (but not only there).
There are tools that help in Pairwise Testing. This technique solves the problem of having too many combinations of parameter values.</p>
<p>I use them for each feature I am testing. Sometimes, when I don’t have enough time to create test cases, my tests looks like the following:</p>
<p><img src="/assets/img/articles/2021-10-28-Developers-Tester-Love-Forever/testcases.webp" alt=""></p>
<p>Once I have some free time I add these tests to our Test Management system.</p>
<p>Additionally, understanding test design techniques helps to not perform superfluous tests and save time for more needed tests.</p>
<p>Let’s not forget about experience-based techniques. To be honest these techniques are just an addition to the more formal ones, but in the right hands they are powerful weapons.</p>
<p>Each QA Engineer has his own list of common bugs in addition to well-known ones that may be found by Google.</p>
<p>Some time ago (when I was preparing for the “Mobile Application Testing Foundation Level” exam), I learned about such an exploratory testing technique as Mnemonics.</p>
<p>An example of a mnemonic is SFiDPOT (taken from <a href="https://www.istqb.org/downloads/send/61-mobile-application-testing/251-mobile-application-testing-specialist-syllabus.html">here</a>). Each letter has its own meaning:</p>
<ul>
<li><strong>S – Structure</strong> (e.g., user interface elements, other application elements and their order and call hierarchy)</li>
<li><strong>F – Function</strong> (e.g., desired features are working, available, and functioning according to the requirements etc.)</li>
<li><strong>i - Input</strong> (e.g., all required inputs are available and processed as they should be, such as inputs from the keyboard, sensors, and camera)</li>
<li><strong>D – Data</strong> (e.g., the data is stored (also on SD card), modified, added, and deleted as defined in the requirements)</li>
<li><strong>P – Platform</strong> (e.g., the specific operating system functions are available depending on device settings, includes store for downloading the app)</li>
<li><strong>O – Operations</strong> (e.g., the activities of the normal user are available, such as moving between mobile carrier networks and Wi-Fi)</li>
<li><strong>T – Time</strong> (e.g., handling and display of time zones, time, and dates)</li>
</ul>
<p>One more interesting technique is Tours. Here is not full list but those which I found more useful:</p>
<ul>
<li><strong>Connectivity</strong> - Connectivity used, such as Wi-Fi, GSM</li>
<li><strong>Location</strong> - Correct language, dates, numbers</li>
<li><strong>Low battery</strong> - Data losses in the app caused by low energy levels</li>
<li><strong>Gesture</strong> - Use all gestures wherever possible</li>
<li><strong>Orientation</strong> - Change orientation</li>
<li><strong>Change your mind</strong> - Go back</li>
<li><strong>Permissions</strong> - Permissions to device features</li>
</ul>
<p>In my IDEAL world Developers work side by side with Testers, they are comrades, brothers in arms. Testers help to test code better, Developers build applications for which automation can be applied easily.</p>
<p>My REAL world is a bit different but mostly it is close to my IDEAL world. Problems are solved, challenges make us stronger as specialists.</p>
<h2>Testers + Developers = Love Forever</h2>
<p><em>Article Photo by <a href="https://unsplash.com/photos/p74ndnYWRY4">Windows</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to setup CI/CD for iOS App Development with Fastlane, CircleCI and Firebase App Distribution]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2021/09/15/How-to-setup-CICD-for-iOS-App-Development-with-Fastlane-CircleCI-and-Firebase-App-Distribution</link>
            <guid>https://engineering.monstar-lab.com/en/post/2021/09/15/How-to-setup-CICD-for-iOS-App-Development-with-Fastlane-CircleCI-and-Firebase-App-Distribution</guid>
            <pubDate>Wed, 15 Sep 2021 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>App developers always strive to improve the product they are working on. So it is definite that either(mostly) you will be adding new features to existing products or (luckily) you will be setting up a whole new project. Either way, onboarding new developers or bootstrapping an entirely new project is a pretty challenging task. If not executed carefully, <strong>technical debt arises</strong> and headaches for developers, managers, sales people, and most importantly, clients.</p>
<h2>Git-flow: A quick overview</h2>
<p>Git flow is reasonably used in Agile as well as conventional Waterfall-based projects as well.</p>
<p>Before we start our thought process about implementing Continous Integration and Continous Delivery, it is crucial to decide your own <strong>git branching model</strong>. It should resemble your development workflow to make use of CI/CD tools.</p>
<h3>Git branching model</h3>
<p>This article is focused on mobile applications. Therefore, we will use the following git branching model for which serves the purpose as an example.</p>
<figure class="image">
  <img src="https://nvie.com/img/git-model@2x.png">
  <figcaption style="text-align: center;">Figure 1: Git Branching Model by <a href="https://nvie.com/posts/a-successful-git-branching-model/">Vincent Driessen</a></figcaption>
</figure>
<p>Git flow is essentially a merge base solution. It does not rebase the branch. Also, keeping tag name format <code>vX.Y.Z-beta-N</code> helps to generate a changelog using automated utilities.</p>
<p><strong>Neither Github-flow nor Git-flow in its purest form can help us adopt a resilient development workflow.</strong> The modern trend of Github usually seems to be having at least two branches, <code>develop</code> and <code>master</code> where <code>develop</code> branch is always under active development and <code>master</code> are holding the latest tagged releases at any given time.</p>
<p>Git-flow(with GitHub-flow tweaks) will help us come to a concrete solution for CI and CD. Of course, depending on your team sizes, you may have a different flow, but that flow should also be resembling the above-mentioned <a href="#Git-branching-model">Git Branching Model</a>.</p>
<h2>Implementing Continous Integration &#x26; Delivery</h2>
<p>By definition, <strong>an automated process or practice employed by software developers collaborating on a single software project of integrating code changes on the shared repository</strong>. This automation process may be composed of steps like testing code, packaging, and delivering and deployment to production.</p>
<p>During product development, developers have often been requested to give a new IPA file or APK file to the QA team or the client for checking and testing the features. Extending your workflow with CI/CD for unit-testing, distributing your binary can significantly reduce your off-development burden.</p>
<h3>Advantages of CI/CD</h3>
<p>CI/CD combined with git-flow gives many benefits such as the following:</p>
<ul>
<li>Saves developer time by sending automated builds to testing teams</li>
<li>Removes probability of inconsistency in builds(mainly caused due to local caching) by the guaranteed pristine build</li>
<li>Takes team engagement to the next level by encouraging open communication and free information flow</li>
<li>Reduces developer dependency by promoting knowledge sharing amongst team members</li>
<li>Improves developer confidence when merging code</li>
<li>Helps eliminate a class of bugs that might occur due to manual handling</li>
</ul>
<p>There could be de-merits of employing CI in a small team or individual developer adding overheads. However, for developers working on multiple projects with many people, CI is a wise investment with very high returns in terms of time and money.</p>
<h2>Fastlane</h2>
<p><strong><a href="https://docs.fastlane.tools/">Fastlane</a></strong> is recommended to implement CI/CD. Historically it became a part of <strong><a href="https://fabric.io">Fabric</a></strong> in 2015, later acquired by Google in 2017. However, depending on the team skills, you may implement the solution on some scripting languages like <code>Shell Script</code>, <code>Ruby</code>, <code>Python</code> etc. The community of Fastlane is vibrant, and you will find a lot of open-source plugins for your business use case.</p>
<p>We have chosen Fastlane tools for implementing our CI/CD Solutions. <strong>It does require some knowledge of Ruby</strong> however, you don't need to be an expert to work with it since it is implemented using Ruby-similar to widely used iOS dependency manager <em>CocoaPods</em>.
(+1 to <code>Gradle</code> for its built-in dependency management function for Android)</p>
<p>Articles on how to get started with Fastlane can be found <a href="https://docs.fastlane.tools/">here</a>. There is reasonably good documentation for both iOS and Android. <strong>However, we had difficulties finding some details related to Android Fastlane actions</strong> though we could search on forums like <code>StackOverflow</code>.</p>
<h4>Modeling Git Branching Model to Fastlane</h4>
<p>Usually, each major branches in our <a href="#Git-branching-model">Git Branching Model</a> correspond to an environment as shown in the table below:</p>





























<table><thead><tr><th>Environment</th><th>Alias/Tag</th><th>Branch</th><th>Description</th></tr></thead><tbody><tr><td>Development</td><td>alpha</td><td>develop</td><td>All the features for next or distant releases are usually developed in this environment</td></tr><tr><td>Staging</td><td>beta</td><td>release/ or hotfix/</td><td>Pre-release environment</td></tr><tr><td>Production</td><td>prod</td><td>master</td><td>Build made on master always uses this environment</td></tr></tbody></table>
<p>More complex flows may have additional layers of environment separation. For example, we can categorize our environment in the following as a <code>lane</code> (a.k.a Ruby function):</p>
<pre><code class="hljs language-ruby"><span class="hljs-comment"># Fastfile</span>

<span class="hljs-variable constant_">ALIAS_MAP</span> = {
  <span class="hljs-string">'alpha'</span> => <span class="hljs-string">'Develop'</span>,
  <span class="hljs-string">'beta'</span> => <span class="hljs-string">'Staging'</span>,
  <span class="hljs-string">'prod'</span>=> <span class="hljs-string">'Production'</span>
}
...
  desc <span class="hljs-string">'Build alpha IPA'</span>
  lane <span class="hljs-symbol">:alpha</span> <span class="hljs-keyword">do</span> |<span class="hljs-params">options</span>|
    build_app <span class="hljs-comment"># This will be replaced with custom `build_deploy` private lane later</span>
  <span class="hljs-keyword">end</span>

  desc <span class="hljs-string">'Build beta IPA'</span>
  lane <span class="hljs-symbol">:beta</span> <span class="hljs-keyword">do</span> |<span class="hljs-params">options</span>|
    build_app <span class="hljs-comment"># This will be replaced with custom `build_deploy` private lane later</span>
  <span class="hljs-keyword">end</span>

  desc <span class="hljs-string">'Build production ipa'</span>
  lane <span class="hljs-symbol">:prod</span> <span class="hljs-keyword">do</span> |<span class="hljs-params">options</span>|
    build_app <span class="hljs-comment"># This will be replaced with custom `build_deploy` private lane later</span>
  <span class="hljs-keyword">end</span>
...
</code></pre>
<div style="text-align: center;">Listing 1</div>
<p>ℹ️<em>We may use lane <code>options</code> parameters to pass command line arguments from CI YAML file.</em></p>
<p>You can put some configuration of <code>build_app</code> action into <code>Gymfile</code> per <code>lane</code> basis as shown below:</p>
<pre><code class="hljs language-ruby"><span class="hljs-comment"># Gymfile</span>
for_platform <span class="hljs-symbol">:ios</span> <span class="hljs-keyword">do</span>

    include_bitcode <span class="hljs-literal">true</span>
    include_symbols <span class="hljs-literal">true</span>

    for_lane <span class="hljs-symbol">:alpha</span> <span class="hljs-keyword">do</span>
      scheme <span class="hljs-string">'&#x3C;YOUR_DEV_SCHEME>'</span>
      export_method <span class="hljs-string">'development'</span> <span class="hljs-comment"># or 'enterprise' for in-house testing</span>
    <span class="hljs-keyword">end</span>

    for_lane <span class="hljs-symbol">:beta</span> <span class="hljs-keyword">do</span>
      scheme <span class="hljs-string">'&#x3C;YOUR_STAGING_SCHEME>'</span>
      export_method <span class="hljs-string">'ad-hoc'</span>
    <span class="hljs-keyword">end</span>

    for_lane <span class="hljs-symbol">:prod</span> <span class="hljs-keyword">do</span>
      scheme <span class="hljs-string">'&#x3C;YOUR_PRODUCTION_SCHEME>'</span>
      export_method <span class="hljs-string">'app-store'</span> <span class="hljs-comment"># or 'enterprise' for release</span>
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>
</code></pre>
<div style="text-align: center;">Listing 2</div>
<p>For available configuration options for <code>Gymfile</code> follow the link <a href="https://docs.fastlane.tools/actions/gym/#parameters">here</a>.</p>
<p>After putting above code from Listing 2 in a <code>Gymfile</code>, <strong>you don't need to pass relavant settings like <code>scheme</code> and <code>export_method</code> into <code>build_app</code> action like below:</strong></p>
<pre><code class="hljs language-ruby"><span class="hljs-comment"># You don't need to set parameters marked with 👈</span>
<span class="hljs-comment"># since it is handled in Gym file</span>
build_app(
  <span class="hljs-symbol">scheme:</span> <span class="hljs-string">"Release"</span>, <span class="hljs-comment"># 👈</span>
  <span class="hljs-symbol">export_options:</span> { <span class="hljs-comment"># 👈</span>
    <span class="hljs-symbol">method:</span> <span class="hljs-string">"app-store"</span> <span class="hljs-comment"># 👈</span>
  }
)
</code></pre>
<div style="text-align: center;">Listing 3</div>
<p>ℹ️Note: <em>We will use <strong>xcconfig</strong> to set <code>bundle id</code> and <code>provisioning profile</code> unlike Listing 3</em></p>
<h2>Firebase App Distribution</h2>
<p>To distribute the built IPA binary to Firebase, Reading this <a href="https://firebase.google.com/docs/app-distribution/ios/distribute-fastlane">document</a> on how to setup Firebase App Distribution is <strong>highly recommended.</strong></p>
<p>In Listing 1, we used <code>build_app</code> action to trigger the build. We can move that action into a <code>private_lane</code> called <code>build_deploy</code> and save duplicate code into these three main lanes. (Check <a href="#Revising-Main-Lanes-in-Listing-1">Listing 7</a> at the end for further clarification)</p>
<pre><code class="hljs language-ruby">  desc <span class="hljs-string">'Build and deploy ipa'</span>
  private_lane <span class="hljs-symbol">:build_deploy</span> <span class="hljs-keyword">do</span> |<span class="hljs-params">options</span>|
    <span class="hljs-comment">#1. Check if any new commits since last release</span>
    is_releasable = analyze_commits(<span class="hljs-symbol">match:</span> <span class="hljs-string">"*<span class="hljs-subst">#{options[<span class="hljs-symbol">:environment</span>]}</span>*"</span>)
    <span class="hljs-keyword">unless</span> is_releasable
      <span class="hljs-variable constant_">UI</span>.important  <span class="hljs-string">"No changes since last one hence skipping build"</span>
      <span class="hljs-keyword">next</span>
    <span class="hljs-keyword">end</span>

    <span class="hljs-comment">#2. Increment build number</span>
    increment_build_number(
      <span class="hljs-symbol">build_number:</span>  lane_context[SharedValues::<span class="hljs-variable constant_">RELEASE_NEXT_VERSION</span>] <span class="hljs-comment"># set a specific number</span>
    )
    <span class="hljs-comment">#3. If you can use `match`, you use `match`.</span>
    setup_provisioning_profiles

    <span class="hljs-comment">#4. Build deploy</span>
    build_app
    deploy_app options
  <span class="hljs-keyword">end</span>
</code></pre>
<div style="text-align: center;">Listing 4</div>
<h6>Step 1. Check if any new commits since the last release</h6>
<p><a href="https://github.com/xotahal/fastlane-plugin-semantic_release#analyze_commits"><code>analyze_commits</code></a> is a third-party action for <strong><a href="https://github.com/semantic-release/semantic-release">semantic release</a></strong> but quite helpful if you follow conventional commits. It lets us check if there has been any change since the last release. If there is then we go further otherwise stop with a message-"No changes since the last one hence skipping build". This will help us save some build minutes on the CI machine.</p>
<h6>Step 2. Increment build number</h6>
<p>We can keep the Marketing Version and Internal Build Version separate. For example, if the Xcode project has <a href="https://developer.apple.com/library/archive/qa/qa1827/_index.html">AGV tooling enabled</a>, we can use <code>increment_build_number</code>, which will automatically change the build number in the target.</p>
<h6>Step 3. Setup Provisioning Profiles</h6>
<p>We can use the Fastlane <code>match</code> command here. In case if we don't have access, we may need to install it manually using <code>import_certificate</code> first and then performing <code>FastlaneCore::ProvisioningProfile.install</code>.</p>
<h6>Step 4. Build and deploy</h6>
<p>We use <code>build_app</code> action as we used in Listing 1. After the IPA file is ready we have to send it to Firebase App Distribution using <code>deploy_app</code> a private lane.</p>
<h3>Deploying</h3>
<p>We deploy to Firebase for <code>alpha</code> and <code>beta</code> only. For <code>prod</code> upload either we manually upload to App Store Connect or automate it using <a href="https://docs.fastlane.tools/actions/upload_to_testflight/"><code>upload_to_testflight</code></a> action. We will limit our discussion to upload <code>prod</code> IPA as an asset on Github Release only.</p>
<p>The <code>deploy_app</code> lane is also self-explanatory as shown in the listing below. We can divide the process in 5 steps:</p>
<pre><code class="hljs language-ruby">  private_lane <span class="hljs-symbol">:deploy_app</span> <span class="hljs-keyword">do</span> |<span class="hljs-params">options</span>|
    environment = options[<span class="hljs-symbol">:environment</span>]
    <span class="hljs-keyword">next</span> <span class="hljs-keyword">if</span> environment == <span class="hljs-string">'prod'</span> <span class="hljs-comment"># Since `prod` is uploaded to testflight and app store</span>
    <span class="hljs-comment">#1. Generate Change Log</span>
    notes = conventional_changelog(<span class="hljs-symbol">title:</span> <span class="hljs-string">"Change Log"</span>, <span class="hljs-symbol">format:</span> <span class="hljs-string">"plain"</span>)

    <span class="hljs-comment">#2. Get Google App ID</span>
    gsp_path = <span class="hljs-string">"SupportingFiles/GoogleService/<span class="hljs-subst">#{<span class="hljs-variable constant_">ALIAS_MAP</span>[environment]}</span>-Info.plist"</span>
    google_app_id = get_info_plist_value(<span class="hljs-symbol">path:</span> gsp_path, <span class="hljs-symbol">key:</span> <span class="hljs-string">'GOOGLE_APP_ID'</span>)

    <span class="hljs-comment">#3. Upload to firebase</span>
    firebase_app_distribution(
      <span class="hljs-symbol">app:</span> google_app_id,
      <span class="hljs-comment"># groups: tester_group,</span>
      <span class="hljs-symbol">release_notes:</span> notes,
      <span class="hljs-symbol">firebase_cli_path:</span> <span class="hljs-variable constant_">FIREBASE_CLI_PATH</span>
    )

    <span class="hljs-comment">#4. Upload dSYM files to crashlytics</span>
    upload_symbols_to_crashlytics(<span class="hljs-symbol">gsp_path:</span> gsp_path)
    clean_build_artifacts
  <span class="hljs-keyword">end</span>
</code></pre>
<div style="text-align: center;">Listing 5</div>
<h6>Step 1. Generating Change Log:</h6>
<p>We are using the <a href="https://github.com/xotahal/fastlane-plugin-semantic_release"><code>semantic-version</code></a> plug-in of Fastlane to generate these logs. <code>conventional_changelog</code> has to be used in conjunction with <code>analyze_commits</code>(which we have used in <code>build_deploy</code> lane to check <code>is_releasable</code>). <code>analyze_commits</code> takes <code>match</code> argument-a regex for matching with the previous git-tag like <code>v1.0.1-beta-5</code>. This helps to generate logs for only between the last tag and current <code>v1.0.1-beta-6</code>.</p>
<h6>Step 2. Get Google App ID</h6>
<p>We need <code>google_app_id</code> from the relevant GoogleService-Info.plist. This Plist is generated on Firebase. Our sample code project is a multi-configuration single target Xcode project. Three GoogleService plists are renamed and moved to <code>GoogleService</code> folder:</p>
<ul>
<li>GoogleService/Develop-Info.plist</li>
<li>GoogleService/Staging-Info.plist</li>
<li>GoogleService/Production-Info.plist</li>
</ul>
<p>We get <code>gsp_path</code> first and from that we get <code>google_app_id</code>.</p>
<h6>Step 3. Upload to firebase</h6>
<p>To use this plugin we have to install <strong>firebase CLI</strong> as well. On CircleCI within <code>setup</code> command we have installed npm package for <code>firebase-cli</code>. <code>firebase_app_distribution</code> is nothing but a wrapper to use the CLI to upload to Firebase. We need to give <code>firebase_cli_path</code> so that the appropriate binary is used.</p>
<h6>Step 4. Upload dSYM files to crashlytics</h6>
<p>Finally we <a href="https://firebase.google.com/docs/crashlytics/get-deobfuscated-reports?hl=en">upload <code>dSYM</code> files to Firebase</a>. This will help Firebase make crash reports de-symbolized and readable.</p>
<p><strong>Note:</strong>
When bitcode is enabled in an Xcode project, App Store recompiles the code and provides us with dSYM files. These files need to be downloaded and uploaded to Firebase Crashlytics for crash report de-symbolization. Therefore, for the production version only we need to perform this step. <strong>checkout <code>download_dsym</code> action for Fastlane.</strong></p>
<h2>Github Release</h2>
<p><code>release_on_github</code> is a private lane and helps us automatically tag the commit, add release notes and attach IPA file for production, which can be later uploaded to App Store Connect.</p>
<p>Since Firebase doesn't have APIs to download the IPA file except from installing it on device only-in case where you may want to give <code>beta</code> releases to the client using other distribution like <code>deploygate</code>-You may want to keep the IPA as a pre-release in Github release history.</p>
<h3>Uploading IPA as an asset</h3>
<pre><code class="hljs language-ruby">  desc <span class="hljs-string">"Release on github"</span>
  private_lane <span class="hljs-symbol">:release_on_github</span> <span class="hljs-keyword">do</span> |<span class="hljs-params">options</span>|
    environment = options[<span class="hljs-symbol">:environment</span>]
    <span class="hljs-comment">#1. Generate Change Log</span>
    notes = conventional_changelog(<span class="hljs-symbol">title:</span> <span class="hljs-string">"Change Log"</span>)

    <span class="hljs-comment">#2. Get Version and Build Number</span>
    version_number = get_version_number
    build_number = get_build_number

    <span class="hljs-comment">#3. Set Github Release</span>
    is_prerelease = environment == <span class="hljs-string">'beta'</span> ? <span class="hljs-literal">true</span> : <span class="hljs-literal">false</span>

    name =  <span class="hljs-string">"[<span class="hljs-subst">#{<span class="hljs-variable constant_">ALIAS_MAP</span>[environment]}</span>] v<span class="hljs-subst">#{version_number}</span> Build: <span class="hljs-subst">#{build_number}</span>}"</span>

    set_github_release(
      <span class="hljs-symbol">repository_name:</span> <span class="hljs-string">"<span class="hljs-subst">#{<span class="hljs-variable constant_">ENV</span>[<span class="hljs-string">'CIRCLE_PROJECT_USERNAME'</span>]}</span>/<span class="hljs-subst">#{<span class="hljs-variable constant_">ENV</span>[<span class="hljs-string">'CIRCLE_PROJECT_REPONAME'</span>]}</span>"</span>,
      <span class="hljs-symbol">name:</span> name,
      <span class="hljs-symbol">commitish:</span> <span class="hljs-variable constant_">ENV</span>[<span class="hljs-string">'CIRCLE_SHA1'</span>],
      <span class="hljs-symbol">description:</span> notes,
      <span class="hljs-symbol">tag_name:</span> <span class="hljs-string">"v<span class="hljs-subst">#{version_number}</span>-<span class="hljs-subst">#{options[<span class="hljs-symbol">:environment</span>]}</span>-<span class="hljs-subst">#{build_number}</span>"</span>,
      <span class="hljs-symbol">is_prerelease:</span> is_prerelease,
      <span class="hljs-symbol">upload_assets:</span> [lane_context[SharedValues::<span class="hljs-variable constant_">IPA_OUTPUT_PATH</span>]]
    )
  <span class="hljs-keyword">end</span>
</code></pre>
<div style="text-align: center;">Listing 6</div>
<h6>Step 1. Generate Change Log</h6>
<p><code>conventional_changelog</code> generates by default mark-down style notes. This is handy when preparing auto-release notes.</p>
<h6>Step 2. Get Version and Build Number</h6>
<p>We use these to set the title of the release. AGV tooling should be enabled in the XCode project to use <code>get_version_number</code> and <code>get_build_number</code>.</p>
<h6>Step 3. Set Github Release</h6>
<p>We check if its beta then marks it as Pre-Release. Based on the environment we format the name/ title for the release notes. Set the tag name in a format like <code>v1.0.1-beta-1234</code> and upload the built IPA file as an asset to the release.</p>
<p>Note: You will need to set your personal token or ci bot token to <code>GITHUB_API_TOKEN</code> in the environment. This token should have permission to create a tag.</p>
<h3>Revising Main Lanes in Listing 1</h3>
<p><strong>We will skip the <code>alpha</code> releases on Github</strong> because they are quite frequent in releases and stored on the Firebase. Hence, you can save some space on Github by not tagging them and causing it hard to navigate release history on Github.</p>
<pre><code class="hljs language-ruby"><span class="hljs-comment"># Fastfile</span>

<span class="hljs-variable constant_">ALIAS_MAP</span> = {
  <span class="hljs-string">'alpha'</span> => <span class="hljs-string">'Develop'</span>,
  <span class="hljs-string">'beta'</span> => <span class="hljs-string">'Staging'</span>,
  <span class="hljs-string">'prod'</span>=> <span class="hljs-string">'Production'</span>
}
...
  desc <span class="hljs-string">'Build alpha IPA'</span>
  lane <span class="hljs-symbol">:alpha</span> <span class="hljs-keyword">do</span> |<span class="hljs-params">options</span>|
    build_deploy options
    <span class="hljs-comment"># Not releasing to Github since Firebase App Distribution</span>
  <span class="hljs-keyword">end</span>

  desc <span class="hljs-string">'Build beta ipa'</span>
  lane <span class="hljs-symbol">:beta</span> <span class="hljs-keyword">do</span> |<span class="hljs-params">options</span>|
    build_deploy options
    release_on_github options
  <span class="hljs-keyword">end</span>

  desc <span class="hljs-string">'Build production ipa'</span>
  lane <span class="hljs-symbol">:prod</span> <span class="hljs-keyword">do</span> |<span class="hljs-params">options</span>|
    build_deploy options
    release_on_github options
  <span class="hljs-keyword">end</span>
...
</code></pre>
<div style="text-align: center;">Listing 7</div>
<h2>Matching up CircleCI configuration with Fastlane</h2>
<p>CircleCI uses <code>YAML</code> files which should be simple. When a pull request is merged or code is pushed to some branch, it may trigger a CircleCI workflow given a properly set <code>config.yml</code> which in turn fires a specific lane on Fastlane.</p>
<p>Please carefully note that we have three environment arguments called <code>alpha</code>, <code>beta</code> and <code>prod</code> which you can refer in Table 2 in the alias column. Have a look at snippet of yaml file below:</p>
<pre><code class="hljs language-yaml"><span class="hljs-meta">---</span>
<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">deploy:</span>
    <span class="hljs-attr">executor:</span>
      <span class="hljs-attr">name:</span> <span class="hljs-string">default</span>
    <span class="hljs-attr">parameters:</span>
      <span class="hljs-attr">build_type:</span>
        <span class="hljs-attr">type:</span> <span class="hljs-string">enum</span>
        <span class="hljs-attr">enum:</span> [<span class="hljs-string">"alpha"</span>, <span class="hljs-string">"beta"</span>, <span class="hljs-string">"prod"</span>] <span class="hljs-comment"># Corresponds to lanes</span>
        <span class="hljs-attr">default:</span> <span class="hljs-string">alpha</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">attach_workspace:</span>
          <span class="hljs-attr">at:</span> <span class="hljs-string">/Users/distiller/project</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">ipa</span>
          <span class="hljs-attr">command:</span> <span class="hljs-string">bundle</span> <span class="hljs-string">exec</span> <span class="hljs-string">fastlane</span> <span class="hljs-string">ios</span> <span class="hljs-string">&#x3C;&#x3C;</span> <span class="hljs-string">parameters.build_type</span> <span class="hljs-string">>></span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">store_artifacts:</span>
          <span class="hljs-attr">path:</span> <span class="hljs-string">output</span>
          <span class="hljs-attr">when:</span> <span class="hljs-string">on_success</span>
</code></pre>
<p>Following is also snippet of the same yaml file. <strong>Here <code>setup</code> installs required dependency like rubygems, npm modules, cocoapod, firebase cli, carthage etc</strong>.</p>
<pre><code class="hljs language-yaml"><span class="hljs-meta">---</span>
<span class="hljs-attr">workflows:</span>
  <span class="hljs-attr">main:</span>
    <span class="hljs-attr">jobs:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">setup</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">test:</span>
          <span class="hljs-attr">requires:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">setup</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">deploy:</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">build_deploy_alpha</span>
          <span class="hljs-attr">build_type:</span> <span class="hljs-string">alpha</span>
          <span class="hljs-attr">requires:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">setup</span>
          <span class="hljs-attr">filters:</span>
            <span class="hljs-attr">branches:</span>
              <span class="hljs-attr">only:</span>
                <span class="hljs-bullet">-</span> <span class="hljs-string">develop</span> <span class="hljs-comment"># RegEx</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">deploy:</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">build_deploy_beta</span>
          <span class="hljs-attr">build_type:</span> <span class="hljs-string">beta</span>
          <span class="hljs-attr">requires:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">setup</span>
          <span class="hljs-attr">filters:</span>
            <span class="hljs-attr">branches:</span>
              <span class="hljs-attr">only:</span>
                <span class="hljs-bullet">-</span> <span class="hljs-string">/release\/.*/</span> <span class="hljs-comment"># RegEx</span>
                <span class="hljs-bullet">-</span> <span class="hljs-string">/hotfix\/.*/</span> <span class="hljs-comment"># RegEx</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">deploy:</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">build_deploy_prod</span>
          <span class="hljs-attr">build_type:</span> <span class="hljs-string">prod</span>
          <span class="hljs-attr">requires:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">setup</span>
          <span class="hljs-attr">filters:</span>
            <span class="hljs-attr">branches:</span>
              <span class="hljs-attr">only:</span>
                <span class="hljs-bullet">-</span> <span class="hljs-string">master</span> <span class="hljs-comment"># RegEx</span>
</code></pre>
<p>Pay special attention to the <em>filters</em> of the each workflow as well. They are containing <strong><a href="https://en.wikipedia.org/wiki/Regular_expression">Regular Expressions</a> like <code>develop</code>, <code>/release\/.*/</code>, <code>/hotfix\/.*/</code>,<code>master</code>.</strong></p>
<p>These filters make sure that CI build is triggered only for those branches which match above regex.<code>alpha</code> builds are triggered when code is pushed to <code>develop</code>, <code>beta</code> for <code>release/*</code> and <code>prod</code> for <code>master</code>.</p>

























<table><thead><tr><th>Push On Branch</th><th>Lane executed</th><th>Environment</th></tr></thead><tbody><tr><td><code>develop</code></td><td>bundle exec fastlane ios <strong>alpha</strong></td><td>Development</td></tr><tr><td><code>release/*</code> &#x26; <code>hotfix/*</code></td><td>bundle exec fastlane ios <strong>beta</strong></td><td>Staging</td></tr><tr><td><code>master</code></td><td>bundle exec fastlane ios <strong>prod</strong></td><td>Production</td></tr></tbody></table>
<p>The only purpose of showing the above snippet is to show you how the <a href="#Git-branching-model">git branching model</a> is applied to the CircleCI YAML file configuration and the Fastlane configuration file. You can learn more about CircleCI Yaml <a href="https://circleci.com/docs/2.0/writing-yaml/">here</a>.</p>
<h2>Conclusion</h2>
<p>Effective development workflow and CI-CD implementation can help reduce tons of developer hours. Similarly, the QA team will be able to link the bugs and issues to a particular build and can have a more productive conversation with developers. Especially, ramping up time for the new developer also reduces significantly.</p>
<h2>Resources</h2>
<p><a href="https://github.com/monstar-lab-oss/ios-template">Sample Code</a>
<a href="https://docs.fastlane.tools/">Fastlane Tools Docs</a>
<a href="https://firebase.google.com/docs/app-distribution/ios/distribute-fastlane">Firebase App Distribution</a>
<a href="https://nvie.com/posts/a-successful-git-branching-model/">Git Branching Model by Vincent Driessen</a>
<a href="https://developer.apple.com/library/archive/qa/qa1827/_index.html">AGV tooling enabled</a></p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/xs0ohkkR_xc">Rasa Kasparaviciene</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Ravishing Ruby: Looking back at RubyKaigi Takeout 2021]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2021/09/15/Ravishing-Ruby-Looking-back-at-Rubykaigi-Takeout-2021</link>
            <guid>https://engineering.monstar-lab.com/en/post/2021/09/15/Ravishing-Ruby-Looking-back-at-Rubykaigi-Takeout-2021</guid>
            <pubDate>Wed, 15 Sep 2021 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>RubyKaigi Takeout 2021 was held from September 9 to 11, 2021, after the original version of the conference was replaced with this online version. A place where the whole ruby community gathered together despite the pandemic’s attempt to keep them apart. It was a bit intimidating for me as a first-time attendee, but I got into the zone very quickly, thanks to the fine lineup of talks and presentations.
I’d like to take a spin through some of the highlights of the events that made this conference so great.</p>
<h2>TypePorf for IDE</h2>
<p>By Yusuke Endoh</p>
<p>This talk by Yusuke Endoh- San was the perfect way to kick off Rubykaigi Takeout 2021.
The talk started with small pseudo-code examples in typescript, depicting how well of a dynamically typed language it is. TypeProf enables developers to achieve the same level of user experience, at least to an extent, in a very dynamically typed language like Ruby. Providing features like on-fly reporting, go-to-definition option, auto-completion, showing hints, inferring types on the fly, etc. There will be Typeprof support from Ruby 3.1, intending to improve our developing experience. Typeprof is still very slow from an analysis point of view, which hopefully will get better in near future. Even though it is still in the experimental phase, this is definitely exciting for all fellow Rubyists.</p>
<h2>Rubocop in 2021: Stable and Beyond</h2>
<p>By Koichi Ito</p>
<p>Rubocop has been the most famous linter/formatter for ruby, which has helped developers enforce standard ruby style guidelines in their code. The talk discusses how things have changed after the release of Rubocop 1.0 last year, providing new upgrades in areas like bug fixes, performance, new ruby syntax support, auto-correction, etc. The optional --parallel option that can improve performance by enabling parallel processing really caught my eye. I think computers with good CPU, memory resources will benefit from this. Even though Koichi Ito-San says he has no plans for the major release of Rubocop version 2.0, he agrees to the fact that this is bound to happen at some point in the future.</p>
<h2>Ruby Archeology</h2>
<p>By Nick Schwaderer</p>
<p>This title seemed odd to me, to be honest at first, but it wasn’t long before fascination crept in. The author reflects on how ruby has evolved over time. He started with setting up the 2009/2008 environment(Ruby 1.8~) on the 2021 machine. The author really dug deep on gems from the same generation in Nokogiri, hpricot (maintainers closed the book on it), builder gem. As a person who was introduced to ruby not until recently, I found it quite fascinating. The author took us all on board on his ride to the past which made us realize how important looking at our past and knowing the history is.</p>
<h2>Variable width allocation: Optimizing Ruby's Memory Layout</h2>
<p>By Peter Zhu and Matt Valentine-House</p>
<p>This was one of the most informative talks for me personally. Started with how Ruby lays out its memory, how it manages memory, for both small and large objects. They also explained how Ruby’s garbage collection works, including slides that depicted how the steps of marking, sweeping, and compaction are implemented. The performance overhead caused by the malloc system was also well explained.
The goals of this project were to improve the performance by enabling ruby to manage its layout rather than relying on malloc system library. Reducing the malloc calls has already shown good improvements in performance. Even though this is not production-ready yet, the prospect of variable width allocation looks really promising.</p>
<h2>Parsing Ruby</h2>
<p>By Kevin Newton</p>
<p>This talk was about different projects that parsed Ruby code over the years. Starting with Ruby 0.06 in 1996 all the way to Ruby 3.0 in 2020. The author also shared the pictorial representation of the releases of these projects along the timeline. The talk concluded with a discussion of current ruby script parsers Ripper and RubyParser, discussing the upsides and downsides of each and how investing in these or similar kinds of libraries can help us build powerful production applications.</p>
<h2>Regular Expressions: Amazing and Dangerous</h2>
<p>By Martin J. Dürst</p>
<p>The author Martin reflects on how strong and handy Regular expressions can get, but at the same time can leave you in a world of evil when not used correctly. Due to its unpredictable time behavior and compact syntax, we could be running a high risk of significantly slowing down our program. Even though the author sees some solutions in RegExp timeouts limits and backtracking limits, the importance of avoiding the use of RegExs unless when it’s really needed is conveyed well enough.</p>
<h2>PRK Firmware: Keyboard is Essentially Ruby</h2>
<p>By Hitoshi Hasumi</p>
<p>PRK firmware is the first keyboard firmware framework in ruby. You can basically write your keymap in ruby for your keyboard firmware. Also, you can add extra layers to your keyboard, essentially expanding your 50% layout keyboard to a much bigger virtual layout. There are also options to set rainbow or breathing effects to make your keyboard look cooler. The level of customization doesn't end here.
The author goes the extra mile with the custom Fibonacci key, which generates the Fibonacci number, and the custom Password key which generates a password every time you click those keys. The Ruby mode on the keyboard lets you execute small ruby codes from your text editor, literally letting you carry a ruby environment with you. The keyboard is essentially ruby, Indeed.</p>
<p>As anticipated, the talk was able to live up to its high expectations. The fact that the author, Hasumi-San is from Monstarlab, made it extra special for me as a fellow employee. Creator truly has taken Ruby to the next level. The first keyboard firmware framework in ruby will definitely be the one for the books.</p>
<h2>Ractor's speed is not light-speed</h2>
<p>By Satoshi "moris" Tagomori</p>
<p>Ractor is designed to run ruby code in parallel, introduced from the newer version of Ruby. Even though it is still in the experimental phase, Ruby with Ractor sounds like an exciting prospect. The talk covered how Ractor will introduce new features which will boost the execution speed. The “shareable” objects (Modules, Classes) that can be shared between the ractors, which are running in parallel on multiple CPU cores will do the job for us.
Even though Ractor sounds like a very good thing that can happen to ruby, it is still a big leap away from being production-ready.</p>
<h2>Do regex dream of Turing Completeness?</h2>
<p>By Daniel Magliola</p>
<p>The presentation was really a treat to the eye, demonstrating Conway's game of life by using complex ruby regex expressions. Once he was able to implement it to a great extent, now he was pushing for the bigger prize, Turing completeness, with all the power vested in him by the Regex expressions. Maybe the author Daniel Magliola wouldn't have cracked the German code during world war 2, but definitely wouldn't have gone down without a fight.</p>
<h2>Charty: Statistical data visualization in Ruby</h2>
<p>By Kenta Murata</p>
<p>Ruby is well established and has proven itself as a widely accepted general-purpose programming language. But have you ever thought of building charts and plots with ruby, making it a decent data visualization tool? This is what the creator Kenta Murata-San achieves with his library, called Charty. Charty, a statistical data visualization tool, can create scatter and line plots (Relational plots), bar and box plots (Categorical plots), and distributional plots (histogram plots) for your data. The fact that Charty can generate these plots from a table makes it extra special.</p>
<h2>Conclusion</h2>
<p>If I were to define RubyKaigi in a line, it would be like this: a marathon of an amazing and diverse lineup of talks. Even for me, as a seasonal Rubyist, it got me buzzing with excitement. While 2020 hasn’t been easy for anyone, fortunately, Ruby didn’t show any signs of slowing down. What the future holds for this underrated programming language is definitely exciting. The end.</p>
<h2>Reference</h2>
<p>You can watch all the talks here:
<a href="https://www.youtube.com/playlist?list=PLbFmgWm555yYgc4sx2Dkb7WWcfflbt6AP">https://www.youtube.com/playlist?list=PLbFmgWm555yYgc4sx2Dkb7WWcfflbt6AP</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[RubyKaigi Takeout 2021: A Look Back]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2021/09/11/RubyKaigi-Takekout-2021</link>
            <guid>https://engineering.monstar-lab.com/en/post/2021/09/11/RubyKaigi-Takekout-2021</guid>
            <pubDate>Sat, 11 Sep 2021 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Can you believe it’s already been an entire year since I had the opportunity to join my first ever developers
conference, the RubyKaigi? Over the past three days, I was blessed with the opportunity to also enjoy this year’s
talks and sessions, and get once again the opportunity to immerse myself in the Ruby ecosystem that I had to,
unfortunately, leave behind for the time being due to project assignments. In this article, I’d like to highlight
some of my favourite moments from the conference.</p>
<h2>About RubyKaigi</h2>
<p>Before I delve into the details on various sessions, let me just make sure we’re all on the same page by reiterating
what RubyKaigi is in the first place. <a href="https://rubykaigi.org/2021-takeout">RubyKaigi</a> is the largest conference for
Ruby programmers held annually in Japan since 2006. Just like last year, while some of the presentations and
discussions are done in Japanese, there was a simultaneous live audio translation service available for the numerous
international members of the community that do not have the luxury of knowing the Japanese language.</p>
<p>Finally, just like last year, this year’s RubyKaigi format has been altered into an online-only conference. I hope that
next year, we can all go back to the original format of an in-person conference.</p>
<h2>TypeProf for IDE</h2>
<p>The conference kicked off into high gear with
<a href="https://rubykaigi.org/2021-takeout/presentations/mametter.html">Yusuke Endoh-san’s keynote presentation</a> on the
benefits of static typing and <a href="https://github.com/ruby/typeprof">TypeProf</a>, a code analysis tool shipped with Ruby 3.0,
that is capable to analyze the types of your code without having to write every single type annotation. Today’s special
announcement followed just shortly after, and it took the form of TypeProf for IDE, an extension for Visual Studio Code
powered by TypeProf.</p>
<p>TypeProf for IDE can already today provide on-the-fly hints on method signatures (displayed conveniently in Code Lens),
report errors as you type, find definitions of methods you call and offer code completion which takes types into
consideration. Of course and as always, if TypeProf guesses the type of your method incorrectly, you can manually
override that inferred type signature by manually specifying the method signature using RBS. TypeProf for IDE will ship
with Ruby 3.1, but if the above description already got you excited (as it should!), you can start using it
<a href="https://gist.github.com/mame/86234de6a58352b9f994e0f8a6d6fbc2">today</a> by following a couple of manual steps.</p>
<p>I welcome TypeProf for IDE as an exciting addition to the Ruby ecosystem that will allow for comfortable development
even in light-weight editors, because it should not be needed to open a full-blown IDE (don’t worry, I still love you,
RubyMine) just to write a simple script or make a small change in your code.</p>
<h2>Ruby Archaeology, parallel testing with Ractors and the danger of regular expressions 💎⛏</h2>
<p>Many other great sessions were happening on the first day of RubyKaigi, but if I had to highlight only three,
then <a href="https://rubykaigi.org/2021-takeout/presentations/schwad4hd14.html">Ruby Archaeology by Nick Schwaderer</a> would have
to be one of my them for sure. In his talk, Nick looked into the ancient history of Ruby and explained all the hoops and
traps he had to overcome and work his way around to get a 12-year-old version of Ruby working. Even though Ruby itself
would run just fine in an appropriate operating system for that time, to my surprise, most of the problems seemed to lie
elsewhere – in particular, by relying on remote resources that may not be accessible the same way as they used to be, or
just might be gone in the digital heaven altogether. Fortunately, if you want to follow in Nick’s footsteps and become a
time traveller yourself, you <a href="https://github.com/Schwad/portal_gun">won’t have to work quite as hard as he did</a>.</p>
<p>Finally, I’d like to highlight
<a href="https://rubykaigi.org/2021-takeout/presentations/vinistock.html">Vinicius Stock’s presentation and work</a> on a test
framework that would leverage the power of Ractors to achieve faster test execution (because having a slow test suite is
the best way to discourage your developers to run it) and
<a href="https://rubykaigi.org/2021-takeout/presentations/duerst.html">Martin J. Dürst’s tour into the land</a> of amazing, yet
sometimes dangerous regular expressions.</p>
<h2>Keyboard is essentially Ruby, or ⌨️ = 💎</h2>
<p>Okay okay, I might be biased but I still have to say this: the personal highlight of the second day, if not the entire
conference, would really have to be our
<a href="https://rubykaigi.org/2021-takeout/presentations/hasumikin.html">Monstarlab’s Hitoshi Hasumi-san’s presentation</a> on
<a href="https://github.com/picoruby/prk_firmware">PRK Firmware</a> – and that’s a pretty crazy achievement if you consider how
amazing the rest of the speakers and presentations were!</p>
<p>In his live presentation, Hasumi-san kept blowing the audience collective minds in regular intervals. On the first
level, it’s already incredibly impressive to be able to program the firmware of your custom-built keyboard in Ruby.
On the next level, it’s even more impressive to be able to dedicate certain keys to execute Ruby code, as Hasumi-san
demonstrated with his Fibonacci key and password generator key. And then on whole another level from another universe,
it’s truly mind-blowing to have a “Ruby key” which turns the combination of your keyboard and any text editor (even as
simple as <code>Notepad.exe</code>) into essentially an IRB shell. You can truly take your Ruby with you anywhere you go – but
before you actually go, don’t forget to read
<a href="https://engineering.monstar-lab.com/2021/05/27/Static-type-checking-in-PicoRuby">Hasumi-san’s article on static type checking</a>
in PicoRuby if you missed it earlier!</p>
<h2>Graphical Terminal User Interface of Ruby 3.1</h2>
<p>I have to come clean, I’m a big fan of smart, intuitive and user-friendly terminal user interfaces, or TUIs for short.
IRB is already a very smart REPL (read-evaluate-print-loop, something every self-respecting programming language should
provide) environment with its powerful auto-completions, automatic indentations and even syntax highlighting, but it
seems that <a href="https://rubykaigi.org/2021-takeout/presentations/aycabta.html">Itoyanagi Sakura-san’s plans</a> don’t stop
there: by improving the Reline library, IRB in Ruby 3.1 will offer not only code auto-completion using the <code>Tab</code> key as
before, but will even show you all possible options in a lovely drop-down menu, just like any IDE would!</p>
<h2>Do regex dream of Turing completeness? 😴💭</h2>
<p>Finally, the third and final day of RubyKaigi kicked off with
<a href="https://rubykaigi.org/2021-takeout/presentations/dmagliola.html">Daniel Magliola’s investigative goal</a> of finding out
whether regular expressions are Turing-complete by trying to implement
<a href="https://playgameoflife.com">Conway’s Game of Life</a> entirely in a regular expression. I think I won’t spoil much
by sharing the conclusion – which is the realization that regular expressions are not fully Turing-complete, but
they’re not that far off the mark – because the entire talk is absolutely worth watching and shows not only Daniel’s
ingenuity and inventiveness, but also the power and quirks of regular expressions that you likely were not aware of
before.</p>
<h2>Native extensions and dead ends</h2>
<p>Following up was an interesting talk by
<a href="https://rubykaigi.org/2021-takeout/presentations/flavorjones.html">Mike Dalessio on the topic of native extensions</a>
(“this could take a while…”, a message we all likely saw in the past and sighed just a bit anytime it appeared in our
terminals) and native gems, which contain the pre-compiled libraries for a specific architecture, thus removing the need
to compile the C extensions yourself.</p>
<p>Richard Schneeman demonstrated in an
<a href="https://rubykaigi.org/2021-takeout/presentations/schneems.html">incredibly professionally done presentation</a> his work
on the <a href="https://github.com/zombocom/dead_end"><code>dead_end</code></a> which will help you next time quickly diagnose and fix all of
your “unexpected end” error messages with ease. It can diagnose missing <code>end</code> and <code>do</code> keywords as well as unmatched <code>|</code>
and <code>}</code> symbols, and I’d love to both try it myself soon and also have it included in Ruby itself.</p>
<h2>Closing keynote by Yukihiro “Matz” Matsumoto</h2>
<p>Finally, the conference concluded with
<a href="https://rubykaigi.org/2021-takeout/presentations/yukihiro_matz.html">Yukihiro Matsumoto-san’s closing keynote</a>.</p>
<p>In the first half, he looked back at the relatively recent release of Ruby 3.0, which was both a first major release
in nearly eight years (since Ruby 2.0’s original release), but also a release that still kept the importance of
backward compatibility in mind, because as surprising as that might be to some communities, developers don’t really
like new things if it means that their old code stops working and has to be re-written.</p>
<p>Matsumoto-san highlighted the wonderful additions that Ruby 3.0 brought to the developers, such as support for type
definitions with RBS, which are conceptually similar to TypeScript’s <code>d.ts</code> files – or personally, I feel that they
are very similar in both nature and syntax to Elixir’s Dialyzer typespecs. Numbered block parameters and support for
pattern matching (funnily enough, two more features that I love in Elixir and am so happy to see in Ruby) were also
mentioned, as well as two approaches for better concurrent programming: Fiber Scheduler with its fast context switching
is ideal for I/O heavy operations, while Ractor is here to save the day for all the CPU-intensive tasks.</p>
<p>After that, the presentation shifted focus to the upcoming release of Ruby 3.1. It was stated that Ruby 3.1 will be
a conservative release with the main goals of improved stability, more bug fixing, and especially two things:
<em>developer comfort</em> (thanks to a more powerful IRB and TypeProf for IDE) and <em>even more speed</em> – but ideally, it would
be a speed that doesn’t come in the form of pointless number-chasing in artificial benchmarks, but rather a speed that’s
more closely related to where speed is truly needed in real-world applications.</p>
<p>I cannot but applaud the decision to focus on these two aspects, as I find the developer’s comfort one of the key
elements in a programming language’s viability, and unfortunately, the rest of the world judges languages by the other
aspect. I can’t wait to see what the future brings to Ruby, and I hope to participate in RubyKaigi also next year!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Safe Navigation With Jetpack Compose]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2021/08/30/Safe-Navigation-With-Jetpack-Compose</link>
            <guid>https://engineering.monstar-lab.com/en/post/2021/08/30/Safe-Navigation-With-Jetpack-Compose</guid>
            <pubDate>Mon, 30 Aug 2021 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Navigation is the key part of application development and Android is no exception. From Activities and Fragments transitions to Navigation Component and now, Navigation Component is available for Jetpack Compose! In this article, I would like to give a brief overview of how Jetpack Compose Navigation works, the problems I’ve faced, and my solution for it. Let's get started 🙂 !</p>
<blockquote>
<p><strong>Disclaimer</strong>
Since Jetpack Compose is still in its early stages and its Navigation APIs are still in alpha, in Monstarlab we don't yet fully rely on Jetpack Compose Navigation APIs when building Android applications. It doesn't mean that we don't play around with it however and that's where the idea for this article (and the library you will learn about) came from =).</p>
</blockquote>
<h2>Your First Route</h2>
<p>When using the original Navigation Component, we used XML to describe our navigation. There we could declare the arguments for our destination and transitions between them. In Compose world, of course, we use functions :)</p>
<blockquote>
<p><em>The example I will be using in the following section is a slightly modified example taken from the developer.android.com —</em> <a href="https://developer.android.com/jetpack/compose/navigation"><em>Navigating with Jetpack Compose</em></a> <em>. Make sure to visit it in case you need more information</em></p>
</blockquote>
<p>We start by declaring a <code>NavHost</code> and then we can build our navigation graph by adding destinations using <code>composable()</code> function. It takes a path URI and a <code>@Composable</code> as the destination content</p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">val</span> navController = rememberNavController()
NavHost(navController = navController, startDestination = <span class="hljs-string">"home"</span>) {
    composable(<span class="hljs-string">"home"</span>) { HomeScreen() }
    composable(<span class="hljs-string">"details"</span>) { DetailsScreen() }
}
</code></pre>
<p>We’ve just set up our first destinations, lets add some action into our code by adding a transition from <code>"home</code>" to <code>"details"</code>. Compose Navigation Component makes it pretty easy as well, all we need to do is to tell our <code>navController</code> the path we want to navigate to</p>
<pre><code class="hljs language-kotlin">composable(<span class="hljs-string">"home"</span>) {
  HomeScreen(onClick: { navController.navigate(<span class="hljs-string">"details"</span>) })
}
</code></pre>
<h3>Arguments Support</h3>
<p>Let's imagine our Details route needs two arguments: an optional <code>Int</code> called <strong>number</strong> and a mandatory <code>String</code> called <strong>id</strong>. To support an ability to pass the arguments you need to do two things: adjust the path so that the Navigation Component knows that this route can accept arguments and describe the type of the arguments. This is how it looks like in our case</p>
<pre><code class="hljs language-kotlin">NavHost(navController = navController, startDestination = <span class="hljs-string">"home"</span>) {
    composable(<span class="hljs-string">"home"</span>) { HomeScreen(<span class="hljs-comment">/*...*/</span>) }
    composable(<span class="hljs-string">"details/{id}?number={number}"</span>,
        arguments = listOf(
          navArgument(<span class="hljs-string">"id"</span>) { type = NavType.StringType }
          navArgument(<span class="hljs-string">"number"</span>) { type = NavType.IntType; defaultValue = <span class="hljs-number">1</span> }
        )
    ) {...}}
</code></pre>
<p>Now for our detail transition, it won’t be enough to specify just the destination name like we used to — we need to provide arguments too, at least id anyway, as the optional param will be set to our default value.</p>
<pre><code class="hljs language-kotlin">navController.navigate(<span class="hljs-string">"details/myFancyId?number=21"</span>)
</code></pre>
<p>The next step is to obtain the arguments. This can be done in multiple ways, either from the <code>NavBackStackEntry</code> or from the <code>SavedStateHandle</code> (this can be injected in our <code>ViewModel</code>)</p>
<p>Note that in both cases, the value you receive can be <code>null</code>, so that must be addressed in some way.</p>
<pre><code class="hljs language-kotlin"><span class="hljs-comment">/**
* Get args from NavBackStackEntry
**/</span>
composable(<span class="hljs-comment">/** our details route **/</span> ) { backStackEntry ->
    <span class="hljs-keyword">val</span> id: String? = backStackEntry.arguments?.getString(<span class="hljs-string">"id"</span>)
    <span class="hljs-keyword">val</span> number: <span class="hljs-built_in">Int</span>? = backStackEntry.arguments?.getInt(<span class="hljs-string">"number"</span>)
    DetailsScreen(id, number)
}

<span class="hljs-comment">/**
* Get args from SavedStateHandle
**/</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DetailsScreenViewModel</span> <span class="hljs-meta">@Inject</span> <span class="hljs-keyword">constructor</span></span>(
  savedStateHandle: SavedStateHandle
) {
  <span class="hljs-keyword">init</span> {
    <span class="hljs-keyword">val</span> id: String? = savedStateHandle.<span class="hljs-keyword">get</span>&#x3C;String>(<span class="hljs-string">"number"</span>)
    <span class="hljs-keyword">val</span> number: <span class="hljs-built_in">Int</span>? = savedStateHandle.<span class="hljs-keyword">get</span>&#x3C;<span class="hljs-built_in">Int</span>>(<span class="hljs-string">"name"</span>)
  }
}
</code></pre>
<h3>The elephant in the room 🐘</h3>
<p>By now, you have probably noticed how much boilerplate code we have to just support these two routes and more importantly, how many hard-coded strings we have. Each navigation stage: declaration, arguments handling, and navigation actions, all use the path and parameters. Moreover, the arguments handling is very poor since we always need to be aware of the argument name and its type.</p>
<p>To reduce this overhead you can wrap your destinations in a <code>sealed class</code> , for example, that will clean up the code for sure, but it won't solve all the problems.</p>
<p>Remember the original Navigation Component? Well, it has <code>safeArgs</code> , a very useful plugin that generates code that allows handling arguments in a very clean and safe way. When I started to work with Jetpack Compose and its Navigation Component I immediately noticed that I miss this functionality, so I've decided to write one myself 👨‍💻 .</p>
<h2>Introducing Safe Routing Library</h2>
<p>At its core, <code>safe-routing</code> is an annotation processing library that generates a code that will handle most of the boilerplate code and hardcoded strings and will provide a safe way to navigate between your composables and ease up arguments handling.</p>
<h3>Installation</h3>
<p>in your project level <code>build.gradle</code></p>
<pre><code class="hljs language-kotlin">allprojects {
  repositories {
    ...
    maven { url <span class="hljs-string">'https://jitpack.io'</span> }
  }
}
</code></pre>
<p>And then in your app-level <code>build.gradle</code></p>
<pre><code class="hljs language-kotlin">dependencies {
    kapt(<span class="hljs-string">"com.github.levinzonr.compose-safe-routing:compiler:2.1.0"</span>)
    implementation(<span class="hljs-string">"com.github.levinzonr.compose-safe-routing:core:2.1.0"</span>)
}
</code></pre>
<p>Let's sync our project and apply some annotations to clean up our code. <code>safe-routing</code> provides two annotations for you to describe the routes. Meet <code>@Route</code> and <code>@RouteArg</code></p>
<ul>
<li><code>@Route</code> is used to describe your Route, its constructor accepts a route name and the arguments for it</li>
<li><code>@RouteArg</code> is used to describe the arguments for your Route, it accepts several parameters: the name, type, a flag to tell whether that argument is optional or not and a String representation of the default value. Note that the default value will only be used in case the argument is optional.</li>
</ul>
<p>Okay, now it's time to revisit our navigation code 🙂 .</p>
<h3>Basic Setup</h3>
<p>To start, all we have to do is to apply the annotations for our two routes</p>
<pre><code class="hljs language-kotlin"><span class="hljs-meta">@Composable</span>
<span class="hljs-meta">@Route(name = <span class="hljs-string">"details"</span>, args = [
    RouteArg(<span class="hljs-string">"id"</span>, RouteArgType.StringType, false)</span>,
    RouteArg(<span class="hljs-string">"number"</span>, RouteArgType.IntType, <span class="hljs-literal">true</span>, defaultValue = <span class="hljs-number">1.</span>toString()),
])
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">DetailsScreen</span><span class="hljs-params">()</span></span> {
  <span class="hljs-comment">/** your sweet composable code **/</span>
}

<span class="hljs-meta">@Composable</span>
<span class="hljs-meta">@Route(<span class="hljs-string">"home"</span>)</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">HomeScreen</span><span class="hljs-params">()</span></span> {
  <span class="hljs-comment">/** your sweet composable code **/</span>
}
</code></pre>
<p>That's it! After rebuilding the project, several new Kotlin files will be generated. Here is a quick description, but if you want to see what it actually looks like, in this particular example, check <a href="https://gist.github.com/levinzonr/2acc21de9050da35d2929112310fc7b5#file-generatedexample-kt">this gist</a></p>
<ul>
<li><code>Routes.kt</code> - This file contains all your routes descriptions that can be used to declare the destinations in our<code>NavGraphBuilder</code> using the <code>RouteSpec</code> interface. <code>RouteSpec</code> knows everything for successful navigation: the name of the route, its arguments, and how to obtain them.</li>
<li><code>RoutesActions.kt</code> - This file contains the Nav Actions for your routes. Similar to <code>NavDirections</code> from <code>safeArg</code> plugin, it will build a proper path only accepting the arguments you provided in the annotations.</li>
<li><code>*RouteArgs.kt</code> and <code>*RouteArgsFactory.kt</code> -> for every route that contains arguments an Argument Wrapper and Factory will be generated - <code>DetailsRouteArgs.kt</code> and <code>DetailsRouteArgsFactory</code> in our case.</li>
</ul>
<h3>Usage</h3>
<p>Now let's start using all this code that's been generated, starting with our <code>NavGraphBuilder</code>. <code>Routes.kt</code> now contains two objects <code>Details</code> and <code>Home</code> and they both extend <code>RouteSpec</code> interface, which gives us access to the following properties:</p>
<ul>
<li><code>route</code> - full path of the route including the arguments we specified</li>
<li><code>navArgs</code> - list of the <code>NamedNavArgument</code> to fully describe our arguments</li>
<li><code>argsFactory</code> - an object that will help us to obtain our arguments from various places</li>
</ul>
<p>Let's put them to good use 🙂 . Here we are using <code>route</code> and <code>navArgs</code> properties to describe your route, and <code>RouteArgsFactory</code> to obtain the arguments that are being passed</p>
<pre><code class="hljs language-kotlin">NavHost(
  navController = navController,
  startDestination = Routes.home.route
) {
  composable(route = Routes.Home.route) { HomeScreen() }
  composable(routes = Routes.Details.route, arguments = Routes.Details.navArgs) {
    <span class="hljs-keyword">val</span> args = DetailsRouteArgsFactory.fromBackStackEntry(it)
    DetailsScreen(args)
  }
}
</code></pre>
<p>But since we are dealing with the RouteSpec interface we can make this declaration even shorter!. SafeRoute lib provides <code>composable</code> extension functions that accept<code>RouteSpec</code> as the main parameter. Another one is <code>composableWithArgs</code> that will handle arguments retrieving for you.</p>
<pre><code class="hljs language-kotlin">NavHost(
  navController = navController,
  startDestination = Routes.home.route
) {
  composable(Routes.Home) { HomeScreen() }
  composableWithArgs(Routes.Details) { entry, args -> DetailsScreen(args)}

}
</code></pre>
<p>Our only transition also needs some love, lets use our <code>RoutesActions.kt</code></p>
<pre><code class="hljs language-kotlin">composable(Routes.Home) {
  HomeScreen(
    onClick = {
      navController.navigate(RoutesActions.toDetails(id = <span class="hljs-string">"MyFancyId"</span>, number = <span class="hljs-number">0</span>))
      <span class="hljs-comment">// navController.navigate(RoutesActions.toDetails("myId")) in case we don't want to pass the number</span>
    }
  )
}
</code></pre>
<p>Bonus, in case you are wondering about how to pass the arguments directly to the ViewModel</p>
<pre><code class="hljs language-kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DetailsScreenViewModel</span> <span class="hljs-meta">@Inject</span> <span class="hljs-keyword">constructor</span></span>(savedStateHandle: SavedStateHandle) {
  <span class="hljs-keyword">init</span> {
    <span class="hljs-keyword">val</span> args = DetailsRouteArgsFactory.fromSavedStateHandle(savedStateHandle)
  }
}
</code></pre>
<p>And that's it!</p>
<h2>Afterword</h2>
<p>Thank you for your time! I really enjoyed making this tiny little library and I hope you can find it useful! I will really appreciate any feedback and suggestions so feel free to reach out to me 🙂 . SafeRoute already had quite a few updates, and it now <a href="https://github.com/levinzonr/compose-safe-routing/blob/master/RELEASE_NOTES.md/#210-release-notes">supports</a> Animations and Material Destinations from the <a href="https://github.com/google/accompanist">Accompanist</a> library Here is the <a href="https://github.com/levinzonr/compose-safe-routing">GitHub page</a> for those of you who want to know more.</p>
<p><em>Article photo by <a href="https://unsplash.com/@jsycra?utm_source=unsplash&#x26;utm_medium=referral&#x26;utm_content=creditCopyText">Siyuan</a> on <a href="https://unsplash.com/photos/RyUWQxgN6RI?utm_source=unsplash&#x26;utm_medium=referral&#x26;utm_content=creditCopyText">Unsplash</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[WWDC21 - A first look at Apple's new Augmented Reality features]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2021/06/08/WWDC21-A-first-look-at-Apples-new-Augmented-Reality-features</link>
            <guid>https://engineering.monstar-lab.com/en/post/2021/06/08/WWDC21-A-first-look-at-Apples-new-Augmented-Reality-features</guid>
            <pubDate>Tue, 08 Jun 2021 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>There are now over 1 billion augmented reality enabled iPhones and iPads which gives an incredible reach for your experiences. Here is an overview of what we know so far from this year's <a href="https://developer.apple.com/wwdc21/">WWDC</a> when it comes to augmented reality, with more details to unravel this week after the engineering sessions. You can find a list of recommended AR sessions and challenges to check out from this year's WWDC at the end of the article.</p>
<h2><a href="https://developer.apple.com/augmented-reality/realitykit/">RealityKit 2</a></h2>
<p>Introduced in 2019, RealityKit is Apple's rendering, animation, physics, and audio Swift framework built from the ground up with augmented reality in mind. This year, RealityKit gets some great improvements:</p>
<h3>Object Capture API</h3>
<p><img src="/assets/img/articles/2021-06-08-WWDC21-A-first-look-at-Apples-new-Augmented-Reality-features/objectcapture.webp" alt="">
<em>Image source: <a href="https://developer.apple.com/videos/wwdc2021/">Apple</a></em></p>
<p>Object Capture is a new API on macOS Monterey that enables anybody to create high-quality, photo-realistic 3D models of real-world objects in minutes. The biggest step-back I have seen for adding augmented reality capabilities to businesses is the cost of 3D content creation. So this new feature is massive for the AR world, especially for the E-commerce industry. You can use an iPhone, iPad or even a DSLR to take pictures and transform them into 3D models optimized for AR. You will be able to generate USDZ, USD and OBJ files with the level of detail you wish.</p>
<p><img src="/assets/img/articles/2021-06-08-WWDC21-A-first-look-at-Apples-new-Augmented-Reality-features/gallery.webp" alt=""></p>
<p>AR Quick Look is a great way to showcase your models created with Object Capture. Apple has even updated their <a href="https://developer.apple.com/augmented-reality/quick-look/">AR Quick Look Gallery</a> with new 3D model examples created with Object Capture, and I must say, I am very impressed!</p>
<p><img src="/assets/img/articles/2021-06-08-WWDC21-A-first-look-at-Apples-new-Augmented-Reality-features/shoe.webp" alt=""></p>
<p>Unity has been working closely with Apple to bring this new feature into their AR Companion app (<a href="https://blog.unity.com/technology/mars-companion-apps">Unity MARS</a>). You can read more about Apple's collaboration with Unity in <a href="https://blog.unity.com/technology/unity-reveals-latest-ar-companion-app-feature-at-apple-wwdc-21">this article</a>.</p>
<h3>Custom Shaders</h3>
<p>Custom shaders give developers more control over the rendering with custom render targets and materials. This update will make Apple AR experiences even more realistic.</p>
<p><img src="/assets/img/articles/2021-06-08-WWDC21-A-first-look-at-Apples-new-Augmented-Reality-features/shaders.webp" alt="">
<em>Video source: <a href="https://developer.apple.com/videos/wwdc2021/">Apple</a></em></p>
<h3>Dynamic assets</h3>
<p>The loading of assets is now more flexible, giving the option for example to programmatically change an image with every frame.</p>
<h3>Improved Entity Component System</h3>
<p>A new <code>System</code> object is available which will affect multiple entities in every frame of a RealityKit scene. Developers will be able to organize their assets better and build more complex apps.</p>
<h3>Character Controller</h3>
<p>Great addition for AR games, developers will now be able to control the characters and make them jump, scale and explore the AR worlds.</p>
<h2><a href="https://developer.apple.com/augmented-reality/arkit/">ARKit 5</a></h2>
<p>ARKit helps developers build powerful augmented reality experiences for millions of users worldwide. Here are the main updates announced this year:</p>
<h3>Location Anchors (in more cities)</h3>
<p>Location anchors were introduced in 2020 but with a limited availability in major United States cities. The update we are getting this year is additional cities including London. AR Coaching UI will also now support location anchors with a new <code>geoTracking</code> goal, meaning that you can easily guide your users through location based experiences.</p>
<h3>Expanded Face Tracking support</h3>
<p>Face Tracking support has been extended on the Ultra Wide camera on iPad Pro (5th generation). If you want to learn more about how to create AR Face filters, you can check out my Borderless Engineering Conference talk on the topic <a href="https://youtu.be/xSLDST-a6qU">Youtube video</a>.</p>
<h3>Improvements to Motion Tracking</h3>
<p>With ARKit, you can capture the motion of a person in real time with a single camera, previously possible only with special equipment. Great potential with this ARKit feature, so it is exciting to see what improvements they are bringing to it.</p>
<h3>App Clip Code Anchors</h3>
<p>Great addition to the list of supported ARKit anchors, you will be able to place your virtual content to a printed or digital App Clip Code.</p>
<p><img src="/assets/img/articles/2021-06-08-WWDC21-A-first-look-at-Apples-new-Augmented-Reality-features/appclips.webp" alt="">
<em>Video source: <a href="https://developer.apple.com/videos/wwdc2021/">Apple</a></em></p>
<h2>Apple Smart Glasses</h2>
<p>Still no Apple Smart Glasses announced, but that was expected. However, if you would like to catch up with the latest rumors, I can recommend <a href="https://www.macrumors.com/roundup/apple-glasses/">this in-depth roundup article from MacRumors</a>.</p>
<h2>WWDC21 AR sessions</h2>
<ul>
<li><a href="https://developer.apple.com/videos/play/wwdc2021/10076/">Create 3D models with Object Capture</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2021/10074/">Dive into RealityKit 2</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2021/10078/">AR Quick Look, meet Object Capture</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2021/10075/">Explore advanced rendering with RealityKit 2</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2021/10073/">Explore ARKit 5</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2021/10077/">Create 3D workflows with USD</a></li>
</ul>
<h2>WWDC21 AR challenges</h2>
<p><a href="https://developer.apple.com/wwdc21/challenges/">WWDC21 Challenges</a> are a fun way to dive right into the latest technologies and frameworks. Each day brings new challenges you can share on the Apple Developer Forums. This year, we have 2 augmented reality related challenges:</p>
<ol>
<li>Create your first 3D model with Object Capture (Tuesday)</li>
<li>Framework Freestyle (Thursday) - build and ARKit sample app in 100 lines of code or less</li>
</ol>
<h2>Resources</h2>
<ul>
<li><a href="https://developer.apple.com/augmented-reality/">Augmented Reality - Apple Developer</a></li>
<li><a href="https://youtu.be/v2Br76XFAYQ">Building Augmented Reality experiences with iOS - Engineering Awesome Conference 2020</a></li>
<li><a href="/en2020-09-07-Easy-web-augmented-reality-with-ar-quick-look">Easy web augmented reality with AR Quick Look</a></li>
<li><a href="/en2020-04-26-how-to-convert-3d-models-to-usdz-files-using-apples-reality-converter">How to convert 3D models to USDZ files using Apple's Reality Converter</a></li>
<li><a href="/en2019-10-07-Using-USDZ-for-a-better-AR-experience">Using USDZ for a better AR experience</a></li>
<li><a href="/en2019-12-31-How-to-make-an-Augmented-Reality-decorating-experience-app-with-AR-Quick-Look">How to make an augmented reality decorating experience app with AR Quick Look</a></li>
</ul>
<p><em>Article Photo by <a href="https://developer.apple.com/videos/wwdc2021/">Apple</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Static type checking in PicoRuby]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2021/05/27/Static-type-checking-in-PicoRuby</link>
            <guid>https://engineering.monstar-lab.com/en/post/2021/05/27/Static-type-checking-in-PicoRuby</guid>
            <pubDate>Thu, 27 May 2021 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>The static type system ensures correspondence of the "type" among arguments, parameters, and return values.
The point is that you can notice some kinds of bugs in advance of execution.</p>
<p>Static typing languages like TypeScript, Go, and Swift are mainstream in programming nowadays.
Because the IT industry has grown and the division of labor has been promoted, the benefit of static typing is quite remarkable.</p>
<p>So, how are dynamic typing languages like PHP, Python, and Ruby catching up the tide?</p>
<h2>Gradual typing and annotations</h2>
<p>PHP offers "Type declarations" from version 5 and the possibility of declaration is getting broader.</p>
<pre><code class="hljs language-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sayhello</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-variable">$name</span></span>): <span class="hljs-title">string</span>
</span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">print</span>(<span class="hljs-string">'Hello '</span> . <span class="hljs-variable">$name</span>);
}
<span class="hljs-title function_ invoke__">sayhello</span>(<span class="hljs-string">"Monstar"</span>);
=> Hello Monstar
</code></pre>
<p>Python 3 has introduced "Type hints" which allows you to annotate a type of variables and functions.</p>
<pre><code class="hljs language-python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">sayhello</span>(<span class="hljs-params">name: <span class="hljs-built_in">str</span></span>) -> <span class="hljs-built_in">str</span>:
  <span class="hljs-keyword">return</span> <span class="hljs-built_in">print</span>(<span class="hljs-string">'Hello '</span> + name)
sayhello(<span class="hljs-string">'Monstar'</span>)
=> Hello Monstar
</code></pre>
<p>Both PHP and Python support gradual static typing by their languages themselves.
In other words, they deal with the types in syntax parsing.</p>
<h2>Not the syntax, but a library</h2>
<p>Matz says that Ruby will not introduce any syntax-level type annotations. I agree with his philosophy since once a language has introduced a static type system in its syntax, it should be maintained to keep backward compatibility. It will possibly be a negative legacy.</p>
<p>Instead, the Ruby ecosystem introduced "RBS" as an exclusive language that describes the structure of a Ruby program.
And you can check the types of your Ruby values described in RBS with "Steep" which is a gradual static type checker.
The point is that both RBS and Steep aren't supported by the syntax of Ruby.</p>
<p>Because CRuby thus offers static type checking as libraries instead of its syntax parsing, unlike PHP and Python, other Ruby language implementations like JRuby and mruby can gracefully take advantage of it, too.</p>
<p><strong>This is, indeed, a hidden innovation!</strong></p>
<h2>Static type checking in firmware programming</h2>
<p>I wrote about PicoRuby and PRK Firmware in <a href="/2021/04/13/PicoRuby">my previous article "PicoRuby"</a> the other day.
In short, PicoRuby is a Ruby implementation for microcontrollers.
PRK Firmware is a framework of keyboard firmware in PicoRuby.</p>
<p>One thing troublesome to develop firmware for microcontrollers is debugging on actual devices.
You need to build an executable from your application program, detach the USB cable from your dev board (because you must have connected it just before, right? :), reconnect the cable while pushing a boot button on the device, then flash the executable to the device's ROM with a flashing script.</p>
<p>You have to do a messy procedure like this every time you amend the program.
That's why you really need to find trivial mistakes such as assigning a String to a variable that should accept only a Symbol while building an executable.</p>
<p>On the other hand, when you write a Rails application, you can easily fix your TYPO without rebooting Rails server.</p>
<p>You now understand why RBS and Steep are good news for PicoRuby in particular.</p>
<h2>How RBS and Steep work together in PicoRuby</h2>
<p>If you want to know about RBS and Steep, there is nothing better than the official documentation listed at the bottom of this article.
However, there is still worth showing how RBS and Steep bring a benefit.</p>
<h3>Types in parameters and arguments</h3>
<p>(All the example code below is abbreviated for explanation)</p>
<p>Let's say Keyboard class has <code>add_layer</code> method to let you be able to configure your keymap.</p>
<pre><code class="hljs language-ruby"><span class="hljs-comment"># in keyboard.rb</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">Keyboard</span>
  <span class="hljs-keyword">def</span> <span class="hljs-title function_">add_layer</span>(<span class="hljs-params">name, map</span>)
    new_map = <span class="hljs-title class_">Array</span>.new(map.size)
    map.each_with_index <span class="hljs-keyword">do</span> |<span class="hljs-params">row, row_index</span>|
      new_map[row_index] = <span class="hljs-title class_">Array</span>.new(row.size)
      row.each_with_index <span class="hljs-keyword">do</span> |<span class="hljs-params">key, col_index</span>|
        keycode_index = <span class="hljs-variable constant_">KEYCODE</span>.index(key)
        new_map[row_index][col_index] = <span class="hljs-keyword">if</span> keycode_index
          keycode_index * -<span class="hljs-number">1</span> <span class="hljs-comment"># Integer</span>
        <span class="hljs-keyword">else</span>
          key                <span class="hljs-comment"># Symbol</span>
        <span class="hljs-keyword">end</span>
      <span class="hljs-keyword">end</span>
    <span class="hljs-keyword">end</span>
    <span class="hljs-variable">@layers</span>[name] = new_map  <span class="hljs-comment"># Array[Array[Integer | Symbol]]</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>

<span class="hljs-comment"># in keyboard.rbs which describes the structure of Keyboard class</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">Keyboard</span>
  <span class="hljs-keyword">def</span> <span class="hljs-title function_">add_layer</span>: (Symbol name, Array[Array[Symbol]] map) -> void
<span class="hljs-keyword">end</span>
</code></pre>
<p><code>keyboard.rbs</code> tells that <code>add_layer</code> method accepts two arguments.
The first one is <code>name</code> and should be a Symbol.
The latter is <code>map</code> and should be an Array of Array of Symbol.</p>
<p>You can call <code>Keyboard#add_layer</code> and <code>steep check</code> successfully passes like this:</p>
<pre><code class="hljs language-ruby">keyboard.add_layer <span class="hljs-symbol">:default</span>, [
  %i(<span class="hljs-variable constant_">KC_ESCAPE</span> <span class="hljs-variable constant_">KC_Q</span> <span class="hljs-variable constant_">KC_W</span> <span class="hljs-variable constant_">KC_E</span> <span class="hljs-variable constant_">KC_R</span> <span class="hljs-variable constant_">KC_T</span> <span class="hljs-variable constant_">KC_Y</span> <span class="hljs-variable constant_">KC_U</span> <span class="hljs-variable constant_">KC_I</span> <span class="hljs-variable constant_">KC_O</span> <span class="hljs-variable constant_">KC_P</span> <span class="hljs-variable constant_">KC_BSPASE</span>),
  <span class="hljs-comment"># The Array of Symbol goes on...</span>
]

<span class="hljs-variable">$ </span>steep check
<span class="hljs-comment"># Type checking files:</span>
...............
No type error detected. 🍵
</code></pre>
<p>You will be warned if you mistakenly wrote like this:</p>
<pre><code class="hljs language-ruby">keyboard.add_layer <span class="hljs-string">"default"</span>, [
  %i(<span class="hljs-variable constant_">KC_ESCAPE</span> <span class="hljs-variable constant_">KC_Q</span> <span class="hljs-variable constant_">KC_W</span> <span class="hljs-variable constant_">KC_E</span> <span class="hljs-variable constant_">KC_R</span> <span class="hljs-variable constant_">KC_T</span> <span class="hljs-variable constant_">KC_Y</span> <span class="hljs-variable constant_">KC_U</span> <span class="hljs-variable constant_">KC_I</span> <span class="hljs-variable constant_">KC_O</span> <span class="hljs-variable constant_">KC_P</span> <span class="hljs-variable constant_">KC_BSPASE</span>),
  <span class="hljs-comment"># The Array of Symbol goes on...</span>
]

<span class="hljs-variable">$ </span>steep check
<span class="hljs-comment"># Type checking files:</span>
.............F.
keymap.<span class="hljs-symbol">rb:</span><span class="hljs-number">22</span><span class="hljs-symbol">:</span><span class="hljs-number">19</span>: [error] Cannot pass a value of type <span class="hljs-string">`::String`</span> as an argument of type <span class="hljs-string">`::Symbol`</span>
│   <span class="hljs-symbol">:</span><span class="hljs-symbol">:String</span> &#x3C;: <span class="hljs-symbol">:</span><span class="hljs-symbol">:Symbol</span>
│     <span class="hljs-symbol">:</span><span class="hljs-symbol">:Object</span> &#x3C;: <span class="hljs-symbol">:</span><span class="hljs-symbol">:Symbol</span>
│       <span class="hljs-symbol">:</span><span class="hljs-symbol">:BasicObject</span> &#x3C;: <span class="hljs-symbol">:</span><span class="hljs-symbol">:Symbol</span>
│
│ Diagnostic <span class="hljs-variable constant_">ID</span>: Ruby::ArgumentTypeMismatch
│
└ keyboard.add_layer <span class="hljs-string">"default"</span>, [
                     ~~~~~~~~~
Detected <span class="hljs-number">1</span> problem from <span class="hljs-number">1</span> file
</code></pre>
<h3>Types in instance variables</h3>
<p>As a result of <code>add_layer</code> method, the instance variable <code>@layers</code> should be a Hash with Symbol as a key and Array of Array of "Integer or Symbol" as its value.
The RBS code below ensures it (and has a type definition of <code>@special_procs</code> mentioned later):</p>
<pre><code class="hljs language-ruby"><span class="hljs-comment"># keyboard.rbs</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">Keyboard</span>
  <span class="hljs-variable">@layers</span>: Hash[Symbol, Array[Array[Integer |<span class="hljs-params"> Symbol]]]
  @special_procs: Hash[Symbol, Proc]
<span class="hljs-keyword">end</span>
</span></code></pre>
<p>Steep understands you are handling the variable as a specific type by a comment form annotation <code># @type var key: Symbol</code>.</p>
<pre><code class="hljs language-ruby">layer = <span class="hljs-variable">@layers</span>[<span class="hljs-symbol">:default</span>]
layer.each <span class="hljs-keyword">do</span> |<span class="hljs-params">rows</span>|
  rows.each <span class="hljs-keyword">do</span> |<span class="hljs-params">cols</span>|
    cols.each <span class="hljs-keyword">do</span> |<span class="hljs-params">key</span>| <span class="hljs-comment"># at this point, `key` should be an Integer or a Symbol</span>
      <span class="hljs-keyword">next</span> <span class="hljs-keyword">if</span> key.is_a?(Integer)
      <span class="hljs-comment"># <span class="hljs-doctag">@type</span> var key: Symbol</span>
      <span class="hljs-variable">@special_procs</span>[key].call
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>Because a comment form annotation is just a comment in terms of Ruby syntax, there is no compatibility issue if Ruby's type system is basically changed in the future.
There are also no worries that type annotation syntax will be an obstruction against a new syntax.
This is what Matz says, I think.</p>
<p>Lastly, Steep sharply makes you notice if you mistakenly call <code>each</code> method for <code>key</code> that should be an Integer or a Symbol before you are going to flash your executable into the device which will crash for sure 💥</p>
<pre><code class="hljs language-ruby">      <span class="hljs-keyword">case</span> key.<span class="hljs-keyword">class</span>
        ...
      <span class="hljs-keyword">else</span>
        key.each { do_something }
      <span class="hljs-keyword">end</span>

<span class="hljs-variable">$ </span>steep check
[error] Type <span class="hljs-string">`(::Symbol | ::Integer)`</span> does <span class="hljs-keyword">not</span> have method <span class="hljs-string">`each`</span>
│ Diagnostic <span class="hljs-variable constant_">ID</span>: Ruby::NoMethod
│
└      key.each { do_something }
           ~~~~
</code></pre>
<h2>Conclusion</h2>
<ul>
<li>RBS is an independent language that describes the type information of a Ruby program</li>
<li>Steep is a library that statically checks types in Ruby program using RBS formed annotations</li>
<li>CRuby syntax itself doesn't suport static type checking</li>
<li>Therefore, other implementations of Ruby can use RBS and Steep. Even PicoRuby can do it❗</li>
<li>Static type checking in firmware programming is truly helpful</li>
</ul>
<h2>Resources</h2>
<ul>
<li><a href="https://www.youtube.com/watch?v=1l3U1X3z0CE&#x26;t=1516s">Matz's keynote in RubyConf 2016</a> describing his opinion about type checking</li>
<li>RBS <a href="https://github.com/ruby/rbs#readme">https://github.com/ruby/rbs#readme</a></li>
<li>Steep <a href="https://github.com/soutaro/steep#readme">https://github.com/soutaro/steep#readme</a></li>
<li>Although this article didn't mention, you may also want to check <a href="https://github.com/ruby/typeprof#readme">TypeProf</a></li>
<li>Find how PRK Firmware actually handles RBS and Steep in practice here: <a href="https://github.com/picoruby/prk_firmware/tree/master/src/ruby">https://github.com/picoruby/prk_firmware/tree/master/src/ruby</a></li>
</ul>
<p>![Helix low profile version with PRK Firmware on Raspberry Pi Pico](/assets/img/articles/2021-05-27-Static-type-checking-in-PicoRuby/header.webp</p>
<p><em>Article Photo by <a href="https://twitter.com/hasumikin">hasumikin</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[NestJS Starter Kit, Monstarlab edition]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2021/04/30/NestJS-Starter-Kit-Monstar-Lab-Edition</link>
            <guid>https://engineering.monstar-lab.com/en/post/2021/04/30/NestJS-Starter-Kit-Monstar-Lab-Edition</guid>
            <pubDate>Fri, 30 Apr 2021 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><strong>TL;DR:</strong> We built a Node.js backend starter kit based on <a href="https://github.com/nestjs/nest">NestJS Framework</a>.
It follows a <strong>Monolithic</strong> architecture and implements <strong>REST APIs</strong>.</p>
<p>The starter kit is completely open source and can be found at this <a href="https://github.com/monstar-lab-oss/nestjs-starter-rest-api">Github Repo</a>.</p>
<h2>Introduction</h2>
<p><a href="https://monstar-lab.com/global/">Monstarlab</a> is a product development and consultation company, which helps other organizations solve their business problems, with the help of the latest technological solutions.</p>
<p>We engineers at Monstarlab tend to work on new customer projects every 4-6 months. We noticed that every time we kickstarted a new project, we spent a considerable amount of time on choosing our technology stack, software architecture, etc.</p>
<p>While we agree that there are no silver bullets, we concluded that some things could be improved in this regard. For instance, we do not need to redesign our logger service, our request validators, JWT authentication middleware, etc. every single time!</p>
<p><strong>Tada!</strong> Hence this starter kit.</p>
<h2>The Game plan</h2>
<p>This wasn't the first time one of us decided something like this needs to be done. Many of us have tried to solve this issue in the past, but unfortunately have failed due to a variety of reasons like time management, motivation, the difference of opinions, etc.</p>
<p>We were worried this time could turn out to be no different, hence we decided to try out a few things differently.</p>
<h4>Keeping it open source.</h4>
<p>In our previous attempts at doing this, we noted we lost motivation halfway due to various reasons. Keeping it open source allowed us to develop a sense of achievement, that we could share this project with others in the community even outside of our organization.</p>
<h4>Sponsored Hackathons.</h4>
<p>Almost all of us contributing to this project could only do so in our free time. Since the weekdays mostly go towards working on the business problems and projects directly with our clients, the time we could spend on this was very limited.</p>
<p>After brainstorming a bit on this issue, we decided to hack away at this starter kit on our weekends.
Monstarlab was kind enough to offer us generous amounts of free food to run these hackathons.
This gave us a chance to keep up with our release timelines for the starter kit project.</p>
<p><img src="/assets/img/articles/2021-04-30-NestJS-Starter-Kit-Monstar-Lab-Edition/Q3-B&#x26;W.webp" alt=""></p>
<h4>We need to go left, no right...</h4>
<p>What do we put in this starter kit. What do we <strong>NOT</strong> put in this starter kit. These discussions were always difficult and time consuming.</p>
<p>The conclusion was as follows:</p>
<ul>
<li>There will be 2 main types of starter kits, one based on monolithic pattern, and the other one based on microservices.</li>
<li>Within monolithic, we decided to further have REST API based and GraphQL based.</li>
<li>The starter kits will be very lightweight. To include some advanced features we will also create a <a href="https://github.com/monstar-lab-oss/nestjs-sample-solutions">Sample Solutions Repo</a> which will showcases some real-life production projects built on top of these starter kits.</li>
</ul>
<h2>Our features</h2>
<p>As pointed above, one of our main principles has been to keep the starter kit as lightweight as possible. With that in mind, here are some of the features that we have added to this starter kit.</p>
<h4>Authentication:</h4>
<p>Authentication is something that almost all of our projects need. We haven’t reinvented the wheel here. We use the popular Node.js library, PassportJS to do most of the heavy lifting for us in our JWT implementation. We use bcrypt library to ensure we are correctly hashing our passwords with salts, etc.
For more enterprise grade projects, we want to encourage everyone to use robust identity management projects like KeyCloak, Okta, etc.
We might be working on such examples in the coming future, so do keep an eye on our <a href="https://github.com/monstar-lab-oss/nestjs-sample-solutions">Sample Solutions Repo</a>. Do feel free to reach out to us for consultation and we can work on this on priority. :)</p>
<h4>Authorization:</h4>
<p>Based on top of authentication, the next thing that most projects require is Authorization. Authorization refers to the process that determines what a user is able to do. We have implemented a basic Role Based Access Control (RBAC) authorization module. To check out how it works and how easy it is for you to use it, refer to this <a href="https://github.com/monstar-lab-oss/nestjs-starter-rest-api/blob/master/docs/acl.md">doc file</a> within the project repository.</p>
<h4>ORM Integration</h4>
<p>Object–relational mapping (ORM) is a technique that lets you query and manipulate the data from a database using an object-oriented paradigm. TypeORM encapsulates the code needed to manipulate the data, so we don't need to use raw SQL anymore.</p>
<h4>DB Migrations</h4>
<p>Migrations provide a way to incrementally update the database schema to keep it in sync with the application's data model while preserving existing data in the database.
DB Schema migrations have always been a nightmare for many of us when it comes to a relational database. TypeORM allows us to easily generate the migration files.</p>
<h4>Logging</h4>
<p>NestJS comes with its own built-in logger. But it is a text-based logger and does not suit production requirements. Hence we implemented our own custom logger based on Winston, which is a very popular production logging package.
What’s even better is that we pass a request context throughout our call stack, which allows us to do <strong>contextual logging</strong> with additional information like request-id, request-user, etc.</p>
<h4>Request Validation</h4>
<p>It is best practice to validate the correctness of any data sent into a web application. NestJS makes use of the powerful <a href="https://github.com/typestack/class-validator">class-validator package</a> and its declarative validation decorators.</p>
<h4>Pagination</h4>
<p>No REST API feels complete without having some basic pagination in it. We went ahead and added support for simple pagination parameters like limit and offset.
Our responses also always include a child property called <code>meta</code>. This includes information like total resource count, etc which further makes the pagination implementation smoother on the frontend application side.</p>
<h4>Docker Ready</h4>
<p>Need we say more?
<img src="/assets/img/articles/2021-04-30-NestJS-Starter-Kit-Monstar-Lab-Edition/memegenerator.webp" alt=""></p>
<h4>Auto-generated OpenAPI</h4>
<p>This is one of our most loved features in NestJS.
The OpenAPI specification is a language-agnostic definition format used to describe RESTful APIs. Nest provides a dedicated module that allows generating such a specification by leveraging decorators.</p>
<h4>More features</h4>
<p>Apart from these features above, our start-kit comes loaded with a bunch of minor awesomeness like prettier integration, commit-linting husky hooks, package import sorting, SonarCloud github actions, docker-compose for database dependencies, etc. :D</p>
<h2>Conclusion</h2>
<p>Recently we released a major version of our starter kit based on Monolithic + REST API.</p>
<p>It was a “work-in-progress” for a long time, but we finally believe it is ready for everyone to try it out and provide us with their invaluable feedback.</p>
<h2>What’s next</h2>
<h4>Auto-generated ChangeLog</h4>
<p>This is the last one in our backlog for this starter kit. After this feature, we believe we won’t be touching this particular starter kit for a while.</p>
<p>Next on our roadmap is a starter kit which uses GraphQL.</p>
<h2>Resources</h2>
<ul>
<li><a href="https://nestjs.com/">Official NestJS Website</a></li>
</ul>
<p><em>Article Photo by <a href="https://unsplash.com/@gaellemarcel">Gaelle Marcel</a></em><br>
<em>Docker Meme Photo by <a href="https://memegenerator.net/">Meme Generator</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[PicoRuby]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2021/04/13/PicoRuby</link>
            <guid>https://engineering.monstar-lab.com/en/post/2021/04/13/PicoRuby</guid>
            <pubDate>Tue, 13 Apr 2021 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><a href="https://github.com/picoruby/picoruby">PicoRuby (f/k/a mmruby)</a> is an alternative Ruby implementation dedicated to one-chip microcontrollers. The binary fits into less than 256 KB ROM and runs on less than 64 KB RAM.
This development was supported by <a href="https://www.ruby.or.jp/en/news/20201022">the Ruby Association (RA) Grant program 2020</a> and mentored by <strong>Matz</strong> himself!
<em>Monstarlab is one of the developers of the Ruby programming ecosystem.</em></p>
<p>C/C++ is still the first language for microcontrollers though, script languages like Lua and MicroPython are nicely developing in embedded systems nowadays.
I, an enthusiastic Ruby fan, dreamed that Ruby would be a real choice to make hardware.
The time has come.</p>
<h2>Ruby in small footprint</h2>
<p>Implementing a Ruby interpreter for microcontrollers was regarded as difficult because of its free and descriptive grammatical rules.
An upside of Ruby is also a downside in terms of a small footprint.
The unnecessity of parenthesizing arguments in method call may increase the size of the syntax parser table.
The dynamic type decision system will complicate the runtime environment.</p>
<p>Despite these valid observations, some people including me are trying to achieve it.
I am working at the Shimane development branch at Monstarlab Japan.</p>
<h2>Developing Ruby as my daily work</h2>
<p>After my proposal was adopted by the RA Grant, I talked to Daisuke, our CTO in Japan, about whether we can work on the project as an official work of our company.
I became a "part-time committer" of the OSS at that moment since he promptly agreed to my suggestion.</p>
<p>Matz, the founder of Ruby, has been responsible for my mentorship during the project as mentioned above.
I could (even now I still can) text him to ask how Ruby works, why did he design Ruby like that, and what are his thoughts in reducing the memory consumption.
Thanks to Matz's big support, I successfully submitted <a href="https://github.com/picoruby/picoruby/wiki/Final-report-for-the-Grant-Program-2020-of-Ruby-Association">the final report</a>.</p>
<h2>A keyboard firmware in Ruby</h2>
<p>Now you can freely fork and build PicoRuby, though it is probably hard to use it properly without any idea.
My next goal is to make a "killer product" to indicate that PicoRuby deserves to be your language of choice.</p>
<p>I definitely know you are a "keyboard lover" if you are a software programmer, right?
I have good news for you that I recently released "<a href="https://github.com/picoruby/prk_firmware">PRK Firmware</a>" which is a keyboard firmware platform and runs on Raspberry Pi Pico (RP2040 chip).
<code>keymap.rb</code> which configures keymap and allows you to modify keyboard behavior can be written as follows:</p>
<h3>Initialize a Keyboard object and configure GPIO pins</h3>
<pre><code class="hljs language-ruby">kbd = Keyboard.new

kbd.init_pins(
  [ <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span> ],                                 <span class="hljs-comment"># row0, row1,... respectively</span>
  [ <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>, <span class="hljs-number">10</span>, <span class="hljs-number">11</span>, <span class="hljs-number">12</span>, <span class="hljs-number">13</span>, <span class="hljs-number">14</span>, <span class="hljs-number">15</span>, <span class="hljs-number">16</span>, <span class="hljs-number">17</span> ]  <span class="hljs-comment"># col0, col1,... respectively</span>
)
</code></pre>
<p>Supposing you are building a 4x12 matrix, a so-called "40%" keyboard.</p>
<h3>Add layers as you like</h3>
<pre><code class="hljs language-ruby"><span class="hljs-comment"># (I skipped the right-side of the keymap)</span>
kbd.add_layer <span class="hljs-symbol">:default</span>, [
  %i(<span class="hljs-variable constant_">KC_ESC</span>  <span class="hljs-variable constant_">KC_Q</span>        <span class="hljs-variable constant_">KC_W</span>        <span class="hljs-variable constant_">KC_E</span>      <span class="hljs-variable constant_">KC_R</span>     <span class="hljs-variable constant_">KC_T</span> ...),
  %i(<span class="hljs-variable constant_">KC_TAB</span>  <span class="hljs-variable constant_">KC_A</span>        <span class="hljs-variable constant_">KC_S</span>        <span class="hljs-variable constant_">KC_D</span>      <span class="hljs-variable constant_">KC_F</span>     <span class="hljs-variable constant_">KC_G</span> ...),
  %i(<span class="hljs-variable constant_">KC_LSFT</span> <span class="hljs-variable constant_">KC_Z</span>        <span class="hljs-variable constant_">KC_X</span>        <span class="hljs-variable constant_">KC_C</span>      <span class="hljs-variable constant_">KC_V</span>     <span class="hljs-variable constant_">KC_B</span> ...),
  %i(<span class="hljs-variable constant_">KC_NO</span>   <span class="hljs-variable constant_">KC_NO</span>       <span class="hljs-variable constant_">KC_LGUI</span>     <span class="hljs-variable constant_">KC_LALT</span>   <span class="hljs-variable constant_">KC_LCTL</span>  <span class="hljs-variable constant_">RAISE_ENTER</span> ...)
]
kbd.add_layer <span class="hljs-symbol">:raise</span>, [
  %i(<span class="hljs-variable constant_">KC_ESC</span>  <span class="hljs-variable constant_">KC_1</span>        <span class="hljs-variable constant_">KC_2</span>        <span class="hljs-variable constant_">KC_3</span>      <span class="hljs-variable constant_">KC_4</span>     <span class="hljs-variable constant_">KC_5</span> ...),
  %i(<span class="hljs-variable constant_">KC_TAB</span>  <span class="hljs-variable constant_">KC_F1</span>       <span class="hljs-variable constant_">KC_F2</span>       <span class="hljs-variable constant_">KC_F3</span>     <span class="hljs-variable constant_">KC_F4</span>    <span class="hljs-variable constant_">KC_F5</span> ...),
  %i(<span class="hljs-variable constant_">KC_LSFT</span> <span class="hljs-variable constant_">KC_LBRACKET</span> <span class="hljs-variable constant_">KC_RBRACKET</span> <span class="hljs-variable constant_">KC_SCOLON</span> <span class="hljs-variable constant_">KC_MINUS</span> <span class="hljs-variable constant_">KC_PLUS</span> ...),
  %i(<span class="hljs-variable constant_">KC_NO</span>   <span class="hljs-variable constant_">KC_NO</span>       <span class="hljs-variable constant_">KC_LGUI</span>     <span class="hljs-variable constant_">KC_LALT</span>   <span class="hljs-variable constant_">KC_LCTL</span>  <span class="hljs-variable constant_">RAISE_ENTER</span> ...)
]
</code></pre>
<p>You don't need to insert <code>,</code> (comma) between <code>KC_x</code> and <code>KC_x</code> while QMK firmware requires you to do.</p>
<h3>Define "mode" keys</h3>
<pre><code class="hljs language-ruby"><span class="hljs-comment">#                   Your mode       Keycode or  Keycode or Proc                     Release time      Re-push time</span>
<span class="hljs-comment">#                   key name        Proc when   while you keep press                threshold(ms)     threshold(ms)</span>
<span class="hljs-comment">#                                   you  click                                      to consider as    to consider as</span>
<span class="hljs-comment">#                                                                                   `click the key`   `hold the key`</span>
kbd.define_mode_key <span class="hljs-symbol">:RAISE_ENTER</span>, [ <span class="hljs-symbol">:KC_ENTER</span>,  <span class="hljs-title class_">Proc</span>.new { kbd.hold_layer <span class="hljs-symbol">:raise</span> }, <span class="hljs-number">200</span>,              <span class="hljs-number">150</span> ]
kbd.define_mode_key <span class="hljs-symbol">:LOWER_SPACE</span>, [ <span class="hljs-symbol">:KC_SPACE</span>,  <span class="hljs-title class_">Proc</span>.new { kbd.lock_layer <span class="hljs-symbol">:lower</span> }, <span class="hljs-number">200</span>,              <span class="hljs-number">150</span> ]
</code></pre>
<p>You will easily see <code>Proc</code> object works particularly.</p>
<h3>Want to input ":" without Shift and ";" with Shift?</h3>
<p>Sure!</p>
<pre><code class="hljs language-ruby">kbd.before_report <span class="hljs-keyword">do</span>
  kbd.invert_sft <span class="hljs-keyword">if</span> kbd.keys_include?(<span class="hljs-symbol">:KC_SCOLON</span>)
<span class="hljs-keyword">end</span>
</code></pre>
<h2>Conclusion</h2>
<p>What do you say?
I hope you find <code>keymap.rb</code> is better than other firmware platforms :)</p>
<p>PRK Firmware will be frequently updated as it is still public beta.
Please check my <a href="https://twitter.com/hasumikin">Twitter</a> and <a href="https://github.com/picoruby">GitHub</a> if you want to be notified.</p>
<h2>Resources</h2>
<ul>
<li><a href="https://github.com/picoruby/picoruby">PicoRuby</a></li>
<li><a href="https://github.com/picoruby/picoruby/wiki/Final-report-for-the-Grant-Program-2020-of-Ruby-Association">Final report for Ruby Association</a></li>
<li><a href="https://github.com/picoruby/prk_firmware">PRK Firmware</a></li>
<li><a href="https://en.wikipedia.org/wiki/Yukihiro_Matsumoto">Yukihiro Matsumoto a.k.a Matz (the founder of Ruby)</a></li>
</ul>
<p><img src="/assets/img/articles/2021-04-13-PicoRuby/header.webp" alt="Developing PRK Firmware on Raspberry Pi Pico"></p>
<p><em>Article Photo by <a href="https://twitter.com/hasumikin">hasumikin</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Using Policy As Code to manage permissions in REST APIs]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2021/04/06/Policy-As-Code-In-REST-APIs</link>
            <guid>https://engineering.monstar-lab.com/en/post/2021/04/06/Policy-As-Code-In-REST-APIs</guid>
            <pubDate>Tue, 06 Apr 2021 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Authorization and Authentication (AuthZ and AuthN) are very critical parts of any system for identity and permissions validation and enforcement, <strong>AuthN</strong> is basically the process to verify who the user is, while <strong>AuthZ</strong> is the process of validating the user’s permissions to access or perform certain functions/actions.</p>
<p>There are plenty of identity providers to handle the AuthN part, however in this blog post, we are going to cover the AuthZ, the permissions validation and enforcement.</p>
<p>In any decent backend (e.g. Rest APIs, Event Processing), we certainly need to verify the identity and permissions, traditionally we will have a code similar to the below somewhere in the implementation to check for AuthZ:</p>
<pre><code class="hljs language-ts">    <span class="hljs-keyword">if</span> ( !user.<span class="hljs-title function_">hasRole</span>(<span class="hljs-string">"admin"</span>) ) {
        <span class="hljs-keyword">throw</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">"Not Authorized"</span>).
    }
</code></pre>
<p>Depending on the permissions model implemented (e.g. RBAC, ABAC), the above code could be slightly different, but in general, the idea is to enforce certain rules to make sure that we don’t over or under expose the data, that is what is called <strong>“Policy Enforcement”</strong>.</p>
<p>With that in mind, policy enforcement is traditionally tightly coupled with the business logic implementation, and any change to the rules, means business logic needs to be updated as well. This is where Open Policy Agent (OPA) comes in handy, it allows developers to manage policies as code and offers the possibility to have centralized policy management.</p>
<blockquote>
<p>The Open Policy Agent (OPA, pronounced “oh-pa”) is an open source,
general-purpose policy engine that unifies policy enforcement across
the stack. OPA provides a high-level declarative language that lets
you specify policy as code and simple APIs to offload policy
decision-making from your software. You can use OPA to enforce
policies in microservices, Kubernetes, CI/CD pipelines, API gateways,
and more.” - <a href="https://www.openpolicyagent.org">OPA Website</a></p>
</blockquote>
<p>OPA allows us to define policies and rules in a declarative way as code (in REGO language), and use the OPA API or libraries to make a decision based on the previously defined policies.</p>
<p>![Open Policy Agent Flow](/assets/img/articles/2021-04-06-Policy-As-Code-In-REST-APIs/opa.webp
<em>Image source: <a href="https://www.openpolicyagent.org/docs/latest/">OPA Website</a></em></p>
<p>As per the Diagram above, we give OPA a policy and some data (JSON), and whenever a request (e.g. REST API call) or an event (Kafka or RabbitMQ) comes in, OPA will make a decision based on the data and request/event payload, and then we can allow or reject the request based on the outcome.</p>
<pre><code class="hljs language-ini">allow {
	<span class="hljs-attr">input.method</span> = <span class="hljs-string">"DELETE"</span>
	<span class="hljs-attr">input.path</span> = [<span class="hljs-string">"posts"</span>, postId]
	<span class="hljs-attr">input.user</span> = user.roles[<span class="hljs-string">'admin'</span>]
}
</code></pre>
<h2>Fine-grained permissions on REST API endpoints</h2>
<p>When implementing any REST API endpoint or event handler, more often than not, we need to perform two things to check permissions:</p>
<ol>
<li><strong>Resource Access</strong>: can the user access this endpoint (has a role or access level)? Example: a reader cannot delete a post.</li>
<li><strong>Resource Ownership</strong>: if the user can call this endpoint, we might need to filter by ownership, and allow access to resources he owns only. Example: an author can only edit/delete his own posts (so even if he can access the endpoint <code>PUT /posts</code> and <code>DELETE /posts</code>, but some ownership filters apply).</li>
</ol>
<p>Implementing the above inside the endpoint code can make the code look a bit redundant and policies and rules are repeated many times.</p>
<p>We can give OPA the data (e.g. users, roles, permissions) and policies (rules, who can access what and when) and we can solve both problems in an elegant and centralized way.</p>
<h2>OPA Deployment Approaches</h2>
<p>There are multiple ways to integrate OPA in a solution, depending on the goal and the case of every solution.</p>
<h3>OPA as Library in projects</h3>
<p>OPA offers a Go library that simplifies the integration of policies and queries for Go based projects, there are also other libraries and integrations such OPA WASM (Web Assembly).</p>
<p>The advantages of using a library is simplicity and it offers a quick way to start using OPA right away without having to interact with an external OPA server or REST API.</p>
<p>However, as of now, the only officially supported library is for Go based projects, if your project is based on another stack, then this approach won’t be helpful.</p>
<h3>OPA as an external service</h3>
<p>Another approach is to deploy OPA as an external service and use its REST API to query and make authorization requests and decisions.</p>
<p>This is especially useful in a Microservice-based environment, where multiple services need to query OPA for policy and authorization decisions, it allows for a single centralized AuthZ service, and enforces Separation of Concerns principle at this level.</p>
<p>This approach works fine in most cases, but there are two major problems that arise when the scale of the environment grows:</p>
<ol>
<li>The services need to be aware of OPA server, and they still need to implement certain logic to interact with OPA REST API.</li>
<li>If OPA is down for some reason, this means other services cannot operate properly.</li>
</ol>
<p>To solve these issues, we can enhance this approach even further by adding a proxy and using the sidecar pattern.</p>
<h3>OPA as a sidecar</h3>
<blockquote>
<p>This pattern is named Sidecar because it resembles a sidecar attached
to a motorcycle. In the pattern, the sidecar is attached to a parent
application and provides supporting features for the application. The
sidecar also shares the same lifecycle as the parent application,
being created and retired alongside the parent -
<a href="https://docs.microsoft.com/">Microsoft Docs</a></p>
</blockquote>
<p>The idea is to deploy OPA as an isolated service that lives next to the main services (each Microservice will have its own OPA instance), this will reduce latency problems since both the service and OPA will share the same network.</p>
<p>This pattern eliminates the single point of failure, since every service is now completely independent to make AuthZ decisions, and if one OPA goes down, this will not affect the other services.</p>
<p>But we still have the other issue, the services still need to be aware of OPA, how can this be resolved? - Service Proxy, specifically Envoy Proxy (<a href="https://www.envoyproxy.io/">https://www.envoyproxy.io/</a>).</p>
<p>Envoy is a service proxy used frequently in Microservices environments, and offers several features such as Service Discovery, Edge Proxy, Health Checks, Load Balancing and more.</p>
<p>Envoy allows us to wrap our service (Microservice + OPA Sidecar in this case), and every request hitting the service will be first received by Envoy, and with the help of the “External Authorization” feature offered by Envoy Proxy, we can separate the AuthZ decision making from service implementation and put it on Envoy Proxy.</p>
<p>We give Envoy the OPA address, and it will ask for a decision every time a request is received.</p>
<p>![OPA with Envoy sidecar](/assets/img/articles/2021-04-06-Policy-As-Code-In-REST-APIs/opa-envoy.webp</p>
<h2>Conclusion</h2>
<p>Combining Policy as Code engine with Microservices in a Sidecar pattern offers a huge improvement in terms of resiliency and reliability, although that setup is not as straightforward as other classic approaches.</p>
<h2>Resources:</h2>
<ol>
<li><a href="https://www.openpolicyagent.org/docs/latest/">OPA Documentation</a></li>
<li><a href="https://www.envoyproxy.io/docs/envoy/latest/">Envoy Proxy</a></li>
</ol>
<p><em>Article Photo by <a href="https://unsplash.com/photos/m_HRfLhgABo">Christopher Gower</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Azure DevOps Pipelines vs GitHub Actions]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2021/03/30/Azure-DevOps-Pipelines-vs-GitHub-Actions</link>
            <guid>https://engineering.monstar-lab.com/en/post/2021/03/30/Azure-DevOps-Pipelines-vs-GitHub-Actions</guid>
            <pubDate>Tue, 30 Mar 2021 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>In .NET we have been using Azure DevOps Pipelines for our CI/CD needs, but after Microsoft bought GitHub and has promised to support both GitHub Actions along with Azure DevOps Pipelines promising that each platform will be able to perform the same tasks, the answer to which provider to use is maybe not as clear cut as it has been in the past.</p>
<p>We will take a small look into the main differences between the two platforms looking both as a newcomer to DevOps but also as a more experienced user. The topics we will take a look into are the following:</p>
<ul>
<li>Adding tasks: how is the experience of setting up your pipelines/actions</li>
<li>Variables: how does the system handle variables both global and environment-specific</li>
<li>Environment hooks: how do you set up a hook into a server or similar you want to deploy to or modify</li>
</ul>
<p>There are of course a lot more to it than just these topics, but these topics are the basic ones that you will use for most CD pipelines/actions.</p>
<h2>Azure DevOps Pipelines</h2>
<p>Azure DevOps Pipelines is Microsoft's main CI/CD platform which traditionally has been used for .NET projects of all kinds. In Azure DevOps, there are also two different pipelines to get your head around: Build pipelines which build artifacts, and release pipelines which can take a build artifact and deploy it to an environment.</p>
<p>At the time of writing, there are two methods of defining build pipelines:</p>
<ol>
<li>Classic</li>
<li>YAML</li>
</ol>
<p>We will not go into the Classic as the functionality is the same, but the defined pipeline is stored in Azure DevOps and not the repository, where YAML serializes the pipeline into a YAML file.</p>
<p>There are two methods of editing YAML files:</p>
<ol>
<li>Manually editing the file in the repository using your favourite editor</li>
<li>Use the web portal</li>
</ol>
<p>We will focus on the web portal editor.</p>
<h3>Adding tasks</h3>
<p>Adding a task in Azure DevOps Pipelines is as simple as selecting the task you want in the task selector, entering the parameters you want and it's added to your YAML file where the cursor is currently placed when clicking add.</p>
<p>![Azure DevOps Pipeline add task example](/assets/img/articles/2021-03-30-Azure-DevOps-Pipelines-vs-GitHub-Actions/azure-devops-task-example.webp</p>
<h3>Variables</h3>
<p>Variables in specifically release pipelines can be defined on a release pipeline level or environment level, which means you as a DevOps developer can differentiate the variables between being global and environment-specific with ease without the pipelines needing to care, they just use the variable with a given name.</p>
<p>![Azure DevOps Release Pipeline variables example](/assets/img/articles/2021-03-30-Azure-DevOps-Pipelines-vs-GitHub-Actions/azure-devops-release-pipelines-variables.webp</p>
<h3>Environment hooks</h3>
<p>Environment hooks in Azure DevOps are set up inside the project settings, where you can use the hooks in your build steps by referencing the name of the environment hook.</p>
<h2>GitHub Actions</h2>
<p>GitHub Actions is GitHub's CI/CD pipeline setup. It has been implemented with an open source usage in mind and not any particular technology to build. GitHub was acquired by Microsoft in 2018 and Microsoft has promised to expand the functionality of the Actions and support it.</p>
<h3>Adding tasks</h3>
<p>As there is no official online editor of the build pipelines, you edit it using your favourite text editor in your cloned repository and push updates to the Git repository. Due to this method, you have to look up the parameters to the build steps you're using.</p>
<h3>Variables</h3>
<p>Variables, or secrets as they are called, are values that can be stored globally or per environment in GitHub. Once you've entered the secret's value, you cannot view it again, but it can be used in your Actions.</p>
<h3>Environment hooks</h3>
<p>In GitHub there's not anything specifically for environment hooks, you can use secrets for e.g. publish profiles to Azure Web Apps which performs the same task in practice.</p>
<h2>Comparison</h2>
<p>Comparing the two platforms, we can conclude functionality wise there isn't really any noticeable difference, but the difference becomes clearer when looking at the user interface and how much the interface helps the pipeline/action developer.</p>
<h3>Adding tasks</h3>
<p>Looking at the generated files, there is practically no difference. The difference lies in how much help is given to the developer. In GitHub Actions the developer must look up the parameters themselves while in Azure DevOps Pipelines, you can view all parameters in the editor with additional help of what the valid values are if the task developer has set it up.</p>
<h3>Variables</h3>
<p>The main difference between Azure DevOps Pipelines and GitHub Actions regarding variables, is that in Azure DevOps Pipelines, you can define whether you want to be able to view the variable values or not where in GitHub Actions, you are not able to view the values at all. Regarding using them in pipelines/actions, the functionality is practically identical for the developer.</p>
<h3>Environment hooks</h3>
<p>In GitHub Actions there are no specific menu or place for environment hooks while in Azure DevOps Pipelines, they are put into their own category. That's the main difference between the two, so it's mostly a matter of taste which of the two are most preferred.</p>
<h2>Summary</h2>
<p>GitHub Actions have over the last year gotten most of the functionality that was present in Azure DevOps Pipelines such as Environments and on-demand execution of Actions. The main difference between the two is in the adding tasks part of the implementation. In Azure DevOps Pipelines you are able to view the parameters of a task directly in the browser while in GitHub Actions you have to look in the documentation of the task you're using to get the same information. However, in Azure DevOps Pipelines, the tasks available are set at an organisation level where GitHub Actions is much more liberal with adding new tasks to an Action.</p>
<p>Another consideration is that in Azure DevOps you can define build pipelines for repositories outside Azure DevOps including GitHub where GitHub is restricted to repositories in GitHub only.</p>
<p>To summarise, if you're new to DevOps development, Azure DevOps Pipelines is easier to get started with as it's holding your hand through the process a bit more, but the features available in each offering is close to identical, so if your code is already in GitHub, it makes more sense to use GitHub Actions, but if your code is in Azure DevOps or you don't want to migrate your repository to GitHub, use Azure DevOps Pipelines to keep the processes in one place.</p>
<h2>Resources</h2>
<ul>
<li><a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/get-started/pipelines-get-started?view=azure-devops">Get started with Azure DevOps Pipelines</a></li>
<li><a href="https://docs.github.com/en/actions/quickstart">Get started with GitHub Actions</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Game server synchronization of large amounts of data in a battle]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2021/02/09/Game-server-Synchronization</link>
            <guid>https://engineering.monstar-lab.com/en/post/2021/02/09/Game-server-Synchronization</guid>
            <pubDate>Tue, 09 Feb 2021 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Multiple players interacting at the same time in a game room will generate a lot of data that needs to be synchronized, such as each player's basic data, change data, skill performance data, damage data, map data, monster data, etc. According to the refresh rate of the game's FPS, it needs to be synchronized to each client in a very short time, so two mainstream synchronization methods are produced: <code>frame synchronization</code> and <code>state synchronization</code>.</p>
<h2>What is frame synchronization?</h2>
<p>It is usually used in real-time strategy and battles that require very high real-time performance. It synchronizes the player's operation instructions which include the previous frame index. The client uploads the operation to the server, and the server does not calculate the game behavior after receiving it. It is forwarded to all clients. The most important concept here is that the <code>same input + the same timing = the same output</code>. The process of realizing frame synchronization is generally:</p>
<ol>
<li><strong>Synchronize the random number seed</strong>. In games, the use of random numbers is common. By synchronizing the random number seeds, the consistency of those can be maintained.</li>
<li><strong>Client uploads the operation instructions</strong>. Instructions include game operations and current frame index.</li>
<li><strong>The server broadcasts all client operations</strong>. If there is no operation, it also broadcasts a null command to drive the game frame forward.</li>
<li><strong>Because of the frame synchronization feature, we can easily replay the battle</strong>. The server records all operations, and the client requests the operation file to execute it again.</li>
</ol>
<h3>Advantages of frame synchronization</h3>
<ol>
<li>Save the amount of messages, each message transfer only needs to be accompanied by operation information, and the message package for state synchronization needs to be accompanied by various state information.</li>
<li>It is convenient to realize the synchronization problem of real-time battle games under weak networks, and the battle can be maintained with as few messages as possible.</li>
<li>To reduce the calculation pressure of the server, the state of the battle is synchronized, the server needs to calculate all the battle logic.</li>
<li>Improve development efficiency, because the server can only rebroadcast messages, and the client can also be separated from the server during development.</li>
<li>Obtain better expressive power in terms of client performance, because the client has obtained more performance management permissions, and does not have to be hard driven by data.</li>
<li>It is very convenient to make battle videos. With the initial information of the battle and the list of battle frames, the battle process can be perfectly reproduced.</li>
</ol>
<h3>Choice of network protocol</h3>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Transmission_Control_Protocol">TCP</a>: The message is stable, but slow. It is suitable for games that are not instantaneous and communication is not intensive, such as cards.</li>
<li><a href="https://en.wikipedia.org/wiki/User_Datagram_Protocol">UDP</a>: Message delivery is fast, but unstable, prone to packet loss and disorder. It is suitable for the key technology in the frame synchronization design of games with strong real-time, random value pseudo-random algorithm. Safe use of UDP protocol.</li>
</ul>
<p><img src="/assets/img/articles/2021-02-09-Game-server-Synchronization/frame1.webp" alt=""></p>
<p><img src="/assets/img/articles/2021-02-09-Game-server-Synchronization/frame2.webp" alt=""></p>
<h2>What is state synchronization?</h2>
<p><img src="/assets/img/articles/2021-02-09-Game-server-Synchronization/state1.webp" alt=""></p>
<p>To synchronize various states in the game, the client uploads operations to the server, the server calculates the results, broadcasts various states in the game and the client displays the content according to the state after receiving the state.
State synchronization is widely used in turn-based games and it is actually a loose synchronization. In its thinking, the consistency of the performance of different players on the screen is not an important indicator, as long as the result of each operation is the same.</p>
<p>Therefore, state synchronization does not require high network delay. Like playing RPG games, 200-300ms delay is also acceptable. But in RTS games, the 50ms delay is also obvious. For example, while on the move, a delay has passed after 50ms. During this process, client B needs to do some smooth processing locally, and finally make the movement normal. In a state-synchronized game, the damage is often displayed after the attack is over, because it needs to wait for the server calculation to end through special effects animation.</p>
<h3>Advantages of state synchronization</h3>
<ol>
<li>It is relatively simple, the client only needs to synchronize data according to the push of the server.</li>
<li>There are almost no plug-ins, because all operations are checked by the server.</li>
<li>It is suitable for some games with small real-time synchronization data: such as RPG, hang-up, chess and other game types.</li>
<li>Client-side development is less difficult, and problems are better positioned for debugging and development.</li>
</ol>
<p><img src="/assets/img/articles/2021-02-09-Game-server-Synchronization/state2.webp" alt=""></p>
<h2>Comparison of the two designs</h2>



































<table><thead><tr><th></th><th>State synchronization</th><th>Frame synchronization</th></tr></thead><tbody><tr><td>Flow rate</td><td>Very high</td><td>Relatively low</td></tr><tr><td>Combat playback</td><td>The record file is large</td><td>The record file is small</td></tr><tr><td>Security</td><td>High (server computing)</td><td>Low (client computing, unavoidable plug-in)</td></tr><tr><td>Server pressure</td><td>Large</td><td>Small</td></tr><tr><td>Caton performance</td><td>Teleportation, inexplicable blood loss</td><td>Battle Caton</td></tr></tbody></table>
<h2>Sources</h2>
<ul>
<li><a href="https://medium.com/@qingweilim/how-do-multiplayer-games-sync-their-state-part-1-ab72d6a54043">How do multiplayer games sync their state? Part 1</a></li>
<li><a href="https://www.gabrielgambetta.com/client-side-prediction-server-reconciliation.html">Fast-Paced Multiplayer (Part II): Client-Side Prediction and Server Reconciliation</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to test a fire extinguisher?]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2021/02/09/How-to-test-a-fire-extinguisher</link>
            <guid>https://engineering.monstar-lab.com/en/post/2021/02/09/How-to-test-a-fire-extinguisher</guid>
            <pubDate>Tue, 09 Feb 2021 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Once I asked my dad how to test a fire extinguisher. He answered:</p>
<p>"Very simple! It is necessary to check the expiration date, see that the seal is in place and that there is no visible damage to the case."</p>
<p>"Is that all? But how do you know it will work? How can you be sure?"</p>
<p>"I just know, that's all." - <em>Dad answered.</em></p>
<p>This question arose for a reason. I was asked the same when I was interviewing for the position of a tester.</p>
<p>I still don't know the correct answer to this question, but since I was hired, I probably answered correctly.</p>
<p>I answered that, in fact, the production of fire extinguishers is an assembly line. At each stage, control must be carried out, moreover, not only control of the quality of materials, sizes of parts, their density, etc., but also control over the equipment used in the manufacture. Also, don't forget about people. They shouldn't get too tired, the schedule should be optimal. Periodically, you need to remove the entire batch of fire extinguishers from the conveyor and check them in the field to determine the percentage of rejects. If the number is small, then everything is in order. But if it exceeds the norm, you need to urgently diagnose all equipment, find out and fix the cause.</p>
<p>Sounds pretty simple in theory. In practice, it turned out differently.</p>
<p>The question is:</p>
<p><strong>Is it possible to put a person at the end of the conveyor and give him the task of making sure that the result is ideal fire extinguishers? What can he do to achieve this goal?</strong></p>
<p>Working in the company, I learned the hard way that this is impossible. We had a lot of problems:</p>
<ul>
<li>We did not have any requirements, but at the same time the program had to work without interruptions.</li>
<li>We did not have enough testers, but we had to have time to check all-excel: the CMS itself, the work of its modules and shared sources, the work of various add-ons and extensions. All this had to work on different versions of the operating system, with 4 different database providers, in several browsers and we only had 3 people for this.</li>
<li>We had to make sure that clients could use our libraries. That is, all open code could be used in any way without problems.</li>
<li>Sometimes, when we (the testers) had questions about why it works like this and not otherwise, developers would answer: “Because it is written in the code”.</li>
<li>The support team complained that customers have many problems and questions after each release.</li>
</ul>
<p>And this is just a small list of the problems we encountered. We hired a few more testers, but it did not save the day much.</p>
<p>The CEO was unhappy and had every right to do so.</p>
<p>My team was doing all the best to improve:</p>
<ul>
<li>We had several hours per week for self-education. We learnt new technology and tool and shared knowledge between each other's.</li>
<li>We tested our applications from different sides (functionality, performance, security, usability etc.). We had separate checklists and test suites for each type of testing.</li>
<li>We tried to automate as much as possible (on the UI level and lower).</li>
<li>We created own knowledge base and any newcomer (not only tester) could use it if he was faced with some installation or environment problems for example.</li>
<li>Thanks to review procedure we became guru in testing documentation.</li>
<li>Our estimations were as accurate as possible because we were gathering corresponding metrics.</li>
<li>We used different templates for reports and they were useful and informative for all concerned.</li>
</ul>
<p>Despite all this we were still at the end of the conveyor.</p>
<p>CEO of the company understood that it is not enough to hire a good tester, you need to be able to give him the opportunity to do this job well and once, during one of the meetings, he reminded me of how I answered the question about the fire extinguisher and suggested setting up a process not only in my team, but throughout the company.</p>
<p>So, he wanted me to tell developers how to write code, the Support Team how to communicate with customers, and the Sales Team how to sell. It was an overwhelming task for me. But this was not necessary.</p>
<p>The company had excellent specialists who knew their business and really wanted to do it better, faster, and more qualitative.</p>
<p>As a result, we had standards and approaches, guided by which we were going to work in the future:</p>
<ul>
<li>In a few words, a coding guidelines and a code review process appeared in the company.</li>
<li>Our developers created recommendation on how to test security of our products.</li>
<li>When new versions of the operation system were released, we certificated our products to let our clients more evidence of good work of them.</li>
<li>Our support team developed new rules of their work process, so that our clients were able to receive hot-fix in 8 hours (not more).</li>
</ul>
<p>I have not worked in this company for many years, but I always remember those times with warmth.</p>
<p><strong>The Fire Extinguisher has become a symbol of excellent work for me.</strong></p>
<p>From that time I always ask this question at interviews, as well as to my students who come to me for "QA Engineer" courses.</p>
<p>I got the most varied answers, but I only hired those who did not blurt out instantly:</p>
<p>"Break the seal, pull out the pin, press the lever."</p>
<p>Every experienced tester knows to think first. And when you think about it, it becomes clear that even if the fire extinguisher works correctly, it will no longer be possible to give it to the client and say:</p>
<p>"Yes, it's a good fire extinguisher. In a critical situation, it will help you."</p>
<p>And it is not good idea to tell him:</p>
<p>"We tested functionality only. Security and performance testing is not our responsibility."</p>
<p>Of course, not all programs need to be tested like fire extinguishers. There are such types of programs, for the testing of which it is imperative to apply formal approaches, carefully plan and document everything (programs for medicine or banking software). But there are programs for which this is not necessary.</p>
<p>How to decide how thoroughly a program should be tested?</p>
<p>Experience tells me that even the simplest handbook-program can eventually become part of a huge system that will be used on board an aircraft. If the housewife does not find how many minutes to cook potatoes, this is not a problem. But what if the pilot of a supersonic fighter cannot find ejection instructions and as a result forgets to put his feet on the seat footrests?</p>
<p><strong>Each tester decides for himself how thoroughly the program should be tested, but he should always be guided by professional ethics, and not by the mood or limits of the project.</strong></p>
<p>I would really like to test my programs in the same way as those people who produce fire extinguishers. So that about the programs that my company produces, people would say the same as my dad:</p>
<p><strong>"I just know, that's all."</strong></p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/jHqAIuAZPU0">Matt Chesin</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to use Firebase Cloud functions to send push notifications]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2021/02/09/Use-Firebase-Cloudfunctions-To-Send-Push-Notifications</link>
            <guid>https://engineering.monstar-lab.com/en/post/2021/02/09/Use-Firebase-Cloudfunctions-To-Send-Push-Notifications</guid>
            <pubDate>Tue, 09 Feb 2021 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>This article provides a quick introduction on how to use <a href="https://firebase.google.com/docs/functions">Firebase Cloud Functions</a> to send push notifications. There are 2 main advantages in using them:</p>
<ol>
<li>A small amount of code can send iOS and Android push notifications at the same time.</li>
<li>There is no need for server-side coding as it is a server-less solution.</li>
</ol>
<p>Before starting, we need to complete the configuration of the development environment:</p>
<h3>1. Create the Firebase project</h3>
<p>In the Firebase console, click <a href="https://firebase.google.com/docs/functions/get-started?authuser=0#create-a-firebase-project">Add project</a> and select or enter a Project name (for example: <code>firebase-cloudfunction-demo</code>).</p>
<h3>2. Add the Cloud messaging config</h3>
<p>Complete the configuration of Firebase Cloud messaging:</p>
<ul>
<li><a href="https://firebase.google.com/docs/cloud-messaging/ios/client?authuser=0">iOS instructions</a></li>
<li><a href="https://firebase.google.com/docs/cloud-messaging/android/client?authuser=0">Android instructions</a></li>
</ul>
<p>When the configuration is done, we also need to save the <code>FCMToken</code> (Firebase Cloud Messaging) in the realtime database or Firestore, to be used for push notifications.</p>
<h3>3. Set up Node.js and the Firebase CLI</h3>
<p>Open your own command line and create a new folder, then use:</p>
<pre><code class="hljs language-sql">npm install firebase<span class="hljs-operator">-</span>functions<span class="hljs-variable">@latest</span> firebase<span class="hljs-operator">-</span>admin<span class="hljs-variable">@latest</span> <span class="hljs-comment">--save</span>
npm install <span class="hljs-operator">-</span>g firebase<span class="hljs-operator">-</span>tools
</code></pre>
<h3>4. Initialize your project</h3>
<p>Run the following command:</p>
<pre><code class="hljs">firebase login
</code></pre>
<p>Your browser will open and you can use your Firebase account login and wait for the redirect. Then you will be able to see this:</p>
<pre><code class="hljs language-rust">Waiting <span class="hljs-keyword">for</span> <span class="hljs-title class_">authentication</span>...
✔  Success! Logged <span class="hljs-keyword">in</span> <span class="hljs-keyword">as</span> *******
</code></pre>
<p>Run the following command:</p>
<pre><code class="hljs language-bash">firebase init <span class="hljs-built_in">functions</span>
</code></pre>
<p>Select JavaScript and when the Cloud functions setup is finished, this is how your folder will look like:</p>
<pre><code class="hljs language-bash">+- .firebaserc
 +- firebase.json
 +- <span class="hljs-built_in">functions</span>/
      +- package.json
      +- index.js
      +- node_modules/
</code></pre>
<h3>5. Add Cloud functions for push notifications</h3>
<p>We only need to edit the <code>functions/index.js</code> file.</p>
<p>There are two ways to send push notifications, through HTTP request or by adding a listener to the realtime database or Firestore.</p>
<pre><code class="hljs language-ini">const <span class="hljs-attr">functions</span> = require(<span class="hljs-string">'firebase-functions'</span>)<span class="hljs-comment">;</span>
const <span class="hljs-attr">admin</span> = require(<span class="hljs-string">'firebase-admin'</span>)<span class="hljs-comment">;</span>
admin.initializeApp()<span class="hljs-comment">;</span>

//http request method
<span class="hljs-attr">exports.sendHttpPushNotification</span> = functions.https.<span class="hljs-literal">on</span>Request((req, res) => {
    const <span class="hljs-attr">userId</span> = req.body.userId<span class="hljs-comment">; //get params like this</span>
    	...

//listener method
<span class="hljs-attr">exports.sendListenerPushNotification</span> = functions.database.ref(<span class="hljs-string">'/sendMessage/{userId}/'</span>).<span class="hljs-literal">on</span>Write((data, context) => {
	const <span class="hljs-attr">userId</span> = context.params.userId<span class="hljs-comment">; //get params like this</span>
		...
</code></pre>
<p>Assume that the <code>FCMToken</code> is stored in this location of the realtime database：</p>
<pre><code class="hljs language-bash">/FCMToken/<span class="hljs-variable">${userId}</span>
</code></pre>
<p>You could get the <code>FCMToken</code> like this:</p>
<pre><code class="hljs language-ini">const <span class="hljs-attr">FCMToken</span> = admin.database().ref(`/FCMTokens/<span class="hljs-variable">${userId}</span>`).<span class="hljs-literal">on</span>ce(<span class="hljs-string">'value'</span>)<span class="hljs-comment">;</span>
</code></pre>
<p>To send a notification, use the following code:</p>
<pre><code class="hljs language-scss">const payload = {
	token: FCMToken,
    notification: {
        title: <span class="hljs-string">'cloud function demo'</span>,
        body: message
    },
    data: {
        <span class="hljs-selector-tag">body</span>: message,
    }
};

admin<span class="hljs-selector-class">.messaging</span>()<span class="hljs-selector-class">.send</span>(payload)<span class="hljs-selector-class">.then</span>((response) => {
    <span class="hljs-comment">// Response is a message ID string.</span>
    console<span class="hljs-selector-class">.log</span>('Successfully sent message:', response);
    return {success: true};
})<span class="hljs-selector-class">.catch</span>((error) => {
    return {error: error.code};
});
</code></pre>
<h3>6. Deploy the functions to a production environment</h3>
<p>Run this command to deploy your functions:</p>
<pre><code class="hljs language-bash">firebase deploy --only <span class="hljs-built_in">functions</span>
</code></pre>
<p>If you add HTTP request functions, a function URL you should see:</p>
<pre><code class="hljs language-java">Function <span class="hljs-title function_">URL</span> <span class="hljs-params">(sendHttpPushNotification)</span>: https:<span class="hljs-comment">//us-central1-firebase-cloudfunction-demo.cloudfunctions.net/sendHttpPushNotification</span>
</code></pre>
<p>If you want to debug your cloud functions, you could use this command:</p>
<pre><code class="hljs language-sql">firebase emulators:<span class="hljs-keyword">start</span>
</code></pre>
<p>When the Cloud functions are deployed, you can send push notifications by making a request to the Function URL, or just set the data into the Firebase realtime database and the Cloud functions will be triggered by the data listener.</p>
<h3>Resources</h3>
<ul>
<li><a href="https://firebase.google.com/docs/functions">Cloud Functions for Firebase</a></li>
<li><a href="https://firebase.google.com/docs/cloud-messaging">Firebase Cloud Messaging</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Using JWT in Laravel]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2021/01/08/laravel-jwt</link>
            <guid>https://engineering.monstar-lab.com/en/post/2021/01/08/laravel-jwt</guid>
            <pubDate>Fri, 08 Jan 2021 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Json web token <a href="https://jwt.io/introduction/">JWT</a>, it is a JSON-based open standard that is implemented to transfer statements between web application environment(RFC 7519).The token is designed to be compact and safe, JWT statements are generally used to pass authenticated user identity information between identity providers and service providers, In order to obtain resources from the resource server, you can also add some additional declaration information necessary for other business logic.The token can also be used directly for authentication or encrypted.</p>
<h3>Laravel-JWT Installation and basic configuration</h3>
<ul>
<li>Install using composer</li>
</ul>
<pre><code class="hljs language-perl">composer <span class="hljs-keyword">require</span> tymon/jwt-auth <span class="hljs-number">1</span>.*@rc
</code></pre>
<ul>
<li>Publish configuration file</li>
</ul>
<pre><code class="hljs language-ini"><span class="hljs-comment"># This command will add a jwt.php configuration file under config</span>
php artisan vendor:publish <span class="hljs-attr">--provider</span>=<span class="hljs-string">"Tymon\JWTAuth\Providers\LaravelServiceProvider"</span>
</code></pre>
<ul>
<li>Generate encryption key</li>
</ul>
<pre><code class="hljs language-bash"><span class="hljs-comment"># This command will generate an encryption key in the .env file, such as:JWT_SECRET=jwt</span>
php artisan jwt:secret
</code></pre>
<ul>
<li>Update your model</li>
</ul>
<p>If you use the default User table to generate tokens, you need to add a piece of code under the model</p>
<pre><code class="hljs language-php"><span class="hljs-meta">&#x3C;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title class_">App</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Tymon</span>\<span class="hljs-title">JWTAuth</span>\<span class="hljs-title">Contracts</span>\<span class="hljs-title">JWTSubject</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Notifications</span>\<span class="hljs-title">Notifiable</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Foundation</span>\<span class="hljs-title">Auth</span>\<span class="hljs-title">User</span> <span class="hljs-keyword">as</span> <span class="hljs-title">Authenticatable</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Authenticatable</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">JWTSubject</span>
</span>{
    <span class="hljs-keyword">use</span> <span class="hljs-title">Notifiable</span>;

    <span class="hljs-comment">// Rest omitted for brevity</span>

    <span class="hljs-comment">/**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * <span class="hljs-doctag">@return</span> mixed
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getJWTIdentifier</span>(<span class="hljs-params"></span>)
    </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-variable language_">$this</span>-><span class="hljs-title function_ invoke__">getKey</span>();
    }

    <span class="hljs-comment">/**
     * Return a key value array, containing any custom claims to be added to the JWT.
     *
     * <span class="hljs-doctag">@return</span> array
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getJWTCustomClaims</span>(<span class="hljs-params"></span>)
    </span>{
        <span class="hljs-keyword">return</span> [];
    }
}

</code></pre>
<ul>
<li>Register two Facade</li>
</ul>
<p><strong>config/app.php</strong></p>
<p>These two facades are not necessary, but using them will bring a little convenience to your code writing.</p>
<pre><code class="hljs language-dart"><span class="hljs-string">'aliases'</span> => [
        ...
        <span class="hljs-comment">// Add the following two lines</span>
        <span class="hljs-string">'JWTAuth'</span> => <span class="hljs-string">'Tymon\JWTAuth\Facades\JWTAuth'</span>,
        <span class="hljs-string">'JWTFactory'</span> => <span class="hljs-string">'Tymon\JWTAuth\Facades\JWTFactory'</span>,
],
</code></pre>
<p><strong>If you don’t use these two Facades, you can use the helper function auth()</strong></p>
<p>auth() is an auxiliary function that returns a guard, which can be regarded as an Auth Facade for the time being.</p>
<pre><code class="hljs language-scss"><span class="hljs-comment">// If you don’t use Facade, you can write like this</span>
<span class="hljs-built_in">auth</span>('api')-><span class="hljs-built_in">refresh</span>();
<span class="hljs-comment">// Use JWTAuth Facade</span>
JWTAuth::parseToken()->refresh();
</code></pre>
<ul>
<li>Modify auth.php</li>
</ul>
<p><strong>config/auth.php</strong></p>
<p>These two facades are not necessary, but using them will bring a little convenience to your code writing.</p>
<pre><code class="hljs language-dart"><span class="hljs-string">'guards'</span> => [
    <span class="hljs-string">'web'</span> => [
        <span class="hljs-string">'driver'</span> => <span class="hljs-string">'session'</span>,
        <span class="hljs-string">'provider'</span> => <span class="hljs-string">'users'</span>,
    ],

    <span class="hljs-string">'api'</span> => [
        <span class="hljs-string">'driver'</span> => <span class="hljs-string">'jwt'</span>,        <span class="hljs-comment">// change token to jwt</span>
        <span class="hljs-string">'provider'</span> => <span class="hljs-string">'users'</span>,
    ],
],
</code></pre>
<ul>
<li>Register some routes</li>
</ul>
<p>These two facades are not necessary, but using them will bring a little convenience to your code writing.</p>
<pre><code class="hljs language-css">Route::<span class="hljs-built_in">group</span>([

    <span class="hljs-string">'prefix'</span> => <span class="hljs-string">'auth'</span>

], function ($router) {

    Route::<span class="hljs-built_in">post</span>(<span class="hljs-string">'login'</span>, <span class="hljs-string">'AuthController@login'</span>);
    Route::<span class="hljs-built_in">post</span>(<span class="hljs-string">'logout'</span>, <span class="hljs-string">'AuthController@logout'</span>);
    Route::<span class="hljs-built_in">post</span>(<span class="hljs-string">'refresh'</span>, <span class="hljs-string">'AuthController@refresh'</span>);
    Route::<span class="hljs-built_in">post</span>(<span class="hljs-string">'me'</span>, <span class="hljs-string">'AuthController@me'</span>);

});
</code></pre>
<ul>
<li>Create token controller</li>
</ul>
<pre><code class="hljs language-go">php artisan <span class="hljs-built_in">make</span>:controller AuthController
</code></pre>
<p><strong>AuthController</strong></p>
<pre><code class="hljs language-php"><span class="hljs-meta">&#x3C;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title class_">App</span>\<span class="hljs-title class_">Http</span>\<span class="hljs-title class_">Controllers</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Auth</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Controllers</span>\<span class="hljs-title">Controller</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AuthController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Controller</span>
</span>{
    <span class="hljs-comment">/**
     * Create a new AuthController instance.
     * Email and password are required (data source users table)
     *
     * <span class="hljs-doctag">@return</span> void
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"></span>)
    </span>{
        <span class="hljs-variable language_">$this</span>-><span class="hljs-title function_ invoke__">middleware</span>(<span class="hljs-string">'auth:api'</span>, [<span class="hljs-string">'except'</span> => [<span class="hljs-string">'login'</span>]]);
    }

    <span class="hljs-comment">/**
     * Get a JWT via given credentials.
     *
     * <span class="hljs-doctag">@return</span> \Illuminate\Http\JsonResponse
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">login</span>(<span class="hljs-params"></span>)
    </span>{
        <span class="hljs-variable">$credentials</span> = <span class="hljs-title function_ invoke__">request</span>([<span class="hljs-string">'email'</span>, <span class="hljs-string">'password'</span>]);

        <span class="hljs-keyword">if</span> (! <span class="hljs-variable">$token</span> = <span class="hljs-title function_ invoke__">auth</span>(<span class="hljs-string">'api'</span>)-><span class="hljs-title function_ invoke__">attempt</span>(<span class="hljs-variable">$credentials</span>)) {
            <span class="hljs-keyword">return</span> <span class="hljs-title function_ invoke__">response</span>()-><span class="hljs-title function_ invoke__">json</span>([<span class="hljs-string">'error'</span> => <span class="hljs-string">'Unauthorized'</span>], <span class="hljs-number">401</span>);
        }

        <span class="hljs-keyword">return</span> <span class="hljs-variable language_">$this</span>-><span class="hljs-title function_ invoke__">respondWithToken</span>(<span class="hljs-variable">$token</span>);
    }

    <span class="hljs-comment">/**
     * Get the authenticated User.
     *
     * <span class="hljs-doctag">@return</span> \Illuminate\Http\JsonResponse
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">me</span>(<span class="hljs-params"></span>)
    </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-title function_ invoke__">response</span>()-><span class="hljs-title function_ invoke__">json</span>(<span class="hljs-title function_ invoke__">auth</span>(<span class="hljs-string">'api'</span>)-><span class="hljs-title function_ invoke__">user</span>());
    }

    <span class="hljs-comment">/**
     * Log the user out (Invalidate the token).
     *
     * <span class="hljs-doctag">@return</span> \Illuminate\Http\JsonResponse
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">logout</span>(<span class="hljs-params"></span>)
    </span>{
        <span class="hljs-title function_ invoke__">auth</span>(<span class="hljs-string">'api'</span>)-><span class="hljs-title function_ invoke__">logout</span>();

        <span class="hljs-keyword">return</span> <span class="hljs-title function_ invoke__">response</span>()-><span class="hljs-title function_ invoke__">json</span>([<span class="hljs-string">'message'</span> => <span class="hljs-string">'Successfully logged out'</span>]);
    }

    <span class="hljs-comment">/**
     * Refresh a token.
     * <span class="hljs-doctag">@return</span> \Illuminate\Http\JsonResponse
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">refresh</span>(<span class="hljs-params"></span>)
    </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-variable language_">$this</span>-><span class="hljs-title function_ invoke__">respondWithToken</span>(<span class="hljs-title function_ invoke__">auth</span>(<span class="hljs-string">'api'</span>)-><span class="hljs-title function_ invoke__">refresh</span>());
    }

    <span class="hljs-comment">/**
     * Get the token array structure.
     *
     * <span class="hljs-doctag">@param</span>  string $token
     *
     * <span class="hljs-doctag">@return</span> \Illuminate\Http\JsonResponse
     */</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">respondWithToken</span>(<span class="hljs-params"><span class="hljs-variable">$token</span></span>)
    </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-title function_ invoke__">response</span>()-><span class="hljs-title function_ invoke__">json</span>([
            <span class="hljs-string">'access_token'</span> => <span class="hljs-variable">$token</span>,
            <span class="hljs-string">'token_type'</span> => <span class="hljs-string">'bearer'</span>,
            <span class="hljs-string">'expires_in'</span> => <span class="hljs-title function_ invoke__">auth</span>(<span class="hljs-string">'api'</span>)-><span class="hljs-title function_ invoke__">factory</span>()-><span class="hljs-title function_ invoke__">getTTL</span>() * <span class="hljs-number">60</span>
        ]);
    }
}
</code></pre>
<p><em>Article Photo by <a href="https://unsplash.com/photos/0jKuUj3vUTg">Dillon Shook</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Four tools to improve the efficiency of Flutter development]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/12/08/Four-tools-to-improve-the-efficiency-of-Flutter-development</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/12/08/Four-tools-to-improve-the-efficiency-of-Flutter-development</guid>
            <pubDate>Tue, 08 Dec 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>This article introduces 4 tools that can greatly improve the efficiency of Flutter development.</p>
<h2>1. Powerful logging package</h2>
<p>Printing logs during <code>Flutter</code> development is one of the commonly used debugging methods, but the log printing built in <code>Flutter</code> is very simple. Here we recommend a powerful package: <code>Logger</code>.</p>
<p><strong><code>Logger</code></strong> is an easy to use and extensible logger, it can print beautiful logs and can divide the log into different levels.</p>
<pre><code class="hljs language-dart">    <span class="hljs-keyword">var</span> logger = Logger();
    logger.v(<span class="hljs-string">"Verbose log"</span>);
    logger.d(<span class="hljs-string">"Debug log"</span>);
    logger.i(<span class="hljs-string">"Info log"</span>);
    logger.w(<span class="hljs-string">"Warning log"</span>);
    logger.e(<span class="hljs-string">"Error log"</span>);
    logger.wtf(<span class="hljs-string">"What a terrible failure log"</span>);
</code></pre>
<p><img src="/assets/img/articles/2020-12-08-Four-tools-to-improve-the-efficiency-of-Flutter-development/1-1.webp" alt=""></p>
<p><a href="https://pub.dev/packages/logger">Logger Pub</a></p>
<h2>2. Convert JSON data to model</h2>
<p>The data returned from a network request is usually in <code>JSON</code> format, so it is particularly important to convert the <code>JSON</code> format to <code>model</code>.</p>
<p>Assume the <code>JSON</code> string as follows:</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
  <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"monstar"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"age"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">14</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"email"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"monstar@example.com"</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<p>Define its corresponding <code>Model</code> class: <code>User</code>:</p>
<pre><code class="hljs language-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> name;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> age;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> email;

  User({<span class="hljs-keyword">this</span>.name, <span class="hljs-keyword">this</span>.age, <span class="hljs-keyword">this</span>.email});

  <span class="hljs-meta">@override</span>
  <span class="hljs-built_in">String</span> toString() {
    <span class="hljs-keyword">return</span> <span class="hljs-string">'name:<span class="hljs-subst">$name</span>,age:<span class="hljs-subst">$age</span>,email:<span class="hljs-subst">$email</span>'</span>;
  }
}
</code></pre>
<p>For parsing:</p>
<pre><code class="hljs language-dart"><span class="hljs-built_in">String</span> jsonStr = <span class="hljs-string">"{\"name\":\"monstar\",\"age\":14,\"email\":\"monstar@example.com\"}"</span>;
    <span class="hljs-keyword">var</span> jsonMap = json.decode(jsonStr);
    <span class="hljs-keyword">var</span> user =
        User(name: jsonMap[<span class="hljs-string">'name'</span>], age: jsonMap[<span class="hljs-string">'age'</span>], email: jsonMap[<span class="hljs-string">'email'</span>]);
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'<span class="hljs-subst">$user</span>'</span>);
</code></pre>
<p>Console output:</p>
<pre><code class="hljs language-java">flutter: name:monstar,age:<span class="hljs-number">14</span>,email:monstar<span class="hljs-meta">@example</span>.com
</code></pre>
<p>In the case above, there are only three fields in <code>JSON</code>. There are often many fields in real life use cases, and writing code by hand is not only error-prone, but also extremely inefficient.</p>
<p>For this situation, <code>Google</code> offers a solution: <strong><code>json_serializable</code></strong>.</p>
<p>But it is cumbersome by this way (<a href="https://pub.dev/packages/json_serializable">json_serializable Pub</a>), we do not recommend it. Instead, we recommend a simpler plugin named <strong><code>JsonToDart</code></strong>.</p>
<p>In <code>Android Studio</code> install <code>JsonToDart</code>, select <code>Preferences (Mac)</code> or <code>Setting (Windows)</code>, then select <code>Plugins</code> and search for <code>JsonToDart</code>.</p>
<p><img src="/assets/img/articles/2020-12-08-Four-tools-to-improve-the-efficiency-of-Flutter-development/2-1.webp" alt=""></p>
<p>Click <code>Install</code> and after the installation is completed, restart Android Studio. If the search fails, you can download it from the <a href="https://plugins.jetbrains.com/plugin/12562-jsontodart-json-to-dart-">official website</a> and install it.</p>
<p>Once installed, select the directory, right-click and select <code>New->Json to Dart</code>.</p>
<p><img src="/assets/img/articles/2020-12-08-Four-tools-to-improve-the-efficiency-of-Flutter-development/2-2.webp" alt=""></p>
<p>Then the <code>JSON</code> input screen appears:</p>
<p><img src="/assets/img/articles/2020-12-08-Four-tools-to-improve-the-efficiency-of-Flutter-development/2-3.webp" alt=""></p>
<p>In this screen input the <code>JSON</code> content and file name, click the <strong><code>Generate</code></strong> button to generate a file named <code>user.dart</code>.</p>
<p><img src="/assets/img/articles/2020-12-08-Four-tools-to-improve-the-efficiency-of-Flutter-development/2-4.webp" alt=""></p>
<p><img src="/assets/img/articles/2020-12-08-Four-tools-to-improve-the-efficiency-of-Flutter-development/2-1.webp" alt=""></p>
<h2>3. View UI effects on phones with different resolutions</h2>
<p>The biggest advantage of <code>Flutter</code> development is that it is cross-platform.</p>
<p>When the development is complete, you want to see the effects on different resolutions of the phone. Should you buy one for each phone to test?</p>
<p>No, using <strong><code>device_preview</code></strong> allows you to view the interface of different resolution devices on a single device.</p>
<h4>How to use</h4>
<p>First you need to import the dependent library into the project, open the file: <code>pubspec.yaml</code>, and add the reference library:</p>
<pre><code class="hljs language-yaml"><span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">device_preview:</span> <span class="hljs-string">^0.5.4</span>
</code></pre>
<p>Then execute the following command from the command line:</p>
<pre><code class="hljs language-bash">$ flutter pub get
</code></pre>
<p>Finally here is a sample code on how to use it:</p>
<pre><code class="hljs language-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:device_preview/device_preview.dart'</span>;

<span class="hljs-keyword">void</span> main() => runApp(
  DevicePreview(
    enabled: !kReleaseMode,
    builder: (context) => MyApp(), <span class="hljs-comment">// Wrap your app</span>
  ),
);

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> MaterialApp(
      locale: DevicePreview.locale(context), <span class="hljs-comment">// Add the locale here</span>
      builder: DevicePreview.appBuilder, <span class="hljs-comment">// Add the builder here</span>
      home: HomePage(),
    );
  }
}
</code></pre>
<h4>Demo</h4>
<p><img src="/assets/img/articles/2020-12-08-Four-tools-to-improve-the-efficiency-of-Flutter-development/3-1.webp" alt=""></p>
<ul>
<li>Interface effect display is supported under different devices (iPhone, iPad, Mac, Windows, Linux).</li>
</ul>
<p><img src="/assets/img/articles/2020-12-08-Four-tools-to-improve-the-efficiency-of-Flutter-development/3-1.webp" alt=""></p>
<ul>
<li>Dynamic system configuration: language, dark mode, text scaling.</li>
</ul>
<p><img src="/assets/img/articles/2020-12-08-Four-tools-to-improve-the-efficiency-of-Flutter-development/3-2.webp" alt=""></p>
<p><img src="/assets/img/articles/2020-12-08-Four-tools-to-improve-the-efficiency-of-Flutter-development/3-5.webp" alt=""></p>
<ul>
<li>Change device direction.</li>
</ul>
<p><img src="/assets/img/articles/2020-12-08-Four-tools-to-improve-the-efficiency-of-Flutter-development/3-3.webp" alt=""></p>
<ul>
<li>Virtual keyboard pop-up effect.</li>
</ul>
<p><img src="/assets/img/articles/2020-12-08-Four-tools-to-improve-the-efficiency-of-Flutter-development/3-4.webp" alt=""></p>
<ul>
<li>Screenshots.</li>
</ul>
<p><a href="https://pub.dev/packages/device_preview">Device_preview Pub</a></p>
<h2>4. Localization package</h2>
<p><strong><code>Intl</code></strong> can localize your <code>Flutter</code> project quickly.</p>
<h3>How to use</h3>
<p>If you use <code>Android Studio</code>, you only need to install this plugin: <code>Flutter Intl</code>.</p>
<p><img src="/assets/img/articles/2020-12-08-Four-tools-to-improve-the-efficiency-of-Flutter-development/4-1.webp" alt=""></p>
<p>For <code>Visual Studio Code</code> there is also a corresponding plugin: <code>Flutter Intl</code>.</p>
<p><img src="/assets/img/articles/2020-12-08-Four-tools-to-improve-the-efficiency-of-Flutter-development/4-2.webp" alt=""></p>
<p>After installing the plugin, you need to add the dependency library to the <code>pubspec.yaml</code> file：</p>
<pre><code class="hljs language-yaml"><span class="hljs-attr">dev_dependencies:</span>
    <span class="hljs-string">...</span>
  <span class="hljs-attr">flutter_localizations:</span>
    <span class="hljs-attr">sdk:</span> <span class="hljs-string">flutter</span>
</code></pre>
<p>Then execute the command:</p>
<pre><code class="hljs language-bash">$ flutter pub get
</code></pre>
<p>Using <code>Android Studio</code> as an example, open <code>Tool->Flutter Intl -> Initalize for the project</code> to add localization support for the project. The plugin automatically alters the file <code>pubspec.yaml</code>, and generates <strong>generated</strong> and <strong>l10n</strong> two file directories under <code>lib</code>.</p>
<ul>
<li>Directory <strong>intl</strong> under <strong>generated</strong> generates <code>messages_all.dart</code> and <code>messages_en.dart</code> by default. The file at the beginning of <strong>messages_xxx.dart</strong> does not require manual modification and is automatically generated.</li>
<li>Directory <strong>I10n.dart</strong> under <strong>generated</strong> is implementation of <code>Localizations</code> and <code>Delegate</code>, does not require manual modification and is automatically generated.</li>
<li><strong>intl_en.arb</strong> exists under <strong>l10n</strong>, the text is stored here。</li>
</ul>
<p>If you want to add a language，open <code>Tool->Flutter Intl -> Add Locale</code>, input the language abbreviation to be supported, and the plugin will automatically generate the relevant file, as shown in the screenshot below, adding support for Chinese：</p>
<p><img src="/assets/img/articles/2020-12-08-Four-tools-to-improve-the-efficiency-of-Flutter-development/4-3.webp" alt=""></p>
<p>With localization, you need to add package dependencies in the file <code>pubspec.yaml</code>:</p>
<pre><code class="hljs language-yaml"><span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">flutter:</span>
    <span class="hljs-attr">sdk:</span> <span class="hljs-string">flutter</span>
  <span class="hljs-attr">flutter_localizations:</span>
    <span class="hljs-attr">sdk:</span> <span class="hljs-string">flutter</span>
</code></pre>
<p>Then execute the command:</p>
<pre><code class="hljs language-bash">$ flutter pub get
</code></pre>
<p>Modify <code>MaterialApp</code> as follows:</p>
<pre><code class="hljs language-dart">...
<span class="hljs-keyword">import</span> <span class="hljs-string">'generated/l10n.dart'</span>;

...
MaterialApp(
  ...
  localizationsDelegates: [
    S.delegate,
    GlobalMaterialLocalizations.delegate,
    GlobalWidgetsLocalizations.delegate,
    GlobalCupertinoLocalizations.delegate,
  ],
  supportedLocales: S.delegate.supportedLocales,
  ...
)
</code></pre>
<p>Add text under <strong>intl_en.arb</strong> and <strong>intl_zh.arb</strong>, for example: <code>cartoon_title</code></p>
<p>intl_en.arb</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
  <span class="hljs-attr">"cartoon_title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"cartoon"</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<p>intl_zh.arb</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
  <span class="hljs-attr">"cartoon_title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"动画"</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<p>Use <code>command + s (Mac)</code> to save, The associated files under the <strong>generated</strong> directory will be generated again.
Here is an example of how to use it:</p>
<pre><code class="hljs language-dart">    ...
    Text(<span class="hljs-string">'<span class="hljs-subst">${S.of(context).cartoon_title}</span>'</span>);
    ...
</code></pre>
<p>The effect looks like the following:</p>
<p><img src="/assets/img/articles/2020-12-08-Four-tools-to-improve-the-efficiency-of-Flutter-development/4-4.webp" alt=""></p>
<h2>Reference</h2>
<ul>
<li><a href="https://pub.dev/packages/logger">Logger Pub</a></li>
<li><a href="https://pub.dev/packages/json_serializable">JSON Serializable Pub</a></li>
<li><a href="https://plugins.jetbrains.com/plugin/12562-jsontodart-json-to-dart-">JSON to Dart Plugin</a></li>
<li><a href="https://pub.dev/packages/device_preview">Device preview Dart Pub</a></li>
<li><a href="https://plugins.jetbrains.com/plugin/13666-flutter-intl">Flutter Intl Plugin</a></li>
<li><a href="https://pub.dev/packages/intl">Intl Pub</a></li>
</ul>
<p><em><a href="https://proandroiddev.com/building-your-ios-android-app-in-30-minutes-using-flutter-2ec61b78a1e5">Source of the header image</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Spring Boot integrated RabbitMQ]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/11/30/springboot-integrated-rabbitmq</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/11/30/springboot-integrated-rabbitmq</guid>
            <pubDate>Mon, 30 Nov 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Recently I learned about micro services and here is how <a href="https://spring.io/projects/spring-boot">Spring Boot</a> integrates <a href="https://spring.io/guides/gs/messaging-rabbitmq/">RabbitMQ</a> to send messages to other micro services.</p>
<h2>Prerequisites</h2>
<ul>
<li><a href="https://www.jetbrains.com/idea/">IntelliJ IDEA</a></li>
<li><a href="https://spring.io/guides/gs/messaging-rabbitmq/">RabbitMQ</a></li>
</ul>
<h2>Getting started</h2>
<p>1.Start by creating an empty Spring Boot project and add the Maven dependency.</p>
<pre><code class="hljs language-xml"><span class="hljs-tag">&#x3C;<span class="hljs-name">dependency</span>></span>
  <span class="hljs-tag">&#x3C;<span class="hljs-name">groupId</span>></span>org.springframework.boot<span class="hljs-tag">&#x3C;/<span class="hljs-name">groupId</span>></span>
  <span class="hljs-tag">&#x3C;<span class="hljs-name">artifactId</span>></span>spring-boot-starter-amqp<span class="hljs-tag">&#x3C;/<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag">&#x3C;/<span class="hljs-name">dependency</span>></span>
</code></pre>
<p>2.Add the RabbitMQ server configuration information in <code>application.yml</code>.</p>
<pre><code class="hljs language-yaml"><span class="hljs-attr">spring:</span>
  <span class="hljs-attr">rabbitmq:</span>
    <span class="hljs-attr">host:</span> <span class="hljs-string">localhost</span>
    <span class="hljs-attr">port:</span> <span class="hljs-number">5672</span>
    <span class="hljs-attr">username:</span> <span class="hljs-string">guest</span>
    <span class="hljs-attr">password:</span> <span class="hljs-string">guest</span>
</code></pre>
<p>3.Create a new message consumer:</p>
<pre><code class="hljs language-java"><span class="hljs-keyword">package</span> com.example.springbootmq.message;

<span class="hljs-keyword">import</span> com.example.springbootmq.constant.QueueNameConstant;
<span class="hljs-keyword">import</span> lombok.extern.slf4j.Slf4j;
<span class="hljs-keyword">import</span> org.springframework.amqp.rabbit.annotation.Queue;
<span class="hljs-keyword">import</span> org.springframework.amqp.rabbit.annotation.RabbitListener;
<span class="hljs-keyword">import</span> org.springframework.stereotype.Component;

<span class="hljs-comment">/**
 * <span class="hljs-doctag">@author</span> donagh.wang
 * <span class="hljs-doctag">@version</span> 1.0.0
 * <span class="hljs-doctag">@date</span> 2020/11/4 15:40
 */</span>
<span class="hljs-meta">@Slf4j</span>
<span class="hljs-meta">@Component</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">MessageReceiver</span> {

    <span class="hljs-meta">@RabbitListener(queuesToDeclare = @Queue(QueueNameConstant.QUEUE_NAME))</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">process</span><span class="hljs-params">(String message)</span> {
        log.info(<span class="hljs-string">"The receiver receives the message: {}"</span>, message);
    }
}
</code></pre>
<p>This is a simple example, in actual development MQ also needs to be bound with exchange, routing-key, etc.</p>
<p>4.Create a test class to act as a message producer:</p>
<pre><code class="hljs language-java"><span class="hljs-keyword">package</span> com.example.springbootmq;

<span class="hljs-keyword">import</span> com.example.springbootmq.constant.QueueNameConstant;
<span class="hljs-keyword">import</span> lombok.extern.slf4j.Slf4j;
<span class="hljs-keyword">import</span> org.junit.jupiter.api.Test;
<span class="hljs-keyword">import</span> org.springframework.amqp.core.AmqpTemplate;
<span class="hljs-keyword">import</span> org.springframework.beans.factory.annotation.Autowired;

<span class="hljs-keyword">import</span> java.util.Date;

<span class="hljs-comment">/**
 * <span class="hljs-doctag">@author</span> donagh.wang
 * <span class="hljs-doctag">@version</span> 1.0.0
 * <span class="hljs-doctag">@date</span> 2020/11/4 15:53
 */</span>
<span class="hljs-meta">@Slf4j</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">RabbitMqTest</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">SpringbootMqApplicationTests</span> {

    <span class="hljs-meta">@Autowired</span>
    <span class="hljs-keyword">private</span> AmqpTemplate amqpTemplate;

    <span class="hljs-meta">@Test</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testSendMessage</span><span class="hljs-params">()</span> {
        <span class="hljs-type">String</span> <span class="hljs-variable">msg</span> <span class="hljs-operator">=</span> <span class="hljs-string">"now time:"</span> + <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>();
        log.info(<span class="hljs-string">"Send message 【{}】 to {} message queue."</span>, msg, QueueNameConstant.QUEUE_NAME);
        amqpTemplate.convertAndSend(QueueNameConstant.QUEUE_NAME, <span class="hljs-string">"now time:"</span> + <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>());
    }
}
</code></pre>
<p>5.Start the project (<code>message consumer</code>) first, and then execute the test class (<code>message producer</code>):</p>
<p><strong>Message producer output log:</strong></p>
<pre><code class="hljs language-yaml"><span class="hljs-attr">com.example.springbootmq.RabbitMqTest:</span> <span class="hljs-string">Send</span> <span class="hljs-string">message</span> <span class="hljs-string">【now</span> <span class="hljs-string">time:Wed</span> <span class="hljs-string">Nov</span> <span class="hljs-number">04</span> <span class="hljs-number">16</span><span class="hljs-string">:33:46</span> <span class="hljs-string">CST</span> <span class="hljs-number">2020</span><span class="hljs-string">】</span> <span class="hljs-string">to</span> <span class="hljs-string">test-mq</span> <span class="hljs-string">message</span> <span class="hljs-string">queue.</span>
</code></pre>
<p><strong>Message consumer output log:</strong></p>
<pre><code class="hljs language-yaml"><span class="hljs-attr">c.e.s.message.MessageReceiver: The receiver receives the message:</span> <span class="hljs-string">now</span> <span class="hljs-string">time:Wed</span> <span class="hljs-string">Nov</span> <span class="hljs-number">04</span> <span class="hljs-number">16</span><span class="hljs-string">:33:46</span> <span class="hljs-string">CST</span> <span class="hljs-number">2020</span>
</code></pre>
<p>This was a simple tutorial on Spring Boot integrated RabbitMQ. For more information, please check those <a href="https://www.rabbitmq.com/getstarted.html">RabbitMQ tutorials</a>.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/kUqqaRjJuw0">Burst on Unsplash</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[WidgetKit]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/11/16/WidgetKit</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/11/16/WidgetKit</guid>
            <pubDate>Mon, 16 Nov 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>In WWDC20, Apple introduced <strong>App Widgets</strong> to the home screen for multiple platforms(iOS, iPadOS, macOS). With <strong>WidgetKit</strong>, you can see the most important information from your app by putting widgets on your Home screen or Notification Center.</p>
<h3>Supported Families</h3>
<p>There are three different size styles available for widgets, <strong>Small</strong>, <strong>Medium</strong> and <strong>Large</strong>. By default, all size styles are enabled. You can configure the ones that works best for your app’s content.</p>
<h3>Configuration</h3>
<p>There are two types of configuration, <strong>StaticConfiguration</strong> and <strong>IntentConfiguration</strong>.</p>
<p><code>StaticConfiguration:</code> It is used for the widgets that do not provide user-configurable options. For example, a news widget that shows trending headlines.</p>
<p><code>IntentConfiguration:</code> It is used for the widgets that want to provide user-configurable options. For example, a stock quotes widget, which allows you to select a specific stock to show in the widget. Click <a href="https://developer.apple.com/documentation/widgetkit/making-a-configurable-widget">here</a> for more information.</p>
<p>We will focus on <strong>StaticConfiguration</strong> in this article.</p>
<h3>Requirements</h3>
<ul>
<li>Xcode 12.0.1(or later)</li>
<li>macOS Catalina 10.15.5 or later</li>
<li>Basic knowledge of SwiftUI</li>
</ul>
<h3>Create a simple app</h3>
<p>Let's first create a simple app that lists quotes along with the author name and on tap show the detailView.</p>
<p>Open Xcode and create iOS App project with name <strong>QuotesWidgetDemo</strong>. Make sure <strong>SwiftUI</strong> is selected in <code>Interface</code> option and <strong>SwiftUI App</strong> is selected in <code>Life cycle</code> option.</p>
<ul>
<li>Create <strong>Quote.swift</strong> file and add model</li>
</ul>
<pre><code class="hljs language-swift"><span class="hljs-keyword">public</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">Quote</span> {
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">let</span> quoteId: <span class="hljs-type">Int</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">let</span> author: <span class="hljs-type">String</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">let</span> quote: <span class="hljs-type">String</span>

    <span class="hljs-keyword">init</span>(<span class="hljs-params">quoteId</span>: <span class="hljs-type">Int</span>, <span class="hljs-params">author</span>: <span class="hljs-type">String</span>, <span class="hljs-params">quote</span>: <span class="hljs-type">String</span>) {
        <span class="hljs-keyword">self</span>.quoteId <span class="hljs-operator">=</span> quoteId
        <span class="hljs-keyword">self</span>.author <span class="hljs-operator">=</span> author
        <span class="hljs-keyword">self</span>.quote <span class="hljs-operator">=</span> quote
    }
}

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">Quote</span>: <span class="hljs-title class_">Identifiable</span> {
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> id: <span class="hljs-type">Int</span> {
        quoteId
    }
}
</code></pre>
<ul>
<li>Create <strong>QuoteProvider.swift</strong> file and add data</li>
</ul>
<pre><code class="hljs language-swift"><span class="hljs-keyword">public</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">QuoteProvider</span> {
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">all</span>() -> [<span class="hljs-type">Quote</span>] {
        <span class="hljs-keyword">return</span> [
            <span class="hljs-type">Quote</span>(quoteId: <span class="hljs-number">1</span>, author: <span class="hljs-string">"Steve Jobs"</span>, quote: <span class="hljs-string">"Have the courage to follow your heart and intuition. They already know what you truly want to become."</span>),
            <span class="hljs-type">Quote</span>(quoteId: <span class="hljs-number">2</span>, author: <span class="hljs-string">"Albert Einstein"</span>, quote: <span class="hljs-string">"Everybody is a genius. But if you judge a fish by its ability to climb a tree , it will live its whole life believing that it is stupid."</span>),
            <span class="hljs-type">Quote</span>(quoteId: <span class="hljs-number">3</span>, author: <span class="hljs-string">"J.K. Rowling"</span>, quote: <span class="hljs-string">"Imagination is the foundation of all invention and innovation."</span>),
            <span class="hljs-type">Quote</span>(quoteId: <span class="hljs-number">4</span>, author: <span class="hljs-string">"Abdul Kalam"</span>, quote: <span class="hljs-string">"Don't take rest after your first victory because if you fail in second, more lips are waiting to say that your first victory was just luck."</span>),
            <span class="hljs-type">Quote</span>(quoteId: <span class="hljs-number">5</span>, author: <span class="hljs-string">"Sandeep Maheshwari"</span>, quote: <span class="hljs-string">"Positive Thinking is not about expecting the best to happen. It's about accepting that whatever happens, happens for the best."</span>)
        ]
    }
}
</code></pre>
<ul>
<li>Rename existing file <code>ContentView.swift</code> and <code>struct ContentView</code> with <strong>QuoteView.swift</strong> and <strong>struct QuoteView</strong> and add the below code for quote listing and detailView</li>
</ul>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> SwiftUI

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">QuoteView</span>: <span class="hljs-title class_">View</span> {
    <span class="hljs-keyword">let</span> quotes: [<span class="hljs-type">Quote</span>] <span class="hljs-operator">=</span> <span class="hljs-type">QuoteProvider</span>.all()
    <span class="hljs-meta">@State</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> showQuote: <span class="hljs-type">Quote</span>?

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
        <span class="hljs-type">NavigationView</span> {
            <span class="hljs-type">List</span> {
                <span class="hljs-type">ForEach</span>(quotes) { quote <span class="hljs-keyword">in</span>
                    <span class="hljs-type">Button</span>(action: {
                        showQuote <span class="hljs-operator">=</span> quote
                    }, label: {
                        <span class="hljs-type">QuoteItemView</span>(author: quote.author, quote: quote.quote)
                    })
                    .sheet(item: <span class="hljs-variable">$showQuote</span>) { quote <span class="hljs-keyword">in</span>
                        <span class="hljs-type">QuoteDetailView</span>(quote: quote)
                    }
                }
            }
            .foregroundColor(.black)
            .listStyle(<span class="hljs-type">PlainListStyle</span>())
            .navigationBarTitle(<span class="hljs-string">"Quotes"</span>)
        }
    }
}

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">QuoteItemView</span>: <span class="hljs-title class_">View</span> {
    <span class="hljs-keyword">let</span> author: <span class="hljs-type">String</span>
    <span class="hljs-keyword">let</span> quote: <span class="hljs-type">String</span>

    <span class="hljs-keyword">var</span>  body: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
        <span class="hljs-type">VStack</span> {
            <span class="hljs-type">VStack</span>(alignment: .leading) {
                <span class="hljs-type">HStack</span> {
                    <span class="hljs-type">Text</span>(<span class="hljs-string">"<span class="hljs-subst">\(quote)</span>"</span>)
                        .font(.subheadline)
                        .bold()
                        .lineLimit(<span class="hljs-number">2</span>)
                }
                .padding([.leading, .trailing])
                <span class="hljs-type">Spacer</span>().frame(height: <span class="hljs-number">10.0</span>)
                <span class="hljs-type">Text</span>(<span class="hljs-string">"- <span class="hljs-subst">\(author)</span>"</span>)
                    .padding([.leading, .trailing, .bottom])
                    .font(.footnote)
            }
        }
        .foregroundColor(.black)
    }
}

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">QuoteDetailView</span>: <span class="hljs-title class_">View</span> {
    <span class="hljs-keyword">var</span> quote: <span class="hljs-type">Quote</span>

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
        <span class="hljs-type">ZStack</span> {
            <span class="hljs-type">Color</span>(<span class="hljs-type">UIColor</span>.systemBlue).edgesIgnoringSafeArea(.all)

            <span class="hljs-type">VStack</span> {
                <span class="hljs-type">VStack</span>(alignment: .leading) {
                    <span class="hljs-type">HStack</span> {
                        <span class="hljs-type">Text</span>(<span class="hljs-string">"<span class="hljs-subst">\(quote.quote)</span>"</span>)
                            .font(.title)
                            .bold()
                    }
                    .padding()
                    <span class="hljs-type">Text</span>(<span class="hljs-string">"- <span class="hljs-subst">\(quote.author)</span>"</span>)
                        .padding([.leading, .trailing, .bottom])
                        .font(.title3)
                }
            }
            .foregroundColor(.white)
        }
    }
}

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">QuoteView_Previews</span>: <span class="hljs-title class_">PreviewProvider</span> {
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> previews: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
        <span class="hljs-type">QuoteView</span>()
    }
}
</code></pre>
<p>We are done with the simple app design. If you run the app, it will look like this</p>
<p><img src="/assets/img/articles/2020-11-16-WidgetKit/quotes.webp" alt=""></p>
<h3>Widget Extension</h3>
<ul>
<li>To build a widget, you need to add a widget extension to your app. To add <strong>Widgit extension</strong>, go to File -> New -> Target -> select Widget extension(under iOS tab) -> click on Next -> set Product name to <strong>QuoteWidget</strong> and make sure <strong>Include Configuration Intent option</strong> is not selected -> once finish button tapped it will show Dialog, press Activate.</li>
</ul>
<p>Run the extension, by default it will look like this</p>
<p><img src="/assets/img/articles/2020-11-16-WidgetKit/default-widget-extension.webp" alt=""></p>
<ul>
<li>
<p>Enable Target <strong>QuoteWidgetExtention</strong> for our model <strong>Quote.swift</strong> and data provider <strong>QuoteProvider.swift</strong> file.</p>
</li>
<li>
<p>Add <strong>random()</strong> function to <strong>QuoteProvider.swift</strong> file to get quote randomly</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">public</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">QuoteProvider</span> {
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">all</span>() -> [<span class="hljs-type">Quote</span>] {
        <span class="hljs-comment">// ...</span>
    }

    <span class="hljs-keyword">static</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">random</span>() -> <span class="hljs-type">Quote</span> {
        <span class="hljs-keyword">let</span> allQuotes <span class="hljs-operator">=</span> <span class="hljs-type">QuoteProvider</span>.all()
        <span class="hljs-keyword">let</span> randomIndex <span class="hljs-operator">=</span> <span class="hljs-type">Int</span>.random(in: <span class="hljs-number">0</span><span class="hljs-operator">..&#x3C;</span>allQuotes.count)
        <span class="hljs-keyword">return</span> allQuotes[randomIndex]
    }
}
</code></pre>
</li>
<li>
<p>Lets design our custom widgetView. Add a swiftUI View file with name <strong>QuoteWidgetView</strong>. Make sure it is part of <strong>QuoteWidgetExtention</strong> target.</p>
</li>
</ul>
<p><img src="/assets/img/articles/2020-11-16-WidgetKit/widget-view.webp" alt=""></p>
<ul>
<li>
<p>Update the above added file(<strong>QuoteWidgetView.swift</strong>) with this code</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">QuoteWidgetView</span>: <span class="hljs-title class_">View</span> {
    <span class="hljs-keyword">let</span> quote: <span class="hljs-type">Quote</span>

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
        <span class="hljs-type">ZStack</span> {
            <span class="hljs-type">Color</span>(<span class="hljs-type">UIColor</span>.systemBlue)
            <span class="hljs-type">VStack</span> {
                <span class="hljs-type">Text</span>(quote.quote)
                    .font(.subheadline)
                    .lineLimit(<span class="hljs-number">2</span>)
                    .padding(.top, <span class="hljs-number">5</span>)
                    .padding([.leading, .trailing])
                    .foregroundColor(.white)
                <span class="hljs-type">Spacer</span>().frame(height: <span class="hljs-number">10.0</span>)
                <span class="hljs-type">Text</span>(<span class="hljs-string">"- <span class="hljs-subst">\(quote.author)</span>"</span>)
                    .font(.footnote)

            }
        }
    }
}

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">QuoteWidgetView_Previews</span>: <span class="hljs-title class_">PreviewProvider</span> {
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> previews: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
        <span class="hljs-type">QuoteWidgetView</span>(quote: <span class="hljs-type">QuoteProvider</span>.random())
    }
}
</code></pre>
<p><strong>Note:</strong> You can define different views for different style size as per your requirement. I've designed the above view for all supported families(Medium, Large).</p>
</li>
<li>
<p>Update <strong>QuoteWidget.swift</strong> file</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> WidgetKit
<span class="hljs-keyword">import</span> SwiftUI

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">Provider</span>: <span class="hljs-title class_">TimelineProvider</span> {
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">placeholder</span>(<span class="hljs-params">in</span> <span class="hljs-params">context</span>: <span class="hljs-type">Context</span>) -> <span class="hljs-type">SimpleEntry</span> {
        <span class="hljs-type">SimpleEntry</span>(date: <span class="hljs-type">Date</span>(), quote: <span class="hljs-type">QuoteProvider</span>.random())
    }

    <span class="hljs-keyword">func</span> <span class="hljs-title function_">getSnapshot</span>(<span class="hljs-params">in</span> <span class="hljs-params">context</span>: <span class="hljs-type">Context</span>, <span class="hljs-params">completion</span>: <span class="hljs-keyword">@escaping</span> (<span class="hljs-type">SimpleEntry</span>) -> ()) {
        <span class="hljs-keyword">let</span> entry <span class="hljs-operator">=</span> <span class="hljs-type">SimpleEntry</span>(date: <span class="hljs-type">Date</span>(), quote: <span class="hljs-type">QuoteProvider</span>.random())
        completion(entry)
    }

    <span class="hljs-keyword">func</span> <span class="hljs-title function_">getTimeline</span>(<span class="hljs-params">in</span> <span class="hljs-params">context</span>: <span class="hljs-type">Context</span>, <span class="hljs-params">completion</span>: <span class="hljs-keyword">@escaping</span> (<span class="hljs-type">Timeline</span>&#x3C;<span class="hljs-type">Entry</span>>) -> ()) {
        <span class="hljs-keyword">var</span> entries: [<span class="hljs-type">SimpleEntry</span>] <span class="hljs-operator">=</span> []

        <span class="hljs-comment">// Generate a timeline consisting of five entries an hour apart, starting from the current date.</span>
        <span class="hljs-keyword">let</span> currentDate <span class="hljs-operator">=</span> <span class="hljs-type">Date</span>()
        <span class="hljs-keyword">for</span> hourOffset <span class="hljs-keyword">in</span> <span class="hljs-number">0</span> <span class="hljs-operator">..&#x3C;</span> <span class="hljs-number">5</span> {
            <span class="hljs-keyword">let</span> entryDate <span class="hljs-operator">=</span> <span class="hljs-type">Calendar</span>.current.date(byAdding: .hour, value: hourOffset, to: currentDate)<span class="hljs-operator">!</span>
            <span class="hljs-keyword">let</span> entry <span class="hljs-operator">=</span> <span class="hljs-type">SimpleEntry</span>(date: entryDate, quote: <span class="hljs-type">QuoteProvider</span>.random())
            entries.append(entry)
        }

        <span class="hljs-keyword">let</span> timeline <span class="hljs-operator">=</span> <span class="hljs-type">Timeline</span>(entries: entries, policy: .atEnd)
        completion(timeline)
    }
}

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">SimpleEntry</span>: <span class="hljs-title class_">TimelineEntry</span> {
    <span class="hljs-keyword">let</span> date: <span class="hljs-type">Date</span>
    <span class="hljs-keyword">let</span> quote: <span class="hljs-type">Quote</span>
}

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">QuoteWidgetEntryView</span> : <span class="hljs-title class_">View</span> {
    <span class="hljs-keyword">var</span> entry: <span class="hljs-type">Provider</span>.<span class="hljs-type">Entry</span>

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
        <span class="hljs-type">QuoteWidgetView</span>(quote: entry.quote)
    }
}

<span class="hljs-keyword">@main</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">QuoteWidget</span>: <span class="hljs-title class_">Widget</span> {

<span class="hljs-comment">// string that identifies the widget</span>
    <span class="hljs-keyword">let</span> kind: <span class="hljs-type">String</span> <span class="hljs-operator">=</span> <span class="hljs-string">"QuoteWidget"</span>

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">WidgetConfiguration</span> {
        <span class="hljs-type">StaticConfiguration</span>(kind: kind, provider: <span class="hljs-type">Provider</span>()) { entry <span class="hljs-keyword">in</span>
            <span class="hljs-type">QuoteWidgetEntryView</span>(entry: entry)
        }
        .configurationDisplayName(<span class="hljs-string">"My Widget"</span>)
        .description(<span class="hljs-string">"This is an example widget."</span>)
        .supportedFamilies([.systemMedium, .systemLarge])
    }
}

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">QuoteWidget_Previews</span>: <span class="hljs-title class_">PreviewProvider</span> {
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> previews: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
        <span class="hljs-type">QuoteWidgetEntryView</span>(entry: <span class="hljs-type">SimpleEntry</span>(date: <span class="hljs-type">Date</span>(), quote: <span class="hljs-type">QuoteProvider</span>.random()))
            .previewContext(<span class="hljs-type">WidgetPreviewContext</span>(family: .systemSmall))
    }
}
</code></pre>
</li>
</ul>
<p>Let's understand the above code</p>
<p><strong>@main attribute:</strong></p>
<p>It indicates that the <strong>QuoteWidget</strong> is the entry point for the widget extension.</p>
<p><strong>Content closure:</strong></p>
<p>It contains SwiftUI views like <strong>QuoteWidgetEntryView</strong> in the above case. WidgetKit invokes this to render the widget’s content.</p>
<p><strong>TimelineEntry:</strong></p>
<p><em>"A type that specifies the date to display a widget, and, optionally, indicates the current relevance of the widget’s content." - Apple</em></p>
<ul>
<li>
<p><strong>SimpleEntry</strong> conforms to <strong>TimelineEntry</strong> protocol. It holds <strong>date</strong> for the <strong>TimelineProvider</strong> to provide to WidgetKit to update the widget. You can specify additional properties like we have added <strong>quote</strong> in the above code.</p>
</li>
<li>
<p>Inside <strong>QuoteWidgetEntryView</strong>, replaced default body <code>Text(entry.date, style: .time)</code> with our custom <strong>QuoteWidgetView</strong></p>
</li>
</ul>
<p><strong>TimelineProvider:</strong></p>
<p><em>"A type that advises WidgetKit when to update a widget’s display." - Apple</em></p>
<p><strong>Provider</strong> conforms to <strong>TimelineProvider</strong> protocol and it provides the following methods</p>
<ul>
<li>
<p><code>Placeholder:</code> It allows you to display a placeholder view to the user. It tells the WidgetKit what to render while the widget is loading.</p>
</li>
<li>
<p><code>getSnapshot:</code> WidgetKit makes the snapshot request when displaying the widget in transient situations, such as when you are adding a widget.</p>
</li>
<li>
<p><code>getTimeline:</code> It allows you to fetch data and declare the next refresh moment of your widget. The above code uses <strong>.atEnd</strong> update policy, which tells WidgetKit to ask for a new timeline once the last date has passed.</p>
</li>
</ul>
<p><img src="/assets/img/articles/2020-11-16-WidgetKit/widget-extension.webp" alt=""></p>
<p>The above output is showing two size styles, Medium and Large because we specified <strong>.systemMedium</strong> and <strong>.systemLarge</strong> inside <strong>supportedFamilies</strong></p>
<h3>Deep Links</h3>
<p>When we tap on the widget, it opens a quote listing(QuoteView). To open <strong>QuoteDetailView</strong> on widget tap, we need to implement deep link in our app.</p>
<ul>
<li>Update model file <strong>Quote.swift</strong> and include unique <strong>url</strong> property.</li>
</ul>
<pre><code class="hljs language-swift"><span class="hljs-keyword">public</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">Quote</span> {
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">let</span> author: <span class="hljs-type">String</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">let</span> quote: <span class="hljs-type">String</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">let</span> url: <span class="hljs-type">URL</span>?

    <span class="hljs-keyword">init</span>(<span class="hljs-params">author</span>: <span class="hljs-type">String</span>, <span class="hljs-params">quote</span>: <span class="hljs-type">String</span>) {
        <span class="hljs-comment">// ...</span>
        <span class="hljs-keyword">self</span>.url <span class="hljs-operator">=</span> <span class="hljs-type">URL</span>(string: <span class="hljs-string">"QuotesWidgetDemo://<span class="hljs-subst">\(<span class="hljs-keyword">self</span>.quoteId)</span>"</span>)
    }
}

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">Quote</span>: <span class="hljs-title class_">Identifiable</span> {
    <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>In the above code I've created url in the form <strong>appname://quoteId</strong> i.e for quoteId <strong>1</strong>, url will be <strong>QuotesWidgetDemo://1</strong>. Make sure url does not contain whitespace, For example, if I create url in the form <strong>appname://author</strong>, then for author Steve Jobs, url will be <strong>QuotesWidgetDemo://SteveJobs</strong></p>
<ul>
<li>Add <strong>onOpenURL</strong> modifier to <strong>AppView</strong> i.e. update <strong>QuoteView.swift</strong> file</li>
</ul>
<pre><code class="hljs language-swift"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">QuoteView</span>: <span class="hljs-title class_">View</span> {
    <span class="hljs-keyword">let</span> quotes: [<span class="hljs-type">Quote</span>] <span class="hljs-operator">=</span> <span class="hljs-type">QuoteProvider</span>.all()
    <span class="hljs-meta">@State</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> showQuote: <span class="hljs-type">Quote</span>?

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
        <span class="hljs-type">NavigationView</span> {
            <span class="hljs-type">List</span> {
                <span class="hljs-type">ForEach</span>(quotes) { quote <span class="hljs-keyword">in</span>
                    <span class="hljs-type">Button</span>(action: {
                        showQuote <span class="hljs-operator">=</span> quote
                    }, label: {
                        <span class="hljs-type">QuoteItemView</span>(author: quote.author, quote: quote.quote)
                    })
                    .sheet(item: <span class="hljs-variable">$showQuote</span>) { quote <span class="hljs-keyword">in</span>
                        <span class="hljs-type">QuoteDetailView</span>(quote: quote)
                    }
                    .onOpenURL { url <span class="hljs-keyword">in</span>
                        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> quote <span class="hljs-operator">=</span> quotes.first(where: { <span class="hljs-variable">$0</span>.url <span class="hljs-operator">==</span> url }) <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> }
                        showQuote <span class="hljs-operator">=</span> quote
                    }
                }
            }
            .foregroundColor(.black)
            .listStyle(<span class="hljs-type">PlainListStyle</span>())
            .navigationBarTitle(<span class="hljs-string">"Quotes"</span>)
        }
    }
}

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">QuoteItemView</span>: <span class="hljs-title class_">View</span> {
    <span class="hljs-comment">// ...</span>
}

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">QuoteDetailView</span>: <span class="hljs-title class_">View</span> {
    <span class="hljs-comment">// ...</span>
}

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">QuoteView_Previews</span>: <span class="hljs-title class_">PreviewProvider</span> {
    <span class="hljs-comment">// ...</span>
}
</code></pre>
<ul>
<li>Add <a href="https://developer.apple.com/documentation/swiftui/view/widgeturl(_:)">widgetURL</a> modifier to <strong>WidgetView</strong> i.e update <strong>QuoteWidgetView.swift</strong> file</li>
</ul>
<pre><code class="hljs language-swift"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">QuoteWidgetView</span>: <span class="hljs-title class_">View</span> {
    <span class="hljs-keyword">let</span> quote: <span class="hljs-type">Quote</span>

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
        <span class="hljs-type">ZStack</span> {
            <span class="hljs-comment">// ...</span>
        }
        .widgetURL(quote.url)
    }
}

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">QuoteWidgetView_Previews</span>: <span class="hljs-title class_">PreviewProvider</span> {
   <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>Let's run the app (make sure QuoteWidgetDemo scheme is selected) and see the output</p>
<p><img src="/assets/img/articles/2020-11-16-WidgetKit/deeplink.webp" alt=""></p>
<h2>References</h2>
<ul>
<li><a href="https://developer.apple.com/documentation/widgetkit/creating-a-widget-extension">Widget Extension</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2020/10028/">Meet WidgetKit</a></li>
<li><a href="https://www.raywenderlich.com/11303363-getting-started-with-widgets">Getting Started With Widgets</a></li>
<li><a href="https://medium.com/swlh/build-your-first-ios-widget-part-2-c69b193b9612">Build Your First iOS Widget</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Engineering Awesome Conference 2020]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/10/08/Engineering-Awesome-Conference-2020</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/10/08/Engineering-Awesome-Conference-2020</guid>
            <pubDate>Thu, 08 Oct 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>We love engaging with the tech community, and we are big consumers of the awesome work that people share online. So a couple of weeks ago we hosted our <a href="https://www.nodesagency.com/engineering-awesome-conference-2020/">first-ever online conference</a> as a way to give back to the community. 6 experts from across our offices in Europe and the Middle East shared their knowledge in topics like Flutter, Vapor, Sitecore and more. After each talk, we had a Q&#x26;A session as well so participants can freely ask their questions on the topic.</p>
<p>All the talks are available on our <a href="https://www.youtube.com/c/BorderlessEngineeringConference">Youtube channel</a> so you can watch or rewatch them at any time. You can also find the presentations slides <a href="https://github.com/nodesagency/events-slides/tree/master/Engineering%20Awesome%20Conference%202020">here</a>.</p>
<p><em>Here are a few behind the scenes photos from our facilitator and speakers.</em></p>
<p><img src="/assets/img/articles/2020-10-08-Engineering-Awesome-Conference-2020/speakers.webp" alt=""></p>
<h4>Building Augmented Reality experiences with iOS</h4>
<p><em>by <a href="http://twitter.com/roxanajula">Roxana Jula</a>, Mobile developer from our Dubai office</em></p>
<p>Apple has a big lead when it comes to augmented reality and in this session, we will have a look at everything that is possible today with ARKit. From motion capture, people occlusion to geolocation activated AR experiences, there are a lot of amazing opportunities for AR.</p>
<p><div class="embed-container">
      <iframe
          src="https://www.youtube.com/embed/LHuidzivLs4"
          width="700"
          height="480"
          frameborder="0"
          allowfullscreen="">
      </iframe>
    </div>
    </p>
<h4>Laravel hosting with AWS ECS and GitHub Actions</h4>
<p><em>by <a href="https://twitter.com/joscdk">Jonas Schwartz</a>, Head of Infrastructure at Nodes sitting in our Berlin office</em></p>
<p>This talk will give you a quick introduction into setting up AWS ECS (Elastic Container Service). We will also be setting up workflows on GitHub Actions for automatically deploying our app on pushes to GitHub.
After this talk, you will have an idea of how to utilize AWS ECS to get your Laravel app up and running in the Cloud. And how to utilize GitHub Actions for your CI/CD system.</p>
<p><div class="embed-container">
      <iframe
          src="https://www.youtube.com/embed/aca3LqdD6TM"
          width="700"
          height="480"
          frameborder="0"
          allowfullscreen="">
      </iframe>
    </div>
    </p>
<h4>What's new in Vapor 4</h4>
<p><em>by <a href="https://twitter.com/HeidiPuk">Heidi Hermann</a>, Vapor Developer at Nodes, from our Berlin office</em></p>
<p>Vapor 4 is released! – So let's have a look at what's new. Some of the topics we'll cover are the changes to Fluent, to database models and the support for eager loading.
Vapor is the most popular Server Side Swift framework. It is written on top of Apple's SwiftNIO, a cross-platform asynchronous event-driven network application framework. You can read more about Vapor <a href="https://vapor.codes/">here</a>.</p>
<p><div class="embed-container">
      <iframe
          src="https://www.youtube.com/embed/fGFA_mfQWWw"
          width="700"
          height="480"
          frameborder="0"
          allowfullscreen="">
      </iframe>
    </div>
    </p>
<h4>Flutter: Creating responsive apps</h4>
<p><em>by <a href="https://twitter.com/markusrubey">Markus Rubey</a>, Senior Android Developer from our Berlin office</em></p>
<p>In this session, we will look at how to layout UI according to the size and shape of a screen or window in order to target phones, tablets, laptop or desktop computers. You can download the code used in this session <a href="https://github.com/markusrubey/responsive_playground">here</a>.</p>
<p><div class="embed-container">
      <iframe
          src="https://www.youtube.com/embed/qtXxrk4q_2A"
          width="700"
          height="480"
          frameborder="0"
          allowfullscreen="">
      </iframe>
    </div>
    </p>
<h4>Android Compose</h4>
<p><em>by <a href="https://twitter.com/jeghedderjohnny">Johnny Sørensen</a>, Head of Android from our Copenhagen office</em></p>
<p>We time travel to the year 2021 in a world where Compose is the standard of the presentation layer on Android. We will have a quick look at how Compose works, a glimpse into structuring an app and how it can work piecing data streams together and ultimately compare it to the ancient days of XML. You can download the code used in this session <a href="https://github.com/johsoe/moviedb-compose">here</a>.</p>
<p><div class="embed-container">
      <iframe
          src="https://www.youtube.com/embed/f_rfr_8cvcY"
          width="700"
          height="480"
          frameborder="0"
          allowfullscreen="">
      </iframe>
    </div>
    </p>
<h4>Thanks to everyone for participating!</h4>
<p>Our facilitator, <a href="https://www.linkedin.com/in/taraujodesouza/">Tiago Araujo de Souza</a>, gave us some good vibes in between the talks with those lo-fi <a href="https://open.spotify.com/playlist/37i9dQZF1DXbtuVQL4zoey?si=6VrRBS7PQta647jUsDvmZQ">Sunny beats</a>.</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Coffee breaks in Zoom Webinars are way more fun 🤩. Plus the lo-fi from <a href="https://twitter.com/Nodes_Apps?ref_src=twsrc%5Etfw">@Nodes_Apps</a> in the background makes everything better. <a href="https://t.co/dqT6VbUWno">pic.twitter.com/dqT6VbUWno</a></p>— Juan Carlos (@JC_Tec_) <a href="https://twitter.com/JC_Tec_/status/1308404290650869760?ref_src=twsrc%5Etfw">September 22, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>We really enjoyed doing this conference and we are planning to bring a second edition soon.</p>
<p>Follow us on social media to stay up to date with Engineering Awesome Conference announcements:</p>
<ul>
<li><a href="https://www.facebook.com/nodesagency/">Facebook</a></li>
<li><a href="https://www.linkedin.com/company/nodes/">Linkedin</a></li>
<li><a href="https://twitter.com/Nodes_Apps">Twitter</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[API First]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/10/07/Api-first</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/10/07/Api-first</guid>
            <pubDate>Wed, 07 Oct 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>We've been building APIs for a long time, enabling our mobile software solutions to communicate with each other across their respective platforms. For years these APIs have been built as part of a "mobile first"-approach, under the guiding light of an internally defined, living document, our so called "API Manifesto", highlighting everything from how URIs are formed to response body formatting and response codes. A recent change in how we document APIs internally, from simple markdown descriptions of individual endpoints to fully fletched OpenAPI specifications and the ability to spawn mock-servers on the fly, has sparked thoughts on how APIs are treated overall in the context of project development within the organization.</p>
<p>Over the past 10 years, being involved with developing and maintaining APIs, I've seen a great deal of approaches to this discipline, some better than others. Some of the worst examples that comes to mind, emerges from different mindsets on how software development should be executed eg. "We need to ship this fast!" (sloppiness), "We need more developers on this to speed up the process" (messy, inconsistent) and "We don't have a mobile developer available at the moment, can we fix this in the API instead?" (platform inconsistency). With no common understanding of "how and why" and "dos and don'ts", the outcome is often disastrous. Yes, in the end it might do the job, but it is in no way a pretty sight, and maintaining these remnants can be a tedious affair.</p>
<h2>The Contract</h2>
<p>Seen from the perspective of the vendor of services, executing like the examples given above can lead to an immense amount of frustration. Nothing can make a developer tick off more than when presented to source code that doesn't follow expected agreements or when being expected to build it in an unexpected way. The consumers of APIs also have different opinions to what can be expected in responses from API-services.</p>
<p>With an API-First approach, the involved parties make a mutual agreement on the structure and behavior of the service. This agreement is the ultimate reference, "the law", when doubt arise. It can be utilised to easen decision making regarding future development of the service, referring to original structure, as well as a tool to resolve disputes regarding data structure, response codes and HTTP methods.</p>
<h2>The Product</h2>
<p>Many sources on API First underlines treating the API as a "first class citizen" when building software projects, and this is a term I really like. Basically it means that the API is, if not solely then a part of, the main product that is being developed. This means that it is being managed like other primary aspects of a project. Mark O'Neill, VP of Innovation at AXWAY explains it really well:</p>
<p><div class="embed-container">
      <iframe
          src="https://www.youtube.com/embed/RP-soXCWoIs"
          width="700"
          height="480"
          frameborder="0"
          allowfullscreen="">
      </iframe>
    </div>
    </p>
<h2>The Future</h2>
<p>I can imagine a lot of the projects, large and small, I've been working on over the years would have benefited from an API development phase under strict management. Even though you can argue that many smaller productions doesn't really require a fully managed development of an API, many of the elements involved in thinking "API first" are to me quite intriguing, enabling better maintainability and transparency across the different teams involved. With a common contribution from people with multiple perspectives (vendor and consumer) on how the API behaves, it is my belief better results we be created.</p>
<p>I do not expect a paradigm shift like this in the company to happen over night, but this article could potentially become the starting point of such a shift. A lot of well written articles about API first already exists and can be found by a simple <a href="https://www.google.com/search?q=api+first">google search</a>, so these are my 5 cents on how my life could become a little better by adopting these ideas.</p>
<h2>Further reading and references</h2>
<ul>
<li><a href="https://swagger.io/resources/articles/adopting-an-api-first-approach/">Understanding the API-First Approach to Building Products</a></li>
<li><a href="https://apifriends.com/api-creation/api-first/">What is an API First definition?</a></li>
</ul>
<p><em>Header photo by <a href="https://unsplash.com/photos/I-SoYkFjVI0">CDC on Unsplash</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Make apps for everyone]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/09/16/Make-apps-for-everyone</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/09/16/Make-apps-for-everyone</guid>
            <pubDate>Wed, 16 Sep 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>The human brain tends to assume that everybody else thinks and behaves in a similar way to it. So every person tends to think that, based on certain categories, they're part of a majority, that there are many others like them. As software developers, it's very easy to assume that every person who uses the apps or the software we make is the same as us. But people are different. How do we make sure we make apps for everyone? We need to care about accessibility and inclusivity.</p>
<h2>Accessibility</h2>
<p>When it comes to mobile apps, I would say that accessibility is something that's closer to UX than to the development phase. That's because accessibility has to start from the design. We have to design and develop apps with accessibility in mind.</p>
<p>Unfortunately, the current state of mobile apps, in general, is that they're not accessible, they're not inclusive, developers don't do accessibility testing, and don't even understand the needs of people with disabilities. How many of you have actually seen how a visually impaired person uses a phone? Fortunately, iOS and Android have made a lot of progress in the accessibility field, and they made it a lot easier for developers to build accessible apps. And that when it comes to mobile apps, some accessibility is better than no accessibility.</p>
<h2>Who needs accessibility?</h2>
<p>But who needs accessibility? Earlier this month I gave an internal talk about accessibility. And when I announced it in Slack, one of the first Slack reactions my post got was the wheelchair symbol emoji ♿️ . When hearing the term "accessibility", many iOS developers immediately think of Voice Over, the technology that reads out loud things on the screen so people who are visually impaired can use the phone. But the thing is, all of us need certain accessibility features now and then.</p>
<p>Microsoft's <a href="https://download.microsoft.com/download/b/0/d/b0d4bf87-09ce-4417-8f28-d60703d672ed/inclusive_toolkit_manual_final.pdf">Inclusive toolkit</a> has great content on accessibility and inclusivity. A disability should be seen as a mismatch between human interactions and society and not as a personal health problem. They also bring into the spotlight the ideas of temporary and situational disability.</p>
<p><img src="/assets/img/articles/2020-09-16-Make-apps-for-everyone/microsoft_toolkit.webp" alt=""></p>
<center><em> © Microsoft </em></center>
<h2>Why should we care about accessibility?</h2>
<p>According to <a href="https://developer.apple.com/design/human-interface-guidelines/accessibility/overview/introduction/">Apple's Human Interface Guidelines</a>, approximately one in seven people worldwide have a disability or impairment that affects the way they interact with the world and their devices. That's 14% of your potential user base, which you may lose from the start unless you make your app for them too.</p>
<p>According to <a href="https://pspdfkit.com/blog/2018/improving-dynamic-type-support/#user-adoption">statistics by PSPDFKit</a>, 27% of the users of their PDF Viewer app used a different text size than the default one. That's more than one in four people who might find the experience of your app nonoptimal. Give them control over the text size by supporting Dynamic Type on iOS. Make your apps for them too.</p>
<p>Not to mention that it's the right thing to do and that we as developers and as human beings should care for other people.</p>
<p>And if that's still not good enough, in the US the Americans with Disabilities Act requires certain technology to be accessible for people with disabilities. And there was also a case where the Supreme Court <a href="https://www.cnbc.com/2019/10/07/dominos-supreme-court.html">ruled in favor of a blind man who sued Domino's because their website wasn't accessible</a>.</p>
<h2>Inclusivity</h2>
<p>But making apps for everyone is about more than accessibility. It's also about making everyone feel welcome on your platform or in your app. For example, you've probably all seen sign up forms where the users have to input their gender. In most cases, that's a drop-down or a picker with 2 choices, male or female. But what about persons who don't identify as male or female? Well, the easiest thing to do is to just add an "I'd rather not say" option. But what about the people who don't identify as male or female, but would rather say that? So you probably add an "Other" option, pat yourself on the back and congratulate yourself on the great job you did at being inclusive and caring about others. And yes, that definitely is a better solution than just "Male" or "Female". But is it enough? How would a non-binary person feel when they see that your app will categorise them as "other"?</p>
<p>So what's the best way to handle this situation? It's simple: don't collect gender data on your users. With the exception of a very limited number of apps, I can't think of any good reason why an app would need to know its users' gender.</p>
<p>And what about names? There are so many cases where developers built systems with validation rules that fail too many valid names.</p>
<blockquote class="twitter-tweet" data-lang="en" data-dnt="true" data-theme="light"><p lang="en" dir="ltr">My last name isn’t valid because two letters. <br><br>The irony of this... is someone who is Asian probably built parts of this app. How did they not catch this..? <a href="https://t.co/zPVzDOsbWN">pic.twitter.com/zPVzDOsbWN</a></p>— Shirley (@shirleyywu) <a href="https://twitter.com/shirleyywu/status/1300628412466298881?ref_src=twsrc%5Etfw">September 1, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>There's an old article that is still very much valid about <a href="https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/">falsehoods programmers believe about names</a>. And one of the most common mistakes with names is assuming that all names your system will ever support are written with the letters of the English alphabet. So no æ, å, ø, ö, ä, ü, ß, ș, ț, ă, â, î, ç, é, etc. How would a person feel when your app tells them their name is not valid?</p>
<p>We need to make apps for everyone. And yes, it's easy to think that if our app is good for us, then it's good for everyone else. But that's not always the case. Here's also where the importance of diversity in the team plays a big role. The more diverse a team is, the more it's likely to build an app that's good for everyone else.</p>
<p>As a conclusion, I'll simply reiterate the title of the article: make apps for everyone.</p>
<h2>Further reading and references:</h2>
<ul>
<li><a href="https://www.youtube.com/watch?v=LHHmx5XxIBc">Apps for All: Making Software Accessible</a></li>
<li><a href="https://download.microsoft.com/download/b/0/d/b0d4bf87-09ce-4417-8f28-d60703d672ed/inclusive_toolkit_manual_final.pdf">Inclusive - Microsoft Design</a></li>
<li><a href="https://ux.shopify.com/you-cant-just-draw-purple-people-and-call-it-diversity-e2aa30f0c0e8">You Can’t Just Draw Purple People and Call it Diversity</a></li>
</ul>
<p><em>Header photo by <a href="https://xd.adobe.com/ideas/perspectives/social-impact/inclusion-doesnt-stop-accessibility/">Adobe</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Locust sketch]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/09/10/Locust-sketch</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/09/10/Locust-sketch</guid>
            <pubDate>Thu, 10 Sep 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><a href="https://locust.io/">Locust</a> is an easy-to-use distributed load testing tool that is completely event-based, that is, a locust node can also support thousands of concurrent users in a process, without callbacks, and use a lightweight process through <code>gevent</code> (that is, in Run in its own process). And has the following characteristics:</p>
<ul>
<li>No need to write cumbersome UI or bloated XML code, based on coroutine instead of callback, script writing is simple and easy to read;</li>
<li>There is a simple Web UI user interface that can display relevant test results in real time;</li>
<li>Support distributed testing, the user interface is based on the network, so it has the characteristics of cross-platform and easy to expand;</li>
<li>All cumbersome I/O and co-programs are entrusted to <code>gevent</code> to replace the limitations of other tools;</li>
</ul>
<h3>Comparison</h3>





















































<table><thead><tr><th></th><th>LoadRunner</th><th>JMeter</th><th align="left">Locust</th></tr></thead><tbody><tr><td>Cost</td><td>Commercial charges</td><td>Free and open source</td><td align="left">Free and open source</td></tr><tr><td>Language</td><td>C/Java</td><td>GUI</td><td align="left">Python</td></tr><tr><td>Concurrency mechanism</td><td>Thread / process</td><td>Thread</td><td align="left">Coroutine</td></tr><tr><td>Single machine concurrency capability</td><td>Low</td><td>Low</td><td align="left">High</td></tr><tr><td>Distributed stress test</td><td>Support</td><td>Support</td><td align="left">Support</td></tr><tr><td>Reporting and analysis</td><td>Perfect</td><td>Simple chart</td><td align="left">Simple chart（Inferior to JMeter）</td></tr><tr><td>Secondary development</td><td>Not Supported</td><td>Support</td><td align="left">Support</td></tr></tbody></table>
<h3>Script introduction</h3>
<h5>Scripting:</h5>
<pre><code class="hljs language-python"><span class="hljs-keyword">from</span> locust <span class="hljs-keyword">import</span> HttpLocust, TaskSet, task, between
<span class="hljs-keyword">class</span> <span class="hljs-title class_">Test</span>(<span class="hljs-title class_ inherited__">TaskSet</span>):
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">on_start</span>(<span class="hljs-params">self</span>):
        <span class="hljs-comment"># Construct request body and request header</span>
        signup_data = {<span class="hljs-string">'account'</span>: <span class="hljs-string">'test@test.com'</span>,
                       <span class="hljs-string">'password'</span>: <span class="hljs-string">'123456'</span>,
                       <span class="hljs-string">'password_confirm'</span>: <span class="hljs-string">'123456'</span>,
                       }
        signup_header = {<span class="hljs-string">"Authorization"</span>: <span class="hljs-string">r"Bearer tester"</span>,
                         <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/x-www-form-urlencoded"</span>}
        <span class="hljs-comment"># send signup request</span>
        self.client.post(<span class="hljs-string">'/api/test/testForSignup'</span>,
                         data=signup_data,
                         headers=signup_header,
                         catch_response=<span class="hljs-literal">True</span>)
<span class="hljs-meta">    @task(<span class="hljs-params"><span class="hljs-number">1</span></span>)</span>
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_timeline_page</span>(<span class="hljs-params">self</span>):
        <span class="hljs-comment"># Construct request header</span>
        get_header = {<span class="hljs-string">"Authorization"</span>: <span class="hljs-string">r"Bearer tester"</span>,
                      <span class="hljs-string">"Accept-Language"</span>: <span class="hljs-string">r"test"</span>}
        <span class="hljs-comment"># send get request</span>
        timeline_page_response = self.client.get(<span class="hljs-string">'/api/test/timelinePage'</span>,
                                                 headers=get_header)
        <span class="hljs-comment"># print response</span>
        <span class="hljs-built_in">print</span>(timeline_page_response.status_code)
<span class="hljs-meta">    @task(<span class="hljs-params"><span class="hljs-number">2</span></span>)</span>
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">get_mypage</span>(<span class="hljs-params">self</span>):
        get_header = {<span class="hljs-string">"Authorization"</span>: <span class="hljs-string">r"Bearer tester"</span>,
                      <span class="hljs-string">"Accept-Language"</span>: <span class="hljs-string">r"test"</span>}
        mypage_response = self.client.get(<span class="hljs-string">"/api/test/mypage"</span>,
                                          headers=get_header)
        <span class="hljs-built_in">print</span>(mypage_response.status_code)
<span class="hljs-keyword">class</span> <span class="hljs-title class_">WebSiteUser</span>(<span class="hljs-title class_ inherited__">HttpLocust</span>):
    task_set = Test
    wait_time = between(<span class="hljs-number">1</span>, <span class="hljs-number">3</span>)
    host = <span class="hljs-string">'https://test.net'</span>
</code></pre>
<p>As shown in the code above, we simulated three requests for API access. After executing the "sign-up" request first, "my-page" or "get_timeline_page" will be randomly executed at a ratio of 2:1.</p>
<p><strong>So, how does the above Python script express the above test scenario?</strong>
As can be seen from the script, it mainly contains two classes, one is <code>WebSiteUser</code> (inherited from <code>HttpLocust</code>, and <code>HttpLocust</code> is inherited from <code>Locust</code>), and the other is <code>Test</code> (inherited from <code>TaskSet</code>). In fact, in Locust's test scripts, all business test scenarios are described in the inherited subclasses of <code>Locust</code> and <code>TaskSet</code>.</p>
<h5>Script arguments:</h5>
<ul>
<li><code>@task()</code>: Indicates the proportion of the execution of the API request; for example, the above figure is filled with 2 and 1 respectively; after the request in the <code>on_start</code> function is completed; the function will be randomly executed according to the proportion of 2:1. The function must have <code>@task()</code> to be executed.</li>
<li><code>wait_time</code>: Indicates the random interval of each request.</li>
<li><code>task_set</code>: Point to a <code>TaskSet</code> class, <code>TaskSet</code> class defines the user's task information, this attribute is required;</li>
<li><code>host</code>: The host of the system under test, only used when the <code>--host</code> parameter is not specified when starting Locust in the terminal.</li>
</ul>
<h5>Enter the locust terminal, and start testing:</h5>
<p>Start Locust, enter the background management page of Locust to execute the script and set the number of concurrency. You can use the following example command to start Locust:</p>
<pre><code class="hljs">Locust -f file_name.py
</code></pre>
<p>After successful startup, you can see the following example prompt:</p>
<pre><code class="hljs language-groovy">DESKTOP<span class="hljs-number">-2</span>M3K1VA<span class="hljs-regexp">/INFO/</span>Locust.<span class="hljs-attr">main:</span> Starting web
monitor at <span class="hljs-attr">http:</span><span class="hljs-comment">//*:8089</span>
DESKTOP<span class="hljs-number">-2</span>M3K1VA<span class="hljs-regexp">/INFO/</span>Locust.<span class="hljs-attr">main:</span> Starting Locust  <span class="hljs-number">0.14</span><span class="hljs-number">.4</span>
</code></pre>
<p>Follow the prompt to visit 「locusthost:8089」 to enter the Locust management background and start the test:</p>
<p><img src="/assets/img/articles/2020-09-10-Locust-sketch/image2.webp" alt=""></p>
<p>After setting the various parameters, start the test. You can see the following information output and you can monitor the resource occupation of the server while increasing the concurrency pressure. You can use locust to complete a simple stress test:</p>
<p><img src="/assets/img/articles/2020-09-10-Locust-sketch/image3.webp" alt=""></p>
<h5>About the Distributed stress test:</h5>
<p>Once a single machine is not enough to simulate enough users, Locust supports running on multiple machines for stress testing.</p>
<p>To achieve this, you can use the <code>--master</code> flag in master mode to enable a Locust instance. This example will run the Locust interactive website you started the test on and view real-time statistics. The machine of the master node does not impersonate any users. Instead, you must use the <code>--slave</code> flag to start one or more Locust slave machine nodes, together with the flag <code>--master-host</code> (indicating the IP/hostname of the master machine).</p>
<p>The common practice is to run the master in a separate machine, and run a slave instance for each processor core in the slave machine.</p>
<p>The commands for master-slave settings are as follows:
Set Master （The host does not participate in the test, only the results are summarized）:</p>
<pre><code class="hljs language-css">Locust -f file_name<span class="hljs-selector-class">.py</span> <span class="hljs-attr">--master</span>
</code></pre>
<p>Set Slaves：</p>
<pre><code class="hljs language-css">Locust -f file_name<span class="hljs-selector-class">.py</span> <span class="hljs-attr">--slave</span> <span class="hljs-attr">--master-host</span>=<span class="hljs-number">127.0</span>.<span class="hljs-number">0.1</span>
</code></pre>
<p>After starting the Locust terminal again, you can see the Slaves, so there are two Slaves participating in the test.</p>
<p><img src="/assets/img/articles/2020-09-10-Locust-sketch/image4.webp" alt=""></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Easy web augmented reality with AR Quick Look]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/09/07/Easy-web-augmented-reality-with-ar-quick-look</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/09/07/Easy-web-augmented-reality-with-ar-quick-look</guid>
            <pubDate>Mon, 07 Sep 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Apple is making augmented reality accessible to everyone with AR Quick Look. Not only is this great for iOS apps, but also for websites. Online shopping is one of the best industries that can take advantage of AR, as it can give users the possibility to see the products in 3D and in their own environments. From previewing furniture in your home with apps like <a href="https://apps.apple.com/us/app/ikea-place/id1279244498">IKEA Place</a>, to experiencing newly launched cars like this <a href="https://www.lamborghini.com/en-en/models/huracan/huracan-evo-rwd-spyder">Lamborghini Huracán</a>, this technology can improve our experience as buyers. But of course, the applications of web AR extend beyond retail as well.</p>
<h2>AR Quick Look</h2>
<blockquote>
<p>Built-in apps, such as Safari, Messages, Mail, News, and Notes, use Quick Look to display USDZ files of virtual objects in 3D or AR on iPhone and iPad. You can embed Quick Look views in your apps and websites to let users see incredibly detailed object renderings in a real-world surrounding with support for audio playback.</p>
</blockquote>
<p>There are many advantages of using AR Quick Look: it gives you interactions like placing, moving and scaling the object, people occlusion and sharing of the model available “out of the box”. It supports vertical and horizontal planes for placing your content and even viewing face accessories. You can view a gallery of Apple's AR Quick Look examples <a href="https://developer.apple.com/augmented-reality/quick-look/">here</a>, all you need to do is open the models on an iPhone or iPad to view them in AR.</p>
<p>For web based AR Quick Look experiences we can use USDZ, the distribution format for <a href="http://openusd.org/">USD (Universal Scene Description)</a>,which is a 3D file format developed by Pixar that focuses on speed, scalability and collaboration.</p>
<p>In this tutorial, we will build a simple webpage that shows a sneaker in AR.</p>
<h2>Preparing the 3D model</h2>
<p>I will be using a model from <a href="www.sketchfab.com">Sketchfab.com</a> but feel free to use whatever model you wish in USDZ file format. Sketchfab offers the option to download the model directly in this file format.</p>
<p><a href="https://sketchfab.com/3d-models/adidas-originals-stan-smith-358c67bd2bf74460a5ffc5d93e1e5be0">Adidas Originals Stan Smith by VRModelFactory</a></p>
<h3>Converting your 3D models to USDZ Files/Changing the base unit of the model</h3>
<p>In case you already have your models in other 3D formats, you can check out <a href="https://engineering.monstar-lab.com/2020/04/26/how-to-convert-3d-models-to-usdz-files-using-apples-reality-converter">this article</a> on how you can easily convert them to USDZ using Apple's <a href="https://developer.apple.com/news/?id=01132020a">Reality Converter</a>.
In this article you can also see in the <code>Properties</code> section how to edit the base unit for example from meters to centimetres. I had to do this as well for the sneaker model so that it has a real life size when viewing it in AR.</p>
<h2>Getting started</h2>
<p>In order to test what we are building right away on real devices, for simplicity, we will be using <a href="https://codepen.io/">codepen.io</a>:</p>
<ol>
<li>Sign up or login on Codepen</li>
<li>Create a new pen and name it as you wish</li>
<li>Click save, this will create a link that you can open on your device for testing. Here is how my link looks like: <a href="https://codepen.io/roxanajula/pen/poydGKx">https://codepen.io/roxanajula/pen/poydGKx</a>, you should have something similar.</li>
</ol>
<h2>Showing the model in AR</h2>
<p>For basic AR Quick Look functionality, all we need to do is add the following code in the HTML file:</p>
<pre><code class="hljs language-html"><span class="hljs-tag">&#x3C;<span class="hljs-name">div</span>></span>
  <span class="hljs-tag">&#x3C;<span class="hljs-name">a</span>
    <span class="hljs-attr">rel</span>=<span class="hljs-string">"ar"</span>
    <span class="hljs-attr">href</span>=<span class="hljs-string">"https://roxanajula.com/wp-content/uploads/2020/09/Adidas_Originals_Stan_Smith.usdz"</span>
  ></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">img</span>
      <span class="hljs-attr">src</span>=<span class="hljs-string">"https://roxanajula.com/wp-content/uploads/2020/09/Adidas_Originals_Stan_Smith.jpg"</span>
    /></span>
  <span class="hljs-tag">&#x3C;/<span class="hljs-name">a</span>></span>
<span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
</code></pre>
<p>The code above is pretty straightforward, we have the AR model inside a hyperlink tag and an image to be displayed for the model. By adding the <code>rel="ar"</code> attribute we tell the browser that this is an AR model so it will automatically add the AR badge in the right corner of the image and will start the AR experience directly instead of navigating to another page.</p>
<p><img src="/assets/img/articles/2020-09-07-Easy-web-augmented-reality-with-ar-quick-look/codepen.webp" alt=""></p>
<p>I uploaded the image and model on my personal website as you can only host assets directly on Codepen with a paid account. When you will be developing your project you can also link to your project files, like this for example: <code>href="/assets/models/model.usdz"</code>.</p>
<p>Additionally, I added the following to the CSS file to fit the image nicer on the screen:</p>
<pre><code class="hljs language-css"><span class="hljs-selector-tag">img</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
}
</code></pre>
<p>And that's it! You can now save it and reload the Codepen link from your device. When we tap on the image on iOS, we will be taken to the AR Quick Look view. If we tap on the image on macOS, the model will be downloaded on your machine.</p>
<p><img src="/assets/img/articles/2020-09-07-Easy-web-augmented-reality-with-ar-quick-look/ar.webp" alt=""></p>
<h2>Banners</h2>
<p><em>Available for devices running iOS 13.3 or later</em></p>
<p>In the previous section we looked at the most basic AR Quick Look experience, but you can also add banners with <code>Apple Pay</code> or <code>Custom Actions</code> to your AR experience.</p>
<p>Check out this very useful <a href="https://applepaydemo.apple.com/ar-quick-look">webpage</a> made by Apple for more information on integrating those features. You can enter all the customisation details you need for your banner and this webpage will generate the code for you, pretty cool!</p>
<p>In this section we will have a look at different styles of banners you can add but it will be up to you to define the actions your website takes in response to tapping on the banner. For more information, see <a href="https://developer.apple.com/documentation/arkit/adding_an_apple_pay_button_or_a_custom_action_in_ar_quick_look">this article from Apple</a>.</p>
<p>The flow of your experience will look like this:
<img src="/assets/img/articles/2020-09-07-Easy-web-augmented-reality-with-ar-quick-look/bannerFlow.webp" alt=""></p>
<p><em>Image Source: <a href="https://developer.apple.com/videos/play/wwdc2020/10604/">Apple</a></em></p>
<p>Here are some examples of custom banners for our sneaker webpage:</p>
<p><strong>Apple Pay banner with title, subtitle, price and a "Buy with  Pay" button:</strong></p>
<ul>
<li>Button types: <code>plain</code> (default), <code>book</code>, <code>buy</code>, <code>check-out</code>, <code>donate</code>, <code>pay</code> and <code>subscribe</code></li>
<li>The <code>price</code> is optional on Apple Pay banners only from iOS 14.</li>
</ul>
<pre><code class="hljs language-html"><span class="hljs-tag">&#x3C;<span class="hljs-name">div</span>></span>
  <span class="hljs-tag">&#x3C;<span class="hljs-name">a</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"ar"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"ar-link"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://roxanajula.com/wp-content/uploads/2020/09/Adidas_Originals_Stan_Smith.usdz#applePayButtonType=buy&#x26;checkoutTitle=Adidas%20Stan%20Smith&#x26;checkoutSubtitle=Classic%20trainers&#x26;price=$80"</span>></span>
      <span class="hljs-tag">&#x3C;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://roxanajula.com/wp-content/uploads/2020/09/Adidas_Originals_Stan_Smith.jpg"</span>></span>
<span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
</code></pre>
<p><img src="/assets/img/articles/2020-09-07-Easy-web-augmented-reality-with-ar-quick-look/applePayBanner.webp" alt=""></p>
<p>Find out more about how to support Apple Pay on your website <a href="https://developer.apple.com/documentation/apple_pay_on_the_web">here</a>.</p>
<p><strong>Custom action banner with title, subtitle, price and a "Preorder" button:</strong></p>
<pre><code class="hljs language-html"><span class="hljs-tag">&#x3C;<span class="hljs-name">div</span>></span>
  <span class="hljs-tag">&#x3C;<span class="hljs-name">a</span>
    <span class="hljs-attr">rel</span>=<span class="hljs-string">"ar"</span>
    <span class="hljs-attr">id</span>=<span class="hljs-string">"ApplePay"</span>
    <span class="hljs-attr">href</span>=<span class="hljs-string">"https://roxanajula.com/wp-content/uploads/2020/09/Adidas_Originals_Stan_Smith.usdz#callToAction=Preorder&#x26;checkoutTitle=Adidas%20Stan%20Smith&#x26;checkoutSubtitle=Classic%20trainers&#x26;price=$100"</span>
  ></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">img</span>
      <span class="hljs-attr">src</span>=<span class="hljs-string">"https://roxanajula.com/wp-content/uploads/2020/09/Adidas_Originals_Stan_Smith.jpg"</span>
    /></span>
  <span class="hljs-tag">&#x3C;/<span class="hljs-name">a</span>></span>
<span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
</code></pre>
<p><img src="/assets/img/articles/2020-09-07-Easy-web-augmented-reality-with-ar-quick-look/preorder.webp" alt=""></p>
<p><strong>Custom large sized banner with own html:</strong></p>
<ul>
<li>You can pick between 3 height sizes: <code>small</code> (81 points), <code>medium</code>(121 points) and <code>large</code>(161 points). Small will be set by default if <code>customHeight</code> is omitted.</li>
<li>The custom HTML file must be sent over HTTPS.</li>
<li>I am hosting <a href="https://roxanajula.com/wp-content/uploads/2020/09/sneaker_custom.html">the custom html file on my website</a></li>
</ul>
<pre><code class="hljs language-html"><span class="hljs-tag">&#x3C;<span class="hljs-name">div</span>></span>
  <span class="hljs-tag">&#x3C;<span class="hljs-name">a</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"ar"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"ApplePay"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://roxanajula.com/wp-content/uploads/2020/09/Adidas_Originals_Stan_Smith.usdz#custom=https://roxanajula.com/wp-content/uploads/2020/09/sneaker_custom.html&#x26;customHeight=large"</span>></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://roxanajula.com/wp-content/uploads/2020/09/Adidas_Originals_Stan_Smith.jpg"</span>></span>
<span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
</code></pre>
<p><img src="/assets/img/articles/2020-09-07-Easy-web-augmented-reality-with-ar-quick-look/customBanner.webp" alt=""></p>
<h2>Content scaling</h2>
<p>Content scaling is enabled by default but you can disable it with the following URL parameter: <code>allowsContentScaling=0</code>.</p>
<h2>Share link</h2>
<p>The share functionality will link to the model by default, but you can also specify another link with the following URL parameter: <code>canonicalWebPageURL=https://example.com</code>.</p>
<h2>Detect support for AR</h2>
<p>It is good practice to only show the AR model to the users if it's supported, here is how you can detect if AR is supported:</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">const</span> a = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">createElement</span>(<span class="hljs-string">"a"</span>);
<span class="hljs-keyword">if</span> (a.<span class="hljs-property">relList</span>.<span class="hljs-title function_">supports</span>(<span class="hljs-string">"ar"</span>)) {
  <span class="hljs-comment">// AR is available.</span>
}
</code></pre>
<p><em>Code Source: <a href="https://webkit.org/blog/8421/viewing-augmented-reality-assets-in-safari-for-ios/">Viewing Augmented Reality Assets in Safari for iOS</a></em></p>
<h2>Bonus: Nested models</h2>
<p>You can have a look at the <code>Making the nested USDZ file</code> section in this <a href="https://engineering.monstar-lab.com/2019/12/31/How-to-make-an-Augmented-Reality-decorating-experience-app-with-AR-Quick-Look">article</a> for a step by step tutorial on how to nest together more models into one USDZ file.</p>
<p><em>Keep in mind that USDZ is still a fairly new format so you might need to check that your web server understands the <code>model/vnd.usdz+zip</code> MIME-type. Refer to your web server documentation on how to configure this if not already supported.</em></p>
<h2>Resources</h2>
<ul>
<li><a href="https://codepen.io/roxanajula/pen/poydGKx">Sneaker shop with AR Quick Look Codepen</a></li>
<li><a href="https://developer.apple.com/documentation/arkit/adding_an_apple_pay_button_or_a_custom_action_in_ar_quick_look">Adding an Apple Pay Button or a Custom Action in AR Quick Look</a></li>
<li><a href="https://webkit.org/blog/8421/viewing-augmented-reality-assets-in-safari-for-ios/">Viewing Augmented Reality Assets in Safari for iOS</a></li>
<li><a href="https://developer.apple.com/documentation/arkit/previewing_a_model_with_ar_quick_look">Previewing a Model with AR Quick Look</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2020/10604/">WWDC20: Shop online with AR Quick Look</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[RubyKaigi Takeout 2020: Day One]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/09/04/RubyKaigi-Takeout-2020-Day-first</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/09/04/RubyKaigi-Takeout-2020-Day-first</guid>
            <pubDate>Fri, 04 Sep 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Today, I participated in the RubyKaigi Takeout 2020 online conference. It was the first large-scale developer conference
that I ever took part in and I am very excited to share my experience with you.</p>
<h2>What is RubyKaigi?</h2>
<p>RubyKaigi is the largest conference for Ruby programmers held annually in Japan since 2006. Due to the nature of the
Ruby ecosystem, a significant portion of the presentations and discussions are done in Japanese, but since the
conference organizers understand the importance and size of the international community, all talks presented in Japanese
are also available with an English interpretation.</p>
<p>This year, RubyKaigi was originally postponed, then cancelled, and finally converted into an online conference. That
way, truly anyone could join the event.</p>
<h2>Sessions available</h2>
<p>This year, there were two “tracks” available: <code>#rubykaigiA</code> with mostly Japanese speakers, and <code>#rubykaigiB</code> with all
talks being done in English. After looking at the first day’s
<a href="https://rubykaigi.org/2020-takeout/schedule/#day1">schedule page</a> and reading through the descriptions of each talks,
I mostly decided to follow the <code>#rubykaigiA</code> track from the start and switch to the <code>#rubykaigiB</code> track just before
lunch; though I definitely want to catch up on some of the talks that I had to skip – there was just way too much good
content available there (which, I suppose, is a very good problem to have 😅)!</p>
<h2>Ractor Report by Koichi Sasada</h2>
<p>The conference kicked off (after just a little bit of technical issues because as beloved Murphy would say, “everything
that could go wrong, will go wrong”) with a status report by Sasada-san on Ractor (previously known as Guild). Ractor
will provide Ruby with a parallelism model that provides many benefits compared to the thread-based model seen in many
other programming languages.</p>
<p>Compared to thread-based concurrency, Ractors are thread-safe (meaning you should not be running into dreaded issues
such race condition) and don’t share most objects – in fact, the only objects that are shareable are immutable objects,
class/module objects and some special shareable objects. To send any other data between two Ractors, either a
push-based (where target Ractor is known) or pull-based (where sender Ractor is known) must be used.</p>
<p>Each Ractor can use one or more threads, and in the demonstration showed by Sasada-san, it seemed that Ractors can
utilize available hardware resources quite a bit better than traditional threads.</p>
<h2>Type Profiler: a Progress Report of a Ruby 3 Type Analyzer by Yusuke Endoh</h2>
<p>Endoh-san showed everyone the impressive work he’s done on Type Profiler (tentative name), a Ruby interpreter that
executes Ruby programs at the type level and tries to determine type information. This information is then stored in
<code>rbs</code> files, which are Ruby 3’s rough equivalent to TypeScript’s <code>d.ts</code> type information files.</p>
<p>![Type Profiler](/assets/img/articles/2020-09-04-RubyKaigi-Takeout-2020-Day-first/type-profiler.webp</p>
<p>As someone who comes to the world of Ruby from the land of statically-typed languages, I can’t wait to get my hands on
Ruby 3 (and RubyMine once it supports RBS type definitions).</p>
<h2>On sending methods by Urabe, Shyouhei</h2>
<p>In this talk, viewers were taken on a magical journey that resembled a detective story: it all started with an unusual
profiler’s output that didn’t make much sense. After digging deeper and deeper into the mystery, a culprit in Rails’
ActiveRecord library was found, causing Ruby’s method cache to evict entries that shouldn’t be evicted. After fixing the
issue in Ruby 2.7, the performance of Ruby and Rails apps increased!</p>
<h2>Reflecting on Ruby Reflection for Rendering RBIs by Ufuk Kayserilioglu</h2>
<p>After switching to the <code>#rubykaigiB</code> track, I was shown Ruby’s powerful capabilities when it comes to reflection and
the many intricacies that one must be aware of when trying to automatically analyze the source code of third-party
dependencies. The work done by Ufuk is used in the <a href="https://github.com/Shopify/tapioca">Tapioca</a> project, which can
automatically generate type information for your project’s Gem dependencies.</p>
<p>![Ruby Reflection](/assets/img/articles/2020-09-04-RubyKaigi-Takeout-2020-Day-first/reflection.webp</p>
<p>I only wish that the resulting type information were generated in the RBS format as opposed to Sorbet’s RBI format, but
one can dream, right?</p>
<h2>The whys and hows of transpiling Ruby by Vladimir Dementyev</h2>
<p>Transpiling is a technique of transforming source code of one programming language into an equivalent source code in the
same or different programming language. While that’s a daily practice for nearly all front-end web developers (hello
Babel), I was at first unsure as to why that would be necessary in the world of Ruby where we don’t have to worry about
obsolete web browsers that don’t support the latest and greatest versions of JavaScript.</p>
<p>![Transpiling](/assets/img/articles/2020-09-04-RubyKaigi-Takeout-2020-Day-first/transpiling.webp</p>
<p>Turns out that my world-view was quite limited, as transpiling doesn’t have to be a process born out of necessity, but
rather out of convenience! When transpiling code written in the Ruby 2.8 syntax into, let’s say, Ruby 2.5 syntax, we
can enjoy the luxury of using new language constructs while still ensuring that the code can be used as a dependency in
projects that are still running an old version of Ruby. Better yet, new or experimental features can be tested and
triaged before the Ruby team decides to add them to the standard language. If you want to learn more about this topic,
I really recommend reading through
<a href="https://evilmartians.com/chronicles/ruby-next-make-all-rubies-quack-alike">this excellent article</a>.</p>
<h2>mruby machine: An Operating System for Microcontoller by Hasumi Hitoshi</h2>
<p>Finally, a personal highlight of the day – our own MonstarLab’s Hasumi-san was showing that Ruby is not a language
viable only for computers and servers, but also microcontrollers. That might seem obvious to you, dear reader, but I
have to admit that I would have never imagined that an interpreted language can ever be capable of running on such a
limited hardware – though I’m someone whose microcontroller experience ends with a bit of tinkering with Arduino, so I
really have close to zero idea as to what’s possible in this field.</p>
<p>![Ruby on Sheep 🐑](/assets/img/articles/2020-09-04-RubyKaigi-Takeout-2020-Day-first/microcontroller.webp</p>
<h2>Running Rack and Rails Faster with TruffleRuby by Benoit Daloze</h2>
<p>Finally, in this talk, Benolt demonstrated the world that while Ruby 3 might be 3× faster than Ruby 2.0,
TruffleRuby still has much more to offer in terms of speed. I’ve personally toyed with TruffleRuby on GraalVM in the
past a bit and was absolutely blown away by the ability to compile the code into a native binary with no dependencies,
and by being able to combine Java, Ruby, Python and R programming languages in a single project.</p>
<h2>Closing words</h2>
<p>The first day of RubyKaigi Takeout 2020 finished with a 30-minute long session where Ruby committers shared their
contributions, favourite features of Ruby 3 and answered a couple of questions from the audience. I really appreciated
being able to see “behind the scenes”, to “meet” the people responsible for creating the beautiful language of Ruby.</p>
<p>The first day of the conference had me excited, yet still managed to overcome my expectations. I adored the quality of
the English interpretation service and I appreciated the fact that all talks were pre-recorded, which both avoided any
possible audio/video/connection issues as well as allowed the speakers to interact with the audience in the chat as
their speech was broadcasted.</p>
<p>I can’t wait to see what the second day of RubyKaigi Takeout 2020 will bring us!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Reverse Proxy Basics and Practice]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/09/02/Reverse-Proxy-Basics-and-Practice</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/09/02/Reverse-Proxy-Basics-and-Practice</guid>
            <pubDate>Wed, 02 Sep 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>In computer networking, a forward proxy server, often called a proxy server, is a server application or appliance that acts as an intermediary for requests from clients seeking resources from servers that provide those resources. A proxy server thus functions on behalf of the client when requesting service, potentially masking the true origin of the request to the resource server.
<img src="/assets/img/articles/2020-09-02-Reverse-Proxy-Basics-and-Practice/image-1.webp" alt=""></p>
<h2>What Is a Reverse Proxy Server?</h2>
<p>A proxy server is a go‑between or intermediary server that forwards requests for content from multiple clients to different servers across the Internet. A reverse proxy server is a type of proxy server that typically sits behind the firewall in a private network and directs client requests to the appropriate backend server. A reverse proxy provides an additional level of abstraction and control to ensure the smooth flow of network traffic between clients and servers.
<img src="/assets/img/articles/2020-09-02-Reverse-Proxy-Basics-and-Practice/image-2.webp" alt=""></p>
<h3>Common use cases of Reverse Proxy scenarios:</h3>
<p>There is a multitude of scenarios and use cases in which having a reverse proxy can make all the difference to the speed and security of your corporate network. By providing you with a point at which you can inspect traffic and route it to the appropriate server, or even transform the request, a reverse proxy can be used to achieve a variety of different goals.</p>
<h4>Load balancing</h4>
<p>A reverse proxy server can act as a traffic cop that sits in front of your backend servers and distributes client requests across a group of servers in a manner that maximizes speed and capacity utilization while ensuring no server is overloaded, which can degrade performance. If a server goes down, the load balancer redirects traffic to the remaining online servers.</p>
<h4>Web acceleration</h4>
<p>Reverse proxies can compress inbound and outbound data, as well as cache commonly requested content, both of which speed up the flow of traffic between clients and servers. They can also perform additional tasks such as SSL encryption to take load off of your web servers, thereby boosting their performance.</p>
<h4>Security and anonymity</h4>
<p>By intercepting requests headed for your backend servers, a reverse proxy server protects their identities and acts as an additional defense against security attacks. It also ensures that multiple servers can be accessed from a single record locator or URL regardless of the structure of your local area network.</p>
<h2>Let's do an experiment</h2>
<p>I'm in Chengdu, China and I want to get a file which is placed on a server in AWS Tokyo, Japan. Due to the existence of the Great Firewall, it is very slow to access AWS Tokyo's resources in China. If I download the file directly, it would cost me about two hours.</p>
<p><img src="/assets/img/articles/2020-09-02-Reverse-Proxy-Basics-and-Practice/image-3.webp" alt=""></p>
<p>Fortunately, the AWS company has three data centers located in Beijing, Ningxia, and Hong Kong. It's fast to connect any other AWS regions in the world with AWS China regions.</p>
<p><img src="/assets/img/articles/2020-09-02-Reverse-Proxy-Basics-and-Practice/image-4.webp" alt=""></p>
<p>I already have an AWS Beijing EC2 instance and I will build a simple nginx reverse proxy server to accelerate the connections. I add the configs below in <code>/etc/nginx/conf.d/proxy.conf</code> which creates a virtual server with reverse proxy.</p>
<p><img src="/assets/img/articles/2020-09-02-Reverse-Proxy-Basics-and-Practice/image-5.webp" alt=""></p>
<p>After saving the config file and restarting the nginx server, I can now download the file in AWS Tokyo via reverse proxy server at very high speed.</p>
<p><img src="/assets/img/articles/2020-09-02-Reverse-Proxy-Basics-and-Practice/image-6.webp" alt=""></p>
<h2>Summary</h2>
<p>The primary function of a reverse proxy is load balancing, ensuring that no individual backend server becomes inundated with more traffic or requests than it can handle. However, there are a number of other scenarios in which a reverse proxy can potentially offer enormous benefits. I hope you can use reverse proxy flexibly in daily work.</p>
<p>Article photo by <a href="https://www.zdnet.com/article/nbn-fibre-to-the-world/">Anton Balazh</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Laravel 8 - A rundown of new features]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/08/27/Laravel-8</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/08/27/Laravel-8</guid>
            <pubDate>Thu, 27 Aug 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Laravel 8, the brand new release of the <a href="https://laravel.com/">Laravel</a> framework is all set to launch on 8th of September. <a href="https://twitter.com/taylorotwell">Taylor Otwell</a> gave an impressive insight of what to expect in this year's <a href="https://laracon.net/">Laracon Online</a>. Due to the pandemic, over 5k developers attended it from home and watched the Live Webinar, making it the largest Laracon ever. It was packed with some amazing presentations, and the Monstarlab PHP Team was lucky to also be part of that conference.</p>
<p>Here is a quick rundown of all the features that are set to ship in Laravel 8. Details of the features will soon be out in the new Dark Mode enabled Laravel site. The addition of dark mode is also a new feature of its own for the developers to enjoy.</p>
<p><em>Small disclaimer: I might misunderstood some features while listening to the one hour presentation. But once the documentation is up, I will revisit this article and update accordingly.</em></p>
<h3>Features:</h3>
<ol>
<li>
<p><code>App/Models</code> is now default:</p>
<p><code>artisan make:model Foo</code></p>
<p>This command will now create Foo Model in the <code>app/Models</code> folder. If developers decide to delete the <code>app/Models</code> folder, and run the above command again, Laravel will understand that <code>app/Models</code> folder doesn't exist and hence will create a <code>Foo</code> model in the root folder. This feature was a great addition by Taylor after he ran a poll on Twitter, to know the community's preference.</p>
<p>Once deleted <code>app/Models</code> folder, the artisan command will make the <code>Foo</code> model in root like <code>app/Foo.php</code></p>
</li>
<li>
<p>No more magic prefixes in namespaces:</p>
<p><code>Route::get('/foo', 'FooController@index')</code></p>
<p>When <code>FooController</code> was declared, behind the scenes <code>App\Http\Controlllers\FooController</code> was added by the <code>RouteServiceProvider</code> using this function:</p>
<pre><code class="hljs language-php"><span class="hljs-keyword">protected</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mapWebRoutes</span>(<span class="hljs-params"></span>)
</span>{
    <span class="hljs-title class_">Route</span>::<span class="hljs-title function_ invoke__">middleware</span>(<span class="hljs-string">'web'</span>)
         -><span class="hljs-title function_ invoke__">namespace</span>(<span class="hljs-variable">$this</span>->namespace)
         -><span class="hljs-title function_ invoke__">group</span>(<span class="hljs-title function_ invoke__">base_path</span>(<span class="hljs-string">'routes/web.php'</span>));
}
</code></pre>
<p>But now, namespace property is trashed by default, so that if someone writes:<br>
<code>Route::get('/foo', '\App\Http\Controlllers\FooController@index')</code>
It won't cause magic prefix to be added.</p>
</li>
<li>
<p>Route Closures can now be cached:</p>
<p>Usually when route caching is enabled, a serialized output of the routes file is generated, i.e. all is compiled into a big php array. Currently routes that are defined like in the example below couldn't be cached.</p>
<pre><code class="hljs language-php"><span class="hljs-title class_">Route</span>::<span class="hljs-title function_ invoke__">get</span>(<span class="hljs-string">'foo'</span>, function () {
    <span class="hljs-keyword">return</span> <span class="hljs-string">'Hello World'</span>;
});
</code></pre>
<p>Now closures can also be cached.</p>
</li>
<li>
<p>Extended component enhancement:</p>
<p>Nested component attributes are now allowed to be merged.</p>
<pre><code class="hljs language-php"><span class="hljs-keyword">public</span> <span class="hljs-title function_ invoke__">render</span>()
{    <span class="hljs-keyword">return</span> &#x3C;&#x3C;&#x3C;<span class="hljs-string">'HTML'</span>
    &#x3C;x-button { <span class="hljs-variable">$attributes</span>-><span class="hljs-title function_ invoke__">merge</span>([<span class="hljs-string">'class'</span> => <span class="hljs-string">'bg-red'</span>]) }>
        { <span class="hljs-variable">$slot</span> }
    &#x3C;/x-button>
    HTML;
}
</code></pre>
</li>
<li>
<p>Registering event listener syntax improvement:</p>
<p>We usually do this register event listener:</p>
<pre><code class="hljs language-php"><span class="hljs-comment">/**
 * The event listener mappings for the application.
 *
 * <span class="hljs-doctag">@var</span> array
 */</span>
<span class="hljs-keyword">protected</span> <span class="hljs-variable">$listen</span> = [
    <span class="hljs-string">'App\Events\SomeEvent'</span> => [
    <span class="hljs-string">'App\Listeners\EventListener'</span>,
    ],
];
</code></pre>
<p>Now, we do not need to repeat class names, rather it is type-hinted:</p>
<pre><code class="hljs language-php">
<span class="hljs-title class_">Event</span>::<span class="hljs-title function_ invoke__">listen</span>(<span class="hljs-title class_">SomeEvent</span>::<span class="hljs-variable language_">class</span>, function(SomeEvent <span class="hljs-variable">$event</span>) {
    <span class="hljs-title function_ invoke__">info</span>(<span class="hljs-variable">$event</span>->whatever)
});

<span class="hljs-title class_">Event</span>::<span class="hljs-title function_ invoke__">listen</span>(function (SomeEvent <span class="hljs-variable">$event</span>) {
    <span class="hljs-title function_ invoke__">info</span>(<span class="hljs-title function_ invoke__">get_class</span>(<span class="hljs-variable">$event</span>))
});

</code></pre>
</li>
<li>
<p>Addition of queuable anonymous event listeners</p>
<p>We can queue Model events now in Models:</p>
<pre><code class="hljs language-php"><span class="hljs-comment">//in Foo Model Class</span>

<span class="hljs-keyword">protected</span> <span class="hljs-built_in">static</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">booting</span>(<span class="hljs-params"></span>)
</span>{
    <span class="hljs-built_in">static</span>::<span class="hljs-title function_ invoke__">created</span>(<span class="hljs-title function_ invoke__">queuable</span>(function (Foo <span class="hljs-variable">$foo</span>){
        <span class="hljs-title function_ invoke__">info</span>(<span class="hljs-string">'Queued: '</span> <span class="hljs-variable">$foo</span>->name);
    }))
}
</code></pre>
</li>
<li>
<p>Secret maintenance mode</p>
<p><code>artisan down —secret=laracon-2020</code></p>
<p>IP whitelisting is very difficult. Now we can access route as secret. During maintenance mode, when the server is down, using the command mentioned above we can start accessing the application and routes. This will be valid for several hours even in maintenance mode. There will be several options available in the artisan down command like: <code>render</code>, <code>redirect</code>, <code>status</code> and <code>secret</code> to ensure much more control.</p>
</li>
<li>
<p>New catch method for anonymous queue function:</p>
<p>There is a new catch method for anonymous queue function. Initially it went to failed jobs, now we can have a callback to catch that.</p>
<pre><code class="hljs language-php"><span class="hljs-title class_">Route</span>::<span class="hljs-title function_ invoke__">get</span>(<span class="hljs-string">'/queue-catch'</span>, function(){
    <span class="hljs-title function_ invoke__">dispatch</span>(function() {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Exception</span>(<span class="hljs-string">'Something went wrong...'</span>);
    })-><span class="hljs-keyword">catch</span>(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"><span class="hljs-built_in">Throwable</span> <span class="hljs-variable">$e</span></span>) </span>{
        <span class="hljs-title function_ invoke__">info</span>(<span class="hljs-string">'Caught exception'</span>);
    });

    <span class="hljs-keyword">return</span> <span class="hljs-string">'Dispatched'</span>;
});
</code></pre>
</li>
<li>
<p>Exponential backoff job class:</p>
<pre><code class="hljs language-php">   <span class="hljs-title class_">BackoffJob</span>::<span class="hljs-title function_ invoke__">dispatch</span>();
</code></pre>
<p>We can now exponentially increase backoff retries, using a simple declaration:</p>
<pre><code class="hljs language-php">   <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">backoff</span>(<span class="hljs-params"></span>)
   </span>{
     <span class="hljs-keyword">return</span> [<span class="hljs-number">1</span>, <span class="hljs-number">5</span>];
   }
</code></pre>
</li>
<li>
<p>Job Batching:</p>
<p>Job Batching, has been modelled after Ruby's <a href="https://github.com/mperham/sidekiq">Sidekiq</a>.
Now we can queue a bunch of jobs that triggers at the same time, and then it will trigger callbacks after the entire batch is finished.</p>
<pre><code class="hljs language-php"> <span class="hljs-title class_">Bus</span>::<span class="hljs-title function_ invoke__">batch</span>([
     <span class="hljs-keyword">new</span> <span class="hljs-title class_">BatchedJob</span>,
     <span class="hljs-keyword">new</span> <span class="hljs-title class_">BatchedJob</span>,
     <span class="hljs-keyword">new</span> <span class="hljs-title class_">BatchedJob</span>,
     <span class="hljs-keyword">new</span> <span class="hljs-title class_">BatchedJob</span>,
     <span class="hljs-keyword">new</span> <span class="hljs-title class_">BatchedJob</span>,
 ])-><span class="hljs-title function_ invoke__">then</span>(function (Batch <span class="hljs-variable">$batch</span>) {
        <span class="hljs-title function_ invoke__">info</span>(<span class="hljs-string">'All Jobs completed Successfully'</span>);
 })-><span class="hljs-keyword">catch</span>(
        <span class="hljs-title function_ invoke__">info</span>(<span class="hljs-string">'First Batch job failure detected'</span>);
 )-><span class="hljs-keyword">finally</span>(
        <span class="hljs-title function_ invoke__">info</span>(<span class="hljs-string">'The batch has finished executing'</span>);
 )-><span class="hljs-title function_ invoke__">dispatch</span>();
</code></pre>
<p>Failed items also can be caught without interuption.</p>
<p>Real time progress can be seen of this batched jobs:</p>
<pre><code class="hljs language-php"><span class="hljs-title class_">Bus</span>::<span class="hljs-title function_ invoke__">findBatch</span>(<span class="hljs-string">'&#x3C;id of the batch>'</span>)
</code></pre>
</li>
<li>
<p>Rate limiting improved:</p>
<p>New global rate limiting facade (like auth gates) is introduced:</p>
<pre><code class="hljs language-php">[<span class="hljs-string">'throttle:global'</span>], [<span class="hljs-string">'throttle:nested'</span>],

<span class="hljs-comment">// in configure functions</span>
<span class="hljs-title class_">Limit</span>::<span class="hljs-title function_ invoke__">perMinute</span>(<span class="hljs-number">100</span>);
<span class="hljs-title class_">Limit</span>::<span class="hljs-title function_ invoke__">perMinute</span>(<span class="hljs-number">3</span>)-><span class="hljs-title function_ invoke__">by</span>(<span class="hljs-variable">$request0</span>-><span class="hljs-title function_ invoke__">input</span>(<span class="hljs-string">'email'</span>));
</code></pre>
</li>
<li>
<p>Custom Exceptions:</p>
<p>Report and render methods can be ditched and there is a new way of declaring custom exceptions:</p>
<pre><code class="hljs language-php"><span class="hljs-variable language_">$this</span>-><span class="hljs-title function_ invoke__">reportable</span>(function (AppException <span class="hljs-variable">$e</span>) {
    <span class="hljs-title function_ invoke__">info</span>(<span class="hljs-string">'error'</span>);
})-><span class="hljs-title function_ invoke__">stop</span>();

<span class="hljs-variable language_">$this</span>-><span class="hljs-title function_ invoke__">renderable</span>(function (AppException <span class="hljs-variable">$e</span>) => … );
</code></pre>
</li>
<li>
<p>Squash migrations:</p>
<p><code>artisan schema:dump</code></p>
<p>This will now generate the entire existing migration into a schema file. After running this command, if we run any new migrations, we can then again run the same command. This will then generate a new one into a single new migration. It is basically for development mode, like one file with 50 migrations squashed.</p>
</li>
<li>
<p>Factories are re-written:</p>
<p>They are class based and they are like factories on steroids, pretty much everything can be done, like creating data with relationships, for one specific id, etc. A short note for the functions are given below:</p>
<pre><code class="hljs language-php"><span class="hljs-comment">// new  definition function</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">definition</span>(<span class="hljs-params"></span>)
</span>{
    <span class="hljs-keyword">return</span> [
        <span class="hljs-string">'name'</span> => …blah blah
    ]
}

 <span class="hljs-comment">// same</span>
<span class="hljs-title class_">Foo</span>::<span class="hljs-title function_ invoke__">factory</span>()-><span class="hljs-title function_ invoke__">create</span>(),
<span class="hljs-title class_">Foo</span>::<span class="hljs-title function_ invoke__">factory</span>()-><span class="hljs-title function_ invoke__">create</span>([]);

<span class="hljs-comment">// custom functions</span>
<span class="hljs-title class_">Foo</span>::<span class="hljs-title function_ invoke__">factory</span>()-><span class="hljs-title function_ invoke__">withStatus</span>(<span class="hljs-string">'draft'</span>)-><span class="hljs-title function_ invoke__">create</span>()
<span class="hljs-comment">// which is:</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withStatus</span>(<span class="hljs-params"></span>)
</span>{
    <span class="hljs-keyword">return</span> <span class="hljs-variable language_">$this</span>-><span class="hljs-title function_ invoke__">state</span>(fn (<span class="hljs-variable">$attributes</span>) =>
    [<span class="hljs-string">'status'</span> => <span class="hljs-string">''</span>];
)};

<span class="hljs-comment">//relationships of all kinds can be used in factory</span>
<span class="hljs-title class_">Foo</span>::<span class="hljs-title function_ invoke__">factory</span>()
-><span class="hljs-title function_ invoke__">times</span>(<span class="hljs-number">3</span>)
-><span class="hljs-title function_ invoke__">has</span>(<span class="hljs-title class_">Bar</span>::<span class="hljs-title function_ invoke__">factory</span>()
    -><span class="hljs-title function_ invoke__">times</span>(<span class="hljs-number">3</span>)
    -><span class="hljs-title function_ invoke__">state</span>(<span class="hljs-string">'draft'</span>))
-><span class="hljs-title function_ invoke__">create</span>()
-><span class="hljs-title function_ invoke__">load</span>(<span class="hljs-string">'fooBar'</span>);
</code></pre>
<p>Legacy factory package will be available to support existing ones.</p>
</li>
<li>
<p>Laravel JetStream:</p>
<p>Free package, it is more that any general auth scaffolding. Laravel JetStream can be used with <a href="https://laravel-livewire.com/">Livewire</a>/<a href="https://inertiajs.com/">Inertia</a>, and has several big punches in store to woo the developer crowd for sure.</p>
</li>
</ol>
<p>I hope you enjoyed this quick rundown of the new features. Huge shout out to the entire community for the tweets, you can follow most here at <a href="https://twitter.com/LaraconOnline">LaraconOnline</a>. Feel free to poke me <a href="https://twitter.com/saadbinamjad">@saadbinamjad</a> if I missed any features. Till then, happy coding folks!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Let it flow]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/08/12/Let-it-Flow</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/08/12/Let-it-Flow</guid>
            <pubDate>Wed, 12 Aug 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>I've been working with <a href="https://kotlinlang.org/docs/reference/coroutines/flow.html">Flow</a> on production for almost a year now. The light weight kotlin coroutines stream library has completely replaced RxJava for our new projects. RxJava is still a trend for Android Development even in 2020. But this article is not another RxJava x Coroutines. It is here to show that you can use Flow with almost anything in the Android framework.</p>
<p>Similar to when RxJava started to become a trend in Android development, there were also tons of libraries that would make anything on Android into an RxJava observer. RxBindings, RxActivityResult, RxPermissions, RxSharedPreferences, you name it. Can the same be done using Flow? Yes of course. And with the power of Kotlin, easier, more comprehensible and lighter.</p>
<p>Before we begin I would like to make a small disclaimer. Using flow for these scenarios can be an overkill and we don't actually need any threading for the below examples. If the scope is not handled properly it can cause memory leaks.</p>
<p>Gradle setup:</p>
<pre><code class="hljs language-groovy">    dependencies {
        ...
        implementation <span class="hljs-string">"org.jetbrains.kotlinx:kotlinx-coroutines-core:${coroutinesVersion}"</span>
        implementation <span class="hljs-string">"org.jetbrains.kotlinx:kotlinx-coroutines-android:${coroutinesVersion}"</span>
        <span class="hljs-comment">// Not required, used in this example to use `lifecycleScope` available from version 2.2.0 and onwards</span>
        implementation <span class="hljs-string">"androidx.lifecycle:lifecycle-runtime-ktx:${lifecycleVersion}"</span>
    }
</code></pre>
<p>Let's think of a simple example first: <code>Click listener</code>.</p>
<pre><code class="hljs language-kotlin">    <span class="hljs-function"><span class="hljs-keyword">fun</span> View.<span class="hljs-title">onClickFlow</span><span class="hljs-params">()</span></span>: Flow&#x3C;View> {
        <span class="hljs-keyword">return</span> callbackFlow {
            setOnClickListener {
                offer(it)
            }
            awaitClose { setOnClickListener(<span class="hljs-literal">null</span>) }
        }
    }
</code></pre>
<pre><code class="hljs language-kotlin">    lifecycleScope.launch {
        btn.onClickFlow()
            .collect { view ->
                Toast.makeText(view.context, <span class="hljs-string">"Clicked"</span>, Toast.LENGTH_SHORT).show()
            }
    }
</code></pre>
<p><img src="/assets/img/articles/2020-08-12-Let-it-Flow/clickflow.webp" alt="clickflow"></p>
<p>Now we can use all Flow operators on a view click listener.
Let's try with a <code>TextWatcher</code>:</p>
<pre><code class="hljs language-kotlin">    <span class="hljs-function"><span class="hljs-keyword">fun</span> EditText.<span class="hljs-title">afterTextChangedFlow</span><span class="hljs-params">()</span></span>: Flow&#x3C;Editable?> {
        <span class="hljs-keyword">return</span> callbackFlow {
            <span class="hljs-keyword">val</span> watcher = <span class="hljs-keyword">object</span> : TextWatcher {
                <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">afterTextChanged</span><span class="hljs-params">(s: <span class="hljs-type">Editable</span>?)</span></span> {
                    offer(s)
                }

                <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">beforeTextChanged</span><span class="hljs-params">(s: <span class="hljs-type">CharSequence</span>?, start: <span class="hljs-type">Int</span>, count: <span class="hljs-type">Int</span>, after: <span class="hljs-type">Int</span>)</span></span> {}

                <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onTextChanged</span><span class="hljs-params">(s: <span class="hljs-type">CharSequence</span>?, start: <span class="hljs-type">Int</span>, before: <span class="hljs-type">Int</span>, count: <span class="hljs-type">Int</span>)</span></span> {}
            }

            addTextChangedListener(watcher)
            awaitClose { removeTextChangedListener(watcher) }
        }
    }
</code></pre>
<pre><code class="hljs language-kotlin">    lifecycleScope.launch {
        editText.afterTextChangedFlow()
            .collect {
                textView.text = it
            }
    }
</code></pre>
<p><img src="/assets/img/articles/2020-08-12-Let-it-Flow/textwatcherflow.webp" alt="textwacherflow"></p>
<p>Now we can add <a href="https://rxmarbles.com/#debounce">debounce</a>, filter, map, whatever we want, let's try it.</p>
<pre><code class="hljs language-kotlin">    lifecycleScope.launch {
        editText.afterTextChangeFlow()
            .debounce(<span class="hljs-number">1000</span>)
            .collect {
                textView.text = it
            }
    }
</code></pre>
<p><img src="/assets/img/articles/2020-08-12-Let-it-Flow/textwatcherdebounce.webp" alt="textwatcherdebounce"></p>
<p>Adding <code>debounce</code> makes the flow wait an amount of time before emitting values. This is a very common use case for search operations that require network requests as an example.</p>
<p>Enough with <code>View</code> flows, let's try applying the same concept on <code>SharedPreferences</code>:</p>
<pre><code class="hljs language-kotlin">    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-type">&#x3C;T></span> SharedPreferences.<span class="hljs-title">observeKey</span><span class="hljs-params">(
        key: <span class="hljs-type">String</span>,
        default: <span class="hljs-type">T</span>
    )</span></span>: Flow&#x3C;T> {
        <span class="hljs-keyword">return</span> callbackFlow {
            send(getItem(key, default))

            <span class="hljs-keyword">val</span> listener = SharedPreferences.OnSharedPreferenceChangeListener { _, k ->
                <span class="hljs-keyword">if</span> (key == k) {
                    offer(getItem(key, default))
                }
            }

            registerOnSharedPreferenceChangeListener(listener)
            awaitClose {
                unregisterOnSharedPreferenceChangeListener(listener)
            }
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-type">&#x3C;T></span> SharedPreferences.<span class="hljs-title">getItem</span><span class="hljs-params">(key: <span class="hljs-type">String</span>, default: <span class="hljs-type">T</span>)</span></span>: T {
        <span class="hljs-meta">@Suppress(<span class="hljs-string">"UNCHECKED_CAST"</span>)</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">when</span> (default) {
            <span class="hljs-keyword">is</span> String -> getString(key, default) <span class="hljs-keyword">as</span> T
            <span class="hljs-keyword">is</span> <span class="hljs-built_in">Int</span> -> getInt(key, default) <span class="hljs-keyword">as</span> T
            <span class="hljs-keyword">is</span> <span class="hljs-built_in">Long</span> -> getLong(key, default) <span class="hljs-keyword">as</span> T
            <span class="hljs-keyword">is</span> <span class="hljs-built_in">Boolean</span> -> getBoolean(key, default) <span class="hljs-keyword">as</span> T
            <span class="hljs-keyword">is</span> <span class="hljs-built_in">Float</span> -> getFloat(key, default) <span class="hljs-keyword">as</span> T
            <span class="hljs-keyword">is</span> Set&#x3C;*> -> getStringSet(key, default <span class="hljs-keyword">as</span> Set&#x3C;String>) <span class="hljs-keyword">as</span> T
            <span class="hljs-keyword">is</span> MutableSet&#x3C;*> -> getStringSet(key, default <span class="hljs-keyword">as</span> MutableSet&#x3C;String>) <span class="hljs-keyword">as</span> T
            <span class="hljs-keyword">else</span> -> <span class="hljs-keyword">throw</span> IllegalArgumentException(<span class="hljs-string">"generic type not handled"</span>)
        }
    }
</code></pre>
<pre><code class="hljs language-kotlin">    lifecycleScope.launch {
        launch {
            repeat(<span class="hljs-number">10</span>) {
                delay(<span class="hljs-number">300</span>)
                sharedPreferences.edit { putString(<span class="hljs-string">"key"</span>, <span class="hljs-string">"Counting <span class="hljs-variable">$it</span>"</span>) }
            }
        }
        sharedPreferences.observeKey(<span class="hljs-string">"key"</span>, <span class="hljs-string">""</span>)
            .collect { string ->
                textView.text = string
            }
    }
</code></pre>
<p><img src="/assets/img/articles/2020-08-12-Let-it-Flow/sharedpreferenceflow.webp" alt="sharedpreferenceflow"></p>
<p>Super easy right? How about <code>BroadcastReceiver</code>? Why not.</p>
<pre><code class="hljs language-kotlin">    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">broadcastReceiverFlow</span><span class="hljs-params">(c: <span class="hljs-type">Context</span>, intentFilter: <span class="hljs-type">IntentFilter</span>)</span></span>: Flow&#x3C;Intent> {
        <span class="hljs-keyword">return</span> callbackFlow {
            <span class="hljs-keyword">val</span> broadcastReceiver = <span class="hljs-keyword">object</span> : BroadcastReceiver() {
                <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onReceive</span><span class="hljs-params">(context: <span class="hljs-type">Context</span>, intent: <span class="hljs-type">Intent</span>)</span></span> {
                    offer(intent)
                }
            }
            c.registerReceiver(broadcastReceiver, intentFilter)
            awaitClose {
                c.unregisterReceiver(broadcastReceiver)
            }
        }
    }
</code></pre>
<p>That's all for today's blog post, see you soon!</p>
<p>Article photo by <a href="https://www.teepublic.com/user/spacecityspin">SpaceCitySpin</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Exploratory Testing]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/07/28/Exploratory-Testing</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/07/28/Exploratory-Testing</guid>
            <pubDate>Tue, 28 Jul 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Exploratory testing (ET) is an important testing method in the agile world. As a research tool, it is an important supplement to user story testing and automated regression sets. It does not have many practical test methods, techniques and tools, but it is a test thinking mode that all testers should master. Emphasize the subjective initiative of testers, abandon the complicated test plan and test case design process, and emphasize the timely change of test strategy when encountering problems.</p>
<h3>The particularities of exploratory testing</h3>
<p>Script testing: Design test cases first, and then execute testing according to the test cases.</p>
<p>Exploratory testomg：Do not design test cases in advance, and consider whether there is a problem elsewhere while performing the test. If you find a problem, focus on testing this part.</p>
<h3>The advantages and disadvantages of ET</h3>
<h4>The advantages</h4>
<ul>
<li>The tester spends more time to test some interesting or complicated situations</li>
<li>The tester can evaluate the system quickly</li>
<li>The test can be flexible</li>
<li>More interesting than script testing</li>
</ul>
<h4>The disadvantages</h4>
<ul>
<li>It is hard to be adjusted</li>
<li>Unable to do a comprehensive test of the system</li>
<li>Rely on the knowledge and technology of the tester</li>
<li>Not applicable for a long test</li>
</ul>
<h3>The main purpose of the ET</h3>
<ul>
<li>
<p>Supplement of coverage</p>
<ul>
<li>As an additional test after the classic script tests, it confirms whether the functionality tested by ET is covered by the script tests</li>
</ul>
</li>
<li>
<p>Early detection of bugs</p>
<ul>
<li>When executed as a smoke test, implement before a lengthy script test to prevent late rework</li>
</ul>
</li>
<li>
<p>Reduce costs of test maintenance</p>
<ul>
<li>During agile development, the cost of changing script tests is high, according exploration testing can reduce costs</li>
</ul>
</li>
</ul>
<h3>The steps of ET</h3>
<h4>1. Preparation</h4>
<ul>
<li>
<p>Establish a test plan</p>
<ul>
<li>2 ways to implement exploratory testing
<ol>
<li>Split screen: each screen decides who is responsible</li>
<li>Based on the scene: select the favorite scene for testing</li>
</ol>
</li>
</ul>
</li>
<li>
<p>Reserve of product knowledge/bug knowledge</p>
<ul>
<li>Understand the style, understand the product's past bugs</li>
</ul>
</li>
<li>
<p>Make sure you're familiar with common testing scenarios</p>
<ol>
<li>
<p>Knowledge of bug detection methods</p>
</li>
</ol>
<ul>
<li>
<p>Input value: maximum and minimum, decimal, maximum number of characters exceeded, greater than maximum, less than minimum, empty text, space, ellipses, null, non-existent time, special character, xss character.</p>
</li>
<li>
<p>Boundary: space boundary, time boundary.</p>
</li>
<li>
<p>Status: After initialization, the status of adding a large amount of data, etc.</p>
</li>
</ul>
<ol start="2">
<li>
<p>Special operations</p>
</li>
</ol>
<ul>
<li>Perform operations quickly, upload files in weak network environment, etc.</li>
</ul>
</li>
</ul>
<h4>2. Implementation</h4>
<ul>
<li>
<p>Time required</p>
<ul>
<li>The key point is short-term concentration test</li>
<li>If you need a long time, try to control it within 30 min</li>
</ul>
</li>
<li>
<p>Test log</p>
<ul>
<li>No detailed log</li>
<li>When a bug is found, record it</li>
</ul>
</li>
</ul>
<h4>3. Report</h4>
<ul>
<li>
<p>Summarize test results and report</p>
<ul>
<li>Report matters</li>
<li>Who is responsible for which picture/which scene
<ul>
<li>Time required</li>
<li>Bug checklist</li>
</ul>
</li>
</ul>
</li>
<li>
<p>Arrange the detailed reproduction steps of the bug and submit bugs</p>
<ul>
<li>If it cannot be reproduced, report it as risk</li>
</ul>
</li>
<li>
<p>Report other concerns</p>
<ul>
<li>Problems such as user medical examination improvement</li>
</ul>
</li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Alibaba Seata]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/07/28/Seata</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/07/28/Seata</guid>
            <pubDate>Tue, 28 Jul 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Seata is an open source distributed transaction solution that delivers high performance and easy to use distributed transaction services under a microservices architecture.</p>
<h2>1.Overview</h2>
<p>A distributed transaction solution with high performance and ease of use for microservices architecture.</p>
<h2>2.Transaction</h2>
<p>A transaction is a unit of operations in which all operations eventually behave in a consistent manner, either all succeed or all are undone. In short, transactions provide a "do nothing or do it all" mechanism.</p>
<h4>2.1 Local transaction</h4>
<ul>
<li>A local transaction can be thought of as a transaction mechanism provided by a database. When it comes to database transactions, there are four features of database transactions:
<ul>
<li>A：Atomicity，All operations in a transaction either successful all or fall all.</li>
<li>C：Consistency，The database must be in a consistent state before and after a transaction executes.</li>
<li>I：Isolation，In a concurrent environment, when different transactions operate on the same data at the same time, the transactions do not affect each other.</li>
<li>D：Durability，This means that any updates to the database made by the transaction must be saved permanently as soon as the transaction completes successfull</li>
</ul>
</li>
<li>A database transaction is implemented in such a way that all the operations involved in a transaction are grouped into an indivisible unit of execution in which all operations either succeed or fail, and any failure of either operation results in a rollback of the entire transaction</li>
</ul>
<h4>2.2 Distributed transaction</h4>
<p>Distributed transactions mean that the participants of a transaction, the server supporting the transaction, the resource server, and the transaction manager are located on different nodes in different distributed systems.
<em>Simply put, a large operation consists of small operations that are distributed on different servers and belong to different applications, and distributed transactions need to ensure that these small operations either all succeed or all fail.</em>
<em>In essence, distributed transactions are to ensure the consistency of data between different databases.</em></p>
<h2>3.The introduction of Seata</h2>
<p>Seata was designed to be non-intrusive, so it started with the business-non-intrusive 2PC solution and evolved from the traditional 2PC. It understands a distributed transaction as a global transaction consisting of several branch transactions. The responsibility of a global transaction is to coordinate the branch transactions under its jurisdiction to reach an agreement that either commits successfully together or fails to roll back together. In addition, usually a branch transaction is itself a local transaction of a relational database.</p>
<p>Seata consists mainly of three important components:</p>
<ul>
<li>TC：Transaction Coordinator. Transaction coordinator, which manages the state of global branch transactions and is used for commit and rollback of global transactions.</li>
<li>TM：Transaction Manager. Transaction manager, used to start, commit, or roll back global transactions.</li>
<li>RM：Resource Manager. Resource manager, used for resource management on branch transactions, registers branch transactions with TC, reports the status of branch transactions, and accepts TC's command to commit or rollback branch transactions.
<img src="/assets/img/articles/2020-07-28-Alibaba-Seata/image1.webp" alt=""></li>
</ul>
<p><em>TM notifies the TC to start a new global transaction. TC generates an XID that represents the global transaction.</em></p>
<p><em>XID is propagated down the chain of invocation of the microservice.</em></p>
<p><em>The RM registers the local transaction as a branch of the corresponding global transaction from XID to TC.</em></p>
<p><em>TM notifies TC to commit or roll back the global transaction corresponding to the XID</em></p>
<p><em>The TC drives all branch transactions under the corresponding global transaction of the XID to complete branch commit or rollback.</em></p>
<h3>AT mode:</h3>
<p>The AT pattern is designed based on the two-phase commit pattern to solve the distributed transaction problem in the microservice scenario in an efficient and non-invasive way. It enables application code to use distributed transactions as if they were local transactions, completely masking the underlying details</p>
<p>In the AT mode, each database is called a "Resource", but in Seata it is called a "DataSource Resource". When a business accesses database resources through the JDBC standard interface, the Seata framework intercepts all requests and does something. When each local Transaction commits, Seata RM (Resource Manager) registers a branch Transaction with the TC (Transaction Coordinator). When the request link call is complete, the initiator notifies the TC to commit or roll back the distributed transaction, entering the two-phase invocation process. At this point, the TC will call back to the corresponding actor to execute the second phase of the corresponding resource based on the previously registered branch transaction. How does TC find the relationship between a branch transaction and a resource? Each resource has a globally unique resource ID and registers the resource with TC at initialization time. At run time, each branch transaction is registered with its resource ID. This will enable the TC to find the right resource during the two-phase invocation. Each branch transaction is registered with its resource ID. This will enable the TC to find the right resource during the two-phase invocation.</p>
<p><strong>One stage process</strong></p>
<p>In one stage, Seata will intercept business SQL. First, SQL semantics will be parsed to find the business data to be updated by business SQL. Before the business data is updated, it will be saved as before image. All of the above operations are done within a database transaction, thus ensuring atomicity of the one-phase operation.</p>
<p><img src="/assets/img/articles/2020-07-28-Alibaba-Seata/image2.webp" alt=""></p>
<p>The specific work of the branch transaction in the first phase includes:</p>
<p>1.Generate the corresponding SQLizer based on the SQL (UPDATE, INSERT, DELETE) type that needs to be executed</p>
<p>2.Generate the corresponding SqlExecutor</p>
<p>3.Enter the before and after snapshots of the core logic query data, such as the red part of the figure, take the before and after snapshots of the modified data row, integrate the two to generate UndoLog, and try to commit them in the same transaction as the business changes.</p>
<p><strong>The two-phase Commit process</strong></p>
<p>The second phase of the AT pattern determines whether a global commit or a global rollback operation will occur based on the first phase. For the service side, wait until a stage do not throw exceptions, global transaction sponsors will be submitted to the server application this global transaction, the server according to the xid query out the global transaction after the lock and close the global transaction, the purpose is to prevent the transaction follow-up and branch continued to register, and amend the state from the Begin to Committing.</p>
<p>Next, determine whether all the branch types under the global transaction are AT types. If so, the server will commit asynchronously, because the data completed in the next phase of AT mode has been landed. Server only modify global transaction status to AsyncCommitting, then there will be a regular thread pool to query in the storage medium (File or Database) to be submitted by the global transaction log to commit, if a global transaction submitted successfully will release global lock and remove the transaction log. The whole process is shown in the following figure:</p>
<p><img src="/assets/img/articles/2020-07-28-Alibaba-Seata/image3.webp" alt=""></p>
<p>If all Branch RMS execute successfully, then global Commit is performed. Since we don't have to roll back, and each Branch local database operation is done, the main thing we do is delete the local Undolog.</p>
<p>The client receives a Branch Commit request from the server, finds the appropriate ResourceManager according to resourceId, and then encapsulates the branch commit request into a Phase2Context and inserts the ASYNC_COMMIT_BUFFER into the memory queue. The client has a timed thread pool to query the queue for asynchronous deletion of UndoLog.</p>
<p>Once the client commit fails or the RPC timeout occurs, the server sets the global transaction state to the CommitRetrying position and another timed thread pool is used to retry the transactions until they succeed.</p>
<p><strong>Two-stage Rollback process</strong></p>
<p>If the second phase is rolled back, Seata needs to roll back the business SQL that has been executed in the first phase to restore the business data. The rollback method is to restore business data with before image. However, the dirty write should be verified before restoration. Compare the current business data of the database with after image. If the two data are completely consistent, it means that there is no dirty write, and the business data can be restored.</p>
<p><img src="/assets/img/articles/2020-07-28-Alibaba-Seata/image4.webp" alt=""></p>
<p>If the initiator throws an exception in one phase and requests the server to roll back the global transaction, the server queries the global transaction based on the XID, locks the transaction so that no more branches are registered, changes its state to Begin as Rollbacking, and then synchronously rolls back to ensure data consistency. With the exception of the synchronous rollback point, the process is similar to commit time, freeing the global lock and deleting the transaction log if the synchronous rollback is successful, and asynchronously retrying if it fails.</p>
<p>The client receives A Branch Rollback request from the server and is resourceId the corresponding data source agent. After querying the UndoLog record against xID and branchId, the rollback field is deserialized and given A before and after snapshot of the data. We call the global transaction A.</p>
<p>According to specific SQL type to generate corresponding UndoExecutor, check whether the data before and after snapshots of UndoLog consistent or front-facing snapshots and current data whether, if don't need to do a rollback operation, consistent if inconsistent reverse SQL generated for the compensation, before submitting a local transaction will detect local access database lock the success and failure is that there are other global transaction (if called B) of one phase is to modify the same line, But because of the bank's primary key on the server has been A global transaction is currently executing two phase rollback A lock, so the transaction B A stage in front of the local submission to attempt to obtain the global lock must be failed, wait to get the global lock timeout after A global transaction B will release the local lock, so that A global affairs can continue local transaction submission, delete local UndoLog records after success.</p>
<p><strong>TCC mode:</strong></p>
<p>Try：Detection and reservation of resources;</p>
<p>Confirm：Commits the business operation performed; If "Try" succeed and "Confirm" must succeed;</p>
<p>Cancel：Reserve resource release.</p>
<p>TCC mode requires users to implement Try, Confirm, and Cancel based on their own business scenario. The transaction initiator performs a Try mode in one phase, commits the Confirm method in the second, and rolls back the Cancel method in the second phase.</p>
<p>The most important thing for users accessing the TCC schema is to consider how to break down the business model into two phases, implement the three methods of TCC, and ensure that Try success Confirm success. Compared with AT mode, TCC mode is somewhat invasive to business code, but TCC mode has much higher performance than AT mode without global row lock of AT mode.</p>
<p><img src="/assets/img/articles/2020-07-28-Alibaba-Seata/image5.webp" alt=""></p>
<p><strong>Saga mode:</strong></p>
<p>In Saga mode, there are multiple participants in distributed transactions, and each participant is a Rectification compensation service, requiring users to implement forward operation and reverse rollback operation according to business scenarios.</p>
<p>During the execution of a distributed transaction, the forward operations of each participant are executed in turn. If all forward operations are successfully executed, then the distributed transaction commits. If any forward operation fails, the distributed transaction will roll back the previous actor's backward rollback and roll back the committed actor, bringing the distributed transaction back to its original state.</p>
<p><img src="/assets/img/articles/2020-07-28-Alibaba-Seata/image6.webp" alt=""></p>
<p><strong>XA mode</strong>:</p>
<p>XA schema is another non-invasive distributed transaction solution. Any database that implements XA protocol can participate in distributed transactions as a resource. Currently, mainstream databases such as MySql, Oracle, DB2, Oceanbase and others all support XA protocol.</p>
<p><strong>One stage process</strong></p>
<p>In one phase of XA mode, Seata intercepts Business SQL, starts XA Transactions (" XA Start ") before Business SQL, then executes Business SQL to end XA Transactions (" XA End "), and finally precommits XA transactions (" XA Prepare ") to complete the preparation of Business SQL.</p>
<p><strong>Two-stage submission:</strong></p>
<p>Execute the "XA Commit" directive to commit the XA transaction, at which point the "business SQL" is actually committed to the database.</p>
<p><strong>Two-stage rollback:</strong></p>
<p>Execute the "xa rollback" instruction, rollback xa transactions, complete the "business SQL" rollback, and release database lock resources.</p>
<p>In XA mode, users focus only on "business SQL," and Seata automatically generates phase 1, phase 2 commits and phase 2 rollbacks. The XA pattern, like the AT pattern, is a business-neutral solution; But unlike the AT mode, the XA mode delegates things like snapshot data and row locks to the database via XA instructions, making the XA mode implementation more lightweight.</p>
<p><img src="/assets/img/articles/2020-07-28-Alibaba-Seata/image7.webp" alt=""></p>
<p>More in <a href="http://seata.io">http://seata.io </a> <a href="https://github.com/seata/seata">https://github.com/seata/seata</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Using MockWebServer On Android]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/07/24/using-mockwebserver-on-android</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/07/24/using-mockwebserver-on-android</guid>
            <pubDate>Fri, 24 Jul 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>At some point in your code development you'll want to test how the interaction with your app and the API server is being handled, but testing with a real connection with a server is expensive and unstable.
Unit tests should be real quick and assert if your code is working as it should. When you introduce a real connection, many problems can show up: long delay on the response, offline server, API changes that were not supposed to be there. All these can raise problems in your tests that are not on your end. The code you wrote can't grant that the server will be online, so it's not something you should be testing in your code.</p>
<p>So, how to solve this? The answer is simple: simulate a real server connection with the expected responses, for which you know how your code should behave and what's the expected result. For this, I'm going to show a very simple usage of <code>MockWebServer</code> that can give you an idea of how to use it in your own tests.</p>
<h2>Setting up the dependencies</h2>
<p>In this demo I'm going to use the following dependencies:</p>
<p><strong>MockWebServer:</strong> The very reason of this article. It'll be used to simulate a real server api with the responses we set.
<strong>Retrofit:</strong> A very known library that will be used for making the requests with the fake server.
<strong>Moshi:</strong> Deserialization library to transform the JSON responses in objects.</p>
<p>These are the dependencies needed to be added in the code:</p>
<pre><code class="hljs language-groovy">testImplementation <span class="hljs-string">'com.squareup.okhttp3:mockwebserver:4.6.0'</span>
implementation <span class="hljs-string">'com.squareup.retrofit2:converter-moshi:2.8.1'</span>
implementation <span class="hljs-string">'com.squareup.retrofit2:retrofit:2.8.1'</span>
implementation <span class="hljs-string">'com.squareup.moshi:moshi-kotlin:1.9.3'</span>
</code></pre>
<h2>Some help</h2>
<p>For this demo I've created a <a href="https://github.com/Tgo1014/MockWebServerSample/blob/master/app/src/test/java/tgo1014/mockwebserver/Helper.kt">Helper</a> class that will make some things easier for us. I won't talk about details of what it does but if you're curious you can check the comments in the <a href="https://github.com/Tgo1014/MockWebServerSample">sample repository</a>.</p>
<h2>Preparing the tests</h2>
<p>The first thing we need is getting some instance of the <code>MockWebServer</code> and starting it before and shutting it down after the tests, we can do it like this:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MockWebServerExampleTest</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> mockWebServer = MockWebServer()
    <span class="hljs-meta">@Before</span>
    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span></span> {
        mockWebServer.start(<span class="hljs-number">8080</span>)
    }
    <span class="hljs-meta">@After</span>
    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">shutdown</span><span class="hljs-params">()</span></span> {
        mockWebServer.shutdown()
    }
}
</code></pre>
<p>Simple, right?</p>
<p>We also need an instance of the API, you can get it on this demo like this:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> api: Api <span class="hljs-keyword">by</span> lazy { generateRetrofit(mockWebServer).create(Api::<span class="hljs-keyword">class</span>.java) }
</code></pre>
<p>This uses a method of the <a href="https://github.com/Tgo1014/MockWebServerSample/blob/master/app/src/test/java/tgo1014/mockwebserver/Helper.kt">Helper</a> class I've added in the project, so make sure to check it out to fully understand what's happening but basically we are just creating a instance of Retrofit using our fake server and getting an instance of the Api class where we have the endpoints.</p>
<h2>Setting up the responses</h2>
<p>After we have the <code>MockWebServer</code> instance we now need to add a file with the <code>JSON</code> response we want the fake server to return. For this you need to add the following command in your <code>build.gradle</code>:</p>
<pre><code class="hljs language-groovy">android {
	...
	sourceSets {
		test {
			resources.srcDirs += [<span class="hljs-string">'src/test/resources'</span>]
		}
	}
	...
}
</code></pre>
<p>This will make sure that we can find our response file when running the tests.</p>
<p>And now we can add the actual response we want in the assets folder. I've used the <a href="https://jsonplaceholder.typicode.com/posts">posts</a> placeholder from the <a href="https://jsonplaceholder.typicode.com/">JSONPlaceholder</a> website.</p>
<p>So it will look like this:</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">[</span>
	<span class="hljs-punctuation">{</span>
		<span class="hljs-attr">"userId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span>
		<span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span>
		<span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"sunt aut facere repellat provident occaecati excepturi optio reprehenderit"</span><span class="hljs-punctuation">,</span>
		<span class="hljs-attr">"body"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"</span>
	 <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
	<span class="hljs-punctuation">{</span>
		<span class="hljs-attr">"userId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span>
	    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span>
	    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"qui est esse"</span><span class="hljs-punctuation">,</span>
		<span class="hljs-attr">"body"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"</span>
	<span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
    ...
<span class="hljs-punctuation">]</span>
</code></pre>
<p>Add it to the <code>src/test/resources</code> folder (create the folder if you don't have it).
You can see the final content of the file <a href="https://github.com/Tgo1014/MockWebServerSample/blob/master/app/src/test/java/tgo1014/mockwebserver/response.json">here</a>.</p>
<h2>Testing</h2>
<p>After setting everything up, we can actually start to write our tests. In this first example we will make sure we have a success response and that after running the test the response file was actually parsed and the list of posts are not empty.</p>
<p>For setting the fake response, we use the following method:</p>
<pre><code class="hljs language-kotlin">mockWebServer.setResponse(<span class="hljs-string">"response.json"</span>)
</code></pre>
<h3>Success</h3>
<p>Noticed that <code>response.json</code> is the file we added to the assets folder which contains the response we expect from the server API.</p>
<p>The full test looks like this:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> `Get Post List Should Not Be Empty`<span class="hljs-params">()</span></span> {
	<span class="hljs-comment">// Given A Post List Request</span>
    <span class="hljs-keyword">val</span> interactor = GetPostsInteractor(api)
    <span class="hljs-comment">// When Executing The Request</span>
	mockWebServer.setResponse(<span class="hljs-string">"response.json"</span>)
    <span class="hljs-keyword">val</span> postList = interactor.execute()
    <span class="hljs-comment">// Then Posts Should Not Be Empty</span>
	assert(postList.isNotEmpty())
}
</code></pre>
<h3>Error</h3>
<p>In the perfect world we always would have a success response from the API, but on a real production environment many things can go wrong: no internet connection, long latencies on response, wrong response from the backend.</p>
<p>Having all this in mind, we need to verify if, when something wrong goes with the API, the code can handle the situation the way we expect.</p>
<p>For this we are going to run basically the same test, but this time returning an error response from the <code>MockWebServer</code>. We can use the following method:</p>
<pre><code class="hljs language-kotlin">mockWebServer.setResponse(<span class="hljs-string">"response.json"</span>, responseCode = <span class="hljs-number">404</span>)
</code></pre>
<p>We set the response code as <code>404</code>, so the request won't be successful.</p>
<p>The final test look like this:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> `Get Post List Should Throw Error`<span class="hljs-params">()</span></span> {
	<span class="hljs-comment">// Given A Post List Request</span>
    <span class="hljs-keyword">val</span> interactor = GetPostsInteractor(api)
    <span class="hljs-comment">// When Executing The Request</span>
    mockWebServer.setResponse(<span class="hljs-string">"response.json"</span>, <span class="hljs-number">404</span>)
	<span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">val</span> postList = interactor.execute()
        assert(<span class="hljs-literal">false</span>)
    } <span class="hljs-keyword">catch</span> (e: Exception) {
        <span class="hljs-comment">// Then An Error Should Be Thrown</span>
	    assert(<span class="hljs-literal">true</span>)
    }
}
</code></pre>
<p>Notice that this time we only <code>asset(true)</code> on the <code>catch</code> as we are expending our code to raise an error when it's not a success response. If it runs even with the server returning <code>404</code> then something is wrong and we set <code>assert(false)</code> so the test fails.</p>
<h2>Conclusion</h2>
<p>This is just a simple and quick example of how you can use fake servers to test your code implementation. The <code>MockWebServer</code> is way more powerful than what I've shown here, so make sure to take a look in the <a href="https://github.com/square/okhttp/tree/master/mockwebserver">GitHub repo</a> for a more detailed use of all its features.</p>
<p>You can also take a look in the <a href="https://github.com/Tgo1014/MockWebServerSample">sample repo</a> where you can understand a bit better how everything works together.</p>
<p>Article photo by <a href="https://unsplash.com/@tvick">Taylor Vick</a> on <a href="https://unsplash.com/">Unsplash</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Android Unit Testing is Easy]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/07/15/android-unit-testing-is-easy</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/07/15/android-unit-testing-is-easy</guid>
            <pubDate>Wed, 15 Jul 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>The reason I am writing this article is because it was very hard for me to find some unit testing materials on Android Components. I hope I can fill the gap. Let’s talk about unit testing for a bit. I am not going to explain what unit testing is in this article. The official Android documentation about <a href="https://developer.android.com/training/testing/unit-testing">unit testing</a> can be a good place to start. Although, it will be good to talk about some of the advantages of writing unit tests.</p>
<ol>
<li>
<p>It helps us refactor/change old code more confidently without fear of breaking something you don’t know.</p>
</li>
<li>
<p>It forces you to write better code</p>
</li>
<li>
<p>It makes debugging easier. If a unit test fails it means your latest change breaks something</p>
</li>
<li>
<p>Eventually, unit testing leads to less bugs</p>
</li>
</ol>
<p>These are the main reasons why we should write unit tests in our projects. Now let’s talk about how to test Android Components.</p>
<p>Mainly I will try to explain Activity/Fragment unit testing with the new <a href="https://mvnrepository.com/artifact/androidx.test/core-ktx">AndroidX Testing library</a>. You can just simply add this dependency to use it</p>
<pre><code class="hljs language-groovy">    testImplementation <span class="hljs-string">'androidx.test:core-ktx:{LATEST-VERSION}'</span>
</code></pre>
<p>This library introduces us to two new classes <code>ActivityScenario</code> and <code>FragmentScenario</code>. With these two scenario classes, we can launch any activity or fragment and test their methods. You can easily change their states too.</p>
<p>One other benefit of this library is you can run these tests on JVM(using <a href="http://robolectric.org/">Roboelectric</a>) or on a device. All you need to do is to decide where to run deciding which folder to use test or androidTest . test for JVM, androidTest for device.</p>
<p>These scenario classes have very easy to use API. Let’s check them.</p>
<pre><code class="hljs language-kotlin">    <span class="hljs-keyword">val</span> scenario = launchActivity&#x3C;MainActivity>()

    <span class="hljs-comment">// Runs a given action on the current Activity's main thread.</span>
    scenario.onActivity { activity ->

    }

    <span class="hljs-comment">// Moves Activity state to a new state</span>
    state.scenario.moveToState(Lifecycle.State.RESUMED)

    <span class="hljs-comment">// Waits for the activity to be finished and returns the activity</span>
    result.scenario.result

    <span class="hljs-comment">// Finishes the managed activity and cleans up device's state</span>
    scenario.close()
</code></pre>
<h2>Preparing</h2>
<p>To write unit tests we need to configure our project a little bit more.</p>
<ul>
<li>Add this option to your project’s Gradle file:</li>
</ul>
<pre><code class="hljs language-groovy">    android {
        ...
        testOptions {
            unitTests.includeAndroidResources = <span class="hljs-literal">true</span>
        }
    }
</code></pre>
<ul>
<li>Add more dependencies to write unit tests more efficiently</li>
</ul>
<pre><code class="hljs language-groovy">    testImplementation <span class="hljs-string">'androidx.test.ext:junit-ktx:{LATEST-VERSION}'</span> <span class="hljs-comment">// Adds AndroidJUnit runner</span>
    testImplementation <span class="hljs-string">'org.robolectric:robolectric:{LATEST-VERSION}'</span> <span class="hljs-comment">// to run Android tests on JVM</span>
    testImplementation <span class="hljs-string">'io.mockk:mockk:{LATEST-VERSION}'</span> <span class="hljs-comment">// Kotlin friendly mocking library</span>
    testImplementation <span class="hljs-string">'androidx.test.espresso:espresso-core:{LATEST-VERSION}'</span> <span class="hljs-comment">// to write android UI tests</span>
</code></pre>
<ul>
<li>Add this annotation <code>@RunWith(AndroidJUnit::class)</code> to your test suite.</li>
</ul>
<h2>Testing</h2>
<p>Let’s start with an example. Let’s assume that we have an activity called MainActivity . Let’s test if it starts without crashing.</p>
<pre><code class="hljs language-kotlin">    <span class="hljs-keyword">private</span> <span class="hljs-keyword">lateinit</span> <span class="hljs-keyword">var</span> scenario: ActivityScenario&#x3C;MainActivity>

    <span class="hljs-meta">@Test</span>
    <span class="hljs-function"><span class="hljs-keyword">fun</span> `Starts without crashing`<span class="hljs-params">()</span></span> {
        scenario = launchActivity()
    }
</code></pre>
<p>That’s it. All we need to do is use the launchActivity function. It will create and start the activity for us.</p>
<p>Let’s do a more complicated example. Assume that we have a button and our ViewModel controls if the button is enabled or not.</p>
<pre><code class="hljs language-kotlin">    <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> viewModel: MainViewModel = mockk(relaxUnitFun = <span class="hljs-literal">true</span>)

    <span class="hljs-meta">@Test</span>
    <span class="hljs-function"><span class="hljs-keyword">fun</span> `Save button starts enabled`<span class="hljs-params">()</span></span> {
        <span class="hljs-comment">// Given</span>
        every { viewModel.saveButtonEnabled } returns <span class="hljs-literal">true</span>

        <span class="hljs-comment">// When</span>
        scenario = launchActivity()

        <span class="hljs-comment">// Then</span>
        onView(withId(R.id.saveBtn)).check(matches(isEnabled()))
    }
</code></pre>
<p>So what happened here?</p>
<ol>
<li>
<p>Mock the ViewModel and define the behavior of saveButtonEnabled flag variable.</p>
</li>
<li>
<p>Start the activity</p>
</li>
<li>
<p>Check if the button is enabled or not</p>
</li>
</ol>
<h2>Conclusion</h2>
<p>What we achieved in this article is to use Google’s new test API and write some small and easy unit tests. AcitivityScenario/FragmentScenenario is definitely the best and easiest way to write unit tests for Android components. I strongly recommend it.</p>
<p>I want to share one more benefit with you. It really feels nice to see all of your tests are passed :)</p>
<p>![conclusion](/assets/img/articles/2020-07-15-android-unit-testing-is-easy/conclusion.webp</p>
<p>Note: You can check my <a href="https://github.com/dtunctuncer/android-components-unit-testing">sample repo</a> and see the setup and some more complex examples.</p>
<p>Article photo by <a href="https://unsplash.com/@bill_oxford">Bill Oxford</a> on <a href="https://unsplash.com/">Unsplash</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Privacy for Photos Access]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/06/29/Privacy-for-Photos-Access</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/06/29/Privacy-for-Photos-Access</guid>
            <pubDate>Mon, 29 Jun 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Apple always takes care of Privacy. Prior to iOS 14, for accessing photos, users were prompted with two options <strong>Don't Allow</strong> and <strong>OK</strong>. To use a feature that actually only required limited access, users still had to expose their entire photo library. This year Apple introduced new tools that give users more granular control over the data they share. Let's see what's new in iOS 14 for accessing Photos in detail.</p>
<h3>Photos</h3>
<p>Apple introduced the <code>Limited Photo Library</code> feature, allowing users to select specific photos rather than giving access to their entire Photo Library. The new prompt for accessing the Photo Library provides 3 options <strong>Select Photos</strong>, <strong>Allow Access to All ‌Photos</strong>‌, or <strong>Don’t Allow</strong>. The <strong>Select Photos</strong> option lets users choose specific photos. This is what the new prompt looks like:</p>
<p><img src="/assets/img/articles/2020-06-29-Privacy-for-Photos-Access/photos-new-prompt.webp" alt="">
<em>Image source: <a href="https://developer.apple.com/videos/play/wwdc2020/10676/">Apple</a></em></p>
<p>There are two ways to modify the photo selection of your app:</p>
<p>1.<code>From Prompt:</code> Once per app life cycle, when assets are fetched via PhotoKit, the user will be prompted with two options, <strong>Select More Photos</strong> and <strong>Keep Current Selection</strong></p>
<p><img src="/assets/img/articles/2020-06-29-Privacy-for-Photos-Access/prompt-settings.webp" alt="">
<em>Image source: <a href="https://developer.apple.com/videos/play/wwdc2020/10641/">Apple</a></em></p>
<p>2.<code>From Settings:</code> Additionally, app-level photo access can be modified from <code>Settings</code> -> <code>Privacy</code> -> <code>Photos</code> -> Your app, which shows the <strong>Edit Selected Photos</strong> option for updating the selection.</p>
<p><img src="/assets/img/articles/2020-06-29-Privacy-for-Photos-Access/photos-settings.webp" alt="">
<em>Image source: <a href="https://developer.apple.com/videos/play/wwdc2020/10641/">Apple</a></em></p>
<h3>PHPicker</h3>
<p>For selecting images/videos without requiring Photo Library access, Apple introduced <strong>PHPicker</strong>. which is a replacement for UIImagePickerController.</p>
<p><em>"PHPicker is the system provided Picker which allows you to get access to photos and videos from the users photo library" - Apple</em></p>
<p><strong>Features</strong></p>
<ul>
<li>Improved with search and multi selection</li>
<li>No direct access to user's Photos Library required</li>
<li>Supports fluid zooming in the grid</li>
<li>Provides user-selected photos and videos only</li>
<li>Runs in a separate process, containing app cannot take screenshots</li>
</ul>
<p><strong>Components</strong></p>
<p>1.<code>PHPickerConfiguration and PHPickerFilter:</code> You can specify your photo/video selection limit and apply a filter for selected items by using two optional properties, <strong>selectionLimit</strong> and <strong>filter</strong>.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">var</span> configuration <span class="hljs-operator">=</span> <span class="hljs-type">PHPickerConfiguration</span>()

<span class="hljs-comment">// Set 0 for unlimited selection, default is 1</span>
configuration.selectionLimit <span class="hljs-operator">=</span> <span class="hljs-number">0</span>

<span class="hljs-comment">// Only show images(including Live Photos)</span>
configuration.filter <span class="hljs-operator">=</span> <span class="hljs-type">PHPickerFilter</span>.images

<span class="hljs-comment">// Only Videos or Live Photos, but no images</span>
configuration.filter <span class="hljs-operator">=</span> <span class="hljs-type">PHPickerFilter</span>.any(of: [.videos, .livePhotos])
</code></pre>
<p>2.<code>PHPickerViewController:</code> Initialize PHPickerViewController with the above configuration and assign a delegate. The client is responsible for presentation and dismissal</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> picker <span class="hljs-operator">=</span> <span class="hljs-type">PHPickerViewController</span>(configuration: configuration)
picker.delegate <span class="hljs-operator">=</span> <span class="hljs-keyword">self</span>
present(picker, animated: <span class="hljs-literal">true</span>)
</code></pre>
<p>3.<code>PHPickerResult:</code> For handling results, a <strong>PHPickerViewControllerDelegate</strong> delegate method will be called with an array of PHPickerResults. If the user cancels the selection, the array will be empty.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">picker</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">picker</span>: <span class="hljs-type">PHPickerViewController</span>, <span class="hljs-params">didFinishPicking</span> <span class="hljs-params">results</span>: [<span class="hljs-type">PHPickerResult</span>]) {
   picker.dismiss(animated: <span class="hljs-literal">true</span>, completion: <span class="hljs-literal">nil</span>)

   <span class="hljs-comment">// Get the first item provider from the results</span>
   <span class="hljs-keyword">let</span> itemProvider <span class="hljs-operator">=</span> results.first<span class="hljs-operator">?</span>.itemProvider

   <span class="hljs-comment">// Access the UIImage</span>
   <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> itemProvider <span class="hljs-operator">=</span> itemProvider, itemProvider.canLoadObject(ofClass: <span class="hljs-type">UIImage</span>.<span class="hljs-keyword">self</span>) {
       itemProvider.loadObject(ofClass: <span class="hljs-type">UIImage</span>.<span class="hljs-keyword">self</span>) { image, error <span class="hljs-keyword">in</span>
           <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> image <span class="hljs-operator">=</span> image {
               <span class="hljs-comment">//Do something with the UIImage</span>
           }
       }
   }
}
</code></pre>
<h2>References</h2>
<ul>
<li><a href="https://developer.apple.com/videos/play/wwdc2020/10676/">Build trust through better privacy</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2020/10641/">Handle the Limited Photos Library in your app</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2020/10652/">Meet the new Photos picker</a></li>
</ul>
<p><em>Article Photo by <a href="https://www.apple.com/newsroom/2020/06/apple-reveals-lineup-for-its-biggest-ever-worldwide-developers-conference/">Apple</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What's new in Swift]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/06/29/Whats-new-in-Swift</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/06/29/Whats-new-in-Swift</guid>
            <pubDate>Mon, 29 Jun 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>This year's "What's new in Swift" included a plethora of improvements and new add-ons. Some of the news are:</p>
<ul>
<li>better diagnostic compiler error's and hence better debugging</li>
<li>improved and faster code completion</li>
<li>improved auto indentation</li>
<li>improved integration</li>
<li>improved handling of chained method calls and property accesses</li>
<li>A standardized way to delegate a program's entry point with the <code>@main</code> attribute</li>
<li>Swift Numerics, an open source project bringing additional support for numeric functions to Swift</li>
<li>Swift Argument Parser, build your command line tools with Swift</li>
<li><code>where</code> clauses on contextually generic declarations</li>
<li>Multi-Pattern <code>catch</code> clauses</li>
<li>Float16</li>
</ul>
<p>But the list is much longer. In the following are some highlights for what is to come with Swift 5.3 and you can find a list of further reading and related videos at the bottom of the post.</p>
<h2>Additional Cross Platform Support and Swift AWS Lambdas</h2>
<p>To support the Swift ecosystem Apple has added additional cross-platform support and will officially support:</p>
<ul>
<li>All Apple platforms</li>
<li>Ubuntu 16.04, 18.04, 20.04</li>
<li>CentOS</li>
<li>Amazon Linux 2</li>
<li>Windows (coming from Swift 5.3)</li>
</ul>
<p>This also opens up for official support for <a href="https://developer.apple.com/videos/play/wwdc2020/10644/">Swift AWS Lambdas</a>. The <code>AWSLambdaRuntime</code> package is based on <code>SwiftNIO</code> and allows for both closure based Lambdas and eventloop Lambda functions. Below is an example of a closure based Lambda as presented in the talk:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> AWSLambdaRuntime
<span class="hljs-type">Lambda</span>.run { (<span class="hljs-keyword">_</span>, name: <span class="hljs-type">String</span>, callback) <span class="hljs-keyword">in</span>
    callback(.success(<span class="hljs-string">"Hello, <span class="hljs-subst">\(name)</span>!"</span>))
}
</code></pre>
<h2>Enum Improvements</h2>
<p>This year we get two big improvements to the <code>enum</code> type:</p>
<h3><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0266-synthesized-comparable-for-enumerations.md">Synthesized <code>Comparable</code> Conformance For <code>enum</code> Types</a></h3>
<p>With Swift 4.1 came synthesized, opt-in <code>enum</code> conformance for <code>Equatable</code> and <code>Hashable</code>, however conformance to <code>Comparable</code> was left out. From Swift 5.3 <code>Comparable</code> will also be included, allowing for easy sorting by the order of declaration. The synthesized, opt-in conformace only comes for <code>enum</code>s without raw values, without associated types or with comparable associated types.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">enum</span> <span class="hljs-title class_">ApplePlatform</span>: <span class="hljs-title class_">Comparable</span> {
    <span class="hljs-keyword">case</span> iOS
    <span class="hljs-keyword">case</span> ipadOS
    <span class="hljs-keyword">case</span> macOS
    <span class="hljs-keyword">case</span> tvOS
    <span class="hljs-keyword">case</span> watchOS
}

[<span class="hljs-type">ApplePlatform</span>.watchOS, <span class="hljs-type">ApplePlatform</span>.ipadOS, <span class="hljs-type">ApplePlatform</span>.macOS].sorted()
<span class="hljs-comment">// [ApplePlatform.ipadOS, ApplePlatform.macOS, ApplePlatform.watchOS]</span>
</code></pre>
<h3><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0280-enum-cases-as-protocol-witnesses.md">Enum Cases As Protocol Witnesses</a></h3>
<p>Another great improvement to <code>enum</code>s in Swift is the possibility to allow for static protocol requirements to be witnessed by an <code>enum</code> case. It introduces two rules:</p>
<ol>
<li>static, get-only protocol requirement can be witnessed by an enum case without an associated value</li>
<li>static func requirement can be witnessed by an enum case with one or more associated values</li>
</ol>
<pre><code class="hljs language-swift"><span class="hljs-keyword">protocol</span> <span class="hljs-title class_">ErrorReportable</span> {
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> notFound: <span class="hljs-keyword">Self</span> { <span class="hljs-keyword">get</span> }
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">invalid</span>(<span class="hljs-params">searchTerm</span>: <span class="hljs-type">String</span>) -> <span class="hljs-keyword">Self</span>
}

<span class="hljs-keyword">enum</span> <span class="hljs-title class_">UserError</span>: <span class="hljs-title class_">ErrorReportable</span> {
    <span class="hljs-keyword">case</span> notFound
    <span class="hljs-keyword">case</span> invalid(searchTerm: <span class="hljs-type">String</span>)
}
</code></pre>
<h2>Multiple Trailing Closures</h2>
<p>If you have been following along in the discussions on Swift forums, you may have come across the topic of <em>multiple trailing closures</em>. It has been thoroughly discussed and many people had their opinions on both the syntax and the final process.</p>
<p>Up until now it has only been possible to use a trailing closure if your method has one escaping closure. An example of that is one of the animate methods in <code>UIKit</code></p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> UIKit
<span class="hljs-type">UIView</span>.animate(withDuration: <span class="hljs-number">0.3</span>) {
    <span class="hljs-keyword">self</span>.view.alpha <span class="hljs-operator">=</span> <span class="hljs-number">0</span>
}
</code></pre>
<p>If we instead wanted to use the animate method that also allows us to define what should happen at completion we can't use trailing closures anymore</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> UIKit
<span class="hljs-type">UIView</span>.animate(
    withDuration: <span class="hljs-number">0.3</span>,
    animations: { <span class="hljs-keyword">self</span>.view.alpha <span class="hljs-operator">=</span> <span class="hljs-number">0</span> },
    completion: { <span class="hljs-keyword">_</span> <span class="hljs-keyword">in</span>
        <span class="hljs-keyword">self</span>.view.removeFromSuperview()
    }
)
</code></pre>
<p>With Swift 5.3 any method can now have multiple trailing closures. The first closure will be without a label, while all remaining closures should be labeled. The animate method from before will now look like:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> UIKit
<span class="hljs-type">UIView</span>.animate(withDuration: <span class="hljs-number">0.3</span>) {
    <span class="hljs-keyword">self</span>.view.alpha <span class="hljs-operator">=</span> <span class="hljs-number">0</span>
} completion: { <span class="hljs-keyword">_</span> <span class="hljs-keyword">in</span>
    <span class="hljs-keyword">self</span>.view.removeFromSuperview()
}
</code></pre>
<h2>Increased Availability of Implicit <code>self</code> in Closures</h2>
<p>Swift requires the explicit use of <code>self</code> in escaping closures which capture it. This can lead to a lot of repetitive code. With implicit <code>self</code> in closures in Swift 5.3, we can omit all <code>self.</code> inside our closures. Instead we can add <code>self</code> to the capture list of the closure. Using implicit <code>self</code> will then update our animate method from before, so we only have to specify <code>self</code> once in each closure.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> UIKit
<span class="hljs-type">UIView</span>.animate(withDuration: <span class="hljs-number">0.3</span>) { [<span class="hljs-keyword">self</span>] <span class="hljs-keyword">in</span>
    view.alpha <span class="hljs-operator">=</span> <span class="hljs-number">0</span>
} completion: { [<span class="hljs-keyword">self</span>] <span class="hljs-keyword">_</span> <span class="hljs-keyword">in</span>
    view.removeFromSuperview()
}
</code></pre>
<h2>Other WWDC Articles</h2>
<ul>
<li><a href="/en2020-06-26-WWDC20-summary">WWDC20</a></li>
<li><a href="/en2020-06-23-A-first-look-at-Apples-new-Augmented-Reality-features">A first look at Apple's new Augmented Reality features</a></li>
<li><a href="/en2020-06-26-Introducing-App-Clips">Introducing App Clips</a></li>
</ul>
<h2>Recommended WWDC Sessions</h2>
<ul>
<li><a href="https://developer.apple.com/videos/play/wwdc2020/10170/">What's new in Swift</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2020/10168/">Explore logging in Swift</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2020/10217/">Explore numeric computing in Swift</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2020/10644/">Use Swift on AWS Lambda with Xcode</a></li>
</ul>
<h2>Resources and Further Reading</h2>
<ul>
<li><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0249-key-path-literal-function-expressions.md">Keypath expressions as functions (SE-0249)</a></li>
<li><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0253-callable.md">Callable values of user-defined nominal types (SE-0253)</a></li>
<li><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0263-string-uninitialized-initializer.md">String initializer with access to uninitialized storage (SE-0263)</a></li>
<li><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0264-stdlib-preview-package.md">Standard library preview package (SE-0264)</a></li>
<li><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0266-synthesized-comparable-for-enumerations.md">Synthesized comparable conformance for enum types (SE-0266)</a></li>
<li><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0267-where-on-contextually-generic.md">Where Clauses on contextually generic declarations (SE-0267)</a></li>
<li><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0268-didset-semantics.md">Refined didSet semantics (SE-0268)</a></li>
<li><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0269-implicit-self-explicit-capture.md">Increased availability of implicit self in closures (SE-0269)</a></li>
<li><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0270-rangeset-and-collection-operations.md">Collection operations on noncontiguous elements (SE-0270)</a></li>
<li><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0276-multi-pattern-catch-clauses.md">Multi-pattern catch clauses (SE-0276)</a></li>
<li><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0277-float16.md">Float16 (SE-0277)</a></li>
<li><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0279-multiple-trailing-closures.md">Multiple trailing closures (SE-0279)</a></li>
<li><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0280-enum-cases-as-protocol-witnesses.md">Enum cases as protocol witnesses (SE-0280)</a></li>
<li><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0281-main-attribute.md">Type-based program entry points (SE-0281)</a></li>
<li><a href="https://github.com/apple/swift-standard-library-preview">Standard library preview</a></li>
<li><a href="https://github.com/apple/swift-numerics">Swift Numerics</a></li>
<li><a href="https://github.com/apple/swift-argument-parser">Swift Argument Parser</a></li>
<li><a href="https://github.com/apple/swift-log">SwiftLog</a></li>
</ul>
<p><em>Article Photo - capture from <a href="https://developer.apple.com/videos/play/wwdc2020/10170/">What's new in Swift</a> by Apple</em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introducing App Clips]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/06/26/Introducing-App-Clips</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/06/26/Introducing-App-Clips</guid>
            <pubDate>Fri, 26 Jun 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Apple introduced a new feature called app clips at this year's WWDC. It is coming to iOS 14 this fall. App clips offer a promising new way for businesses to increase the discoverability of their apps and services while at the same time eliminating the need to install a full app. This concept may sound familiar to Android developers who have had access to <a href="https://developer.android.com/topic/google-play-instant">Instant Apps</a> since 2017.</p>
<p>App clips are segments of your app that should always aim at focusing on a clearly defined use-case, such as ordering a burrito at your business's point of sale. As such, you should refrain from using top-level navigation concepts like tab bars or sidebars. Do not attempt to replicate the full feature set of your app but rather focus on the task at hand. A single app clip can contain multiple experiences, and thus handle multiple use-cases (such as ordering online or picking up an order at a bricks and mortar store) by defining dedicated deep link URLs which are passed to the app clip at launch as part of an <code>NSUserActivity</code>.</p>
<p>App clips are ephemeral in nature and not added to the user's home screen. An app clip and all its data is removed from the user's device after a certain period of inactivity (likely a few days) which gets extended each time the clip is launched.</p>
<h3>Quick Facts</h3>
<ul>
<li>Maximum 10 MB after app thinning</li>
<li>Can share data with containing app using app group containers</li>
<li>Not added to home screen, not backed up</li>
<li>Deleted from device after period of inactivity</li>
<li>Can be built using either UIKit or the new SwiftUI app lifecycle</li>
</ul>
<h3>Technical Implementation</h3>
<p>App clips are developed in the context of you app's main Xcode project. As such, new app clip releases have to be bundled with updates of its containing app and cannot be submitted for review independently. Once published on the App Store, clips are separate binaries that are downloaded on-demand.</p>
<p>Similar to other types of extensions, such as WatchKit or Share extensions, you start by adding a new target to your Xcode project.
However, unlike other extensions, app clips have full access to all frameworks the iOS SDK has to offer. Just know that certain features concerning the user's privacy, such as obtaining health data, won't work.
Code, assets catalogs and other types of resources can be shared between your app clip and its containing app simply by adding them to both targets. If you intent to store data you want to migrate to your containing app once the user decides to install it, you can do so by creating an app group and storing the data in a shared app group container. If you have developed any kind of app extension before, you should be familiar with this concept.</p>
<p>App cards contain a link to your app on the App Store. You may, however, also offer an opportunity to download your app from within your app clip using <code>StoreKit's</code> new <a href="https://developer.apple.com/documentation/storekit/skoverlay">SKOverlay</a>. App clips and full apps are mutually exclusive. Once the full app has been installed, iOS will give you an opportunity to migrate your data before your app clip is deleted. From this point on, links will launch your full app rather than your app clip. As such, you have to ensure your app can handle the same deep links and offer the same experiences as your app clip.</p>
<p>App clips can only be launched via app clip URLs. Universal links and custom URL schemes are not supported. If your app clip requires user authentication to function properly, consider using <a href="https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession">ASWebAuthenticationSession</a> or <a href="https://developer.apple.com/documentation/authenticationservices/asauthorizationcontroller">ASAuthorizationController</a> to facilitate 3rd party single sign-on or set up <a href="https://developer.apple.com/sign-in-with-apple/">Sign in with Apple</a>. To customize the experience, e.g. for different branches of your business, specify a prefix URL and pass branch-specific information as a path or query parameter.</p>
<h3>Testing</h3>
<p>You can test proper handling of deep links before submitting your app clip for review by specifying the environment variable <code>_XCAppClipURL</code> in your clip's Run scheme and passing the URL you want to handle.</p>
<h3>App Clip Cards</h3>
<p>App clip cards, which are part of an app clip experience and facilitate the entry point to your app clip, are set up and configured using <a href="https://appstoreconnect.apple.com">App Store Connect</a>. Apple aims at offering a streamlined and recognizable experience for all app clip cards. As such, the general visual appearance is largely predefined. You can customize your cards by specifying a background image, a subtitle for the card, and a verb for the CTA that launches your app clip. For further information on image sizes, character limits and other recommendations, refer to Apple's <a href="https://developer.apple.com/design/human-interface-guidelines/app-clips/overview/">interface guidelines</a>.</p>
<p><img src="/assets/img/articles/2020-06-26-Introducing-App-Clips/app-cards-clips.webp" alt="">
<em>Image source: <a href="https://developer.apple.com/documentation/app_clips">Apple</a></em></p>
<h3>Ways to Discover</h3>
<h5>App Clip Codes</h5>
<p>Later this year, Apple is set to launch dedicated app clip codes that are somewhat reminiscent of Facebook's <a href="https://developers.facebook.com/docs/messenger-platform/discovery/messenger-codes/">Messenger Codes</a>. App clip codes can for instance be placed at your point of sale. Users can launch app clip experiences by either scanning your code with their iPhone's camera or by simply tapping it as it also contains an NFC tag.</p>
<h5>QR-Codes and NFC Tags</h5>
<p>Aside from Apple's proprietary codes, you may as well use regular QR-codes and NFC tags as linking works by simply registering and encoding URLs associated with your app clip experience.</p>
<p><img src="/assets/img/articles/2020-06-26-Introducing-App-Clips/nfc.webp" alt="">
<em>Image source: <a href="https://developer.apple.com/videos/play/wwdc2020/10146/">Apple</a></em></p>
<h5>Website Banners</h5>
<p>Much like existing smart app banners, you can enable support for launching app clips by adding the meta tag <code>apple-itunes-app</code> containing your clip's bundle identifier to your website's HTML. Remember to also add your App ID, so iOS versions prior to 14 can fall back to showing your app's App Store link.</p>
<h5>Messages</h5>
<p>One of the many new features added to Messages this year is the option to share app clips directly in a conversation.
Links can be shared by enabling sharing from within your app clip.</p>
<h5>Business Details in Maps</h5>
<p>You can add an app clip CTA to your business details on Apple Maps by adding the corresponding information to <a href="https://mapsconnect.apple.com/">Apple Maps Connect</a>. Users will be able to discover and launch your app clip directly from within Maps.</p>
<h3>Recommended WWDC Sessions</h3>
<ul>
<li><a href="https://developer.apple.com/videos/play/wwdc2020/10174/">Explore app clips</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2020/10146/">Configure and link your app clips</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2020/10172/">Design great app clips</a></li>
</ul>
<h3>Resources and Further Reading</h3>
<ul>
<li><a href="https://developer.apple.com/app-clips/">Introducing app clips</a></li>
<li><a href="https://developer.apple.com/documentation/app_clips">Developer guidelines</a></li>
<li><a href="https://developer.apple.com/design/human-interface-guidelines/app-clips/overview/">Design guidelines</a></li>
<li><a href="https://developer.apple.com/documentation/storekit/skoverlay">Apple Developer - SKOverlay</a></li>
<li><a href="https://developer.apple.com/documentation/authenticationservices/asauthorizationcontroller">Apple Developer - ASAuthorizationController</a></li>
<li><a href="https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession">Apple Developer - ASWebAuthenticationSession</a></li>
</ul>
<p><em>Article Photo by <a href="https://developer.apple.com/app-clips/images/hero-lockup-large.png">Apple</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[WWDC20]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/06/26/WWDC20-summary</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/06/26/WWDC20-summary</guid>
            <pubDate>Fri, 26 Jun 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Every year Apple's <a href="https://developer.apple.com/wwdc20/">WWDC</a> event is a highlight in the Apple developer community, and this year was no different. Because of COVID-19 the event had to take on a new form and move out of Cupertino and into the virtual world. The week was kicked off with the highly anticipated <em>keynote</em> and <em>state of the union</em> talks followed by a daily release of video sessions and developer labs, where developers could book 1:1 sessions with Apple's engineers.</p>
<p>Even more than in recent years, the leitmotif of a lot of topics Apple covered was the convergence of all platforms within their ecosystem. Starting with the transition to ARM-based CPUs for the entire Mac product line, to universally applicable SwiftUI code, through to macOS Big Sur's new user interface, unification appeared to be the mantra resonating through everything. Take Big Sur's new Desktop, put an iPad next to it, and ask your grandma which one is which.</p>
<p>For users this means they only have to learn how to use an Apple product once. If you already know how to use an iPhone, chances are you'll have little trouble getting used to an iPad or a Mac. In the majority of cases you'll probably be able to use the same apps.</p>
<p>For developers it means it should be easier than ever to offer apps on multiple platforms. You just have to make up your mind about which technology to adopt. On iOS your options are manageable. On macOS on the other hand, you can now choose between AppKit, UIKit, Catalyst, and SwiftUI to build the user interface of your app.</p>
<p>For Apple it means everything they've been able to do with their in-house SoCs powering iOS devices for a number of years now, finally comes home to the Mac. They have full control over the development roadmap of both their software and hardware, on all of their devices.</p>
<p>Another topic Apple again put a lot of emphasis on, is the privacy of its users, with iOS users gaining even more control over data that is shared with developers or 3rd parties and macOS inheriting a lot of privacy-focused features iOS user have been familiar with for years.</p>
<h2>Highlights For Users (and Developers)</h2>
<h3>App Library</h3>
<p>Apple added an App Library to the last page of the home screen. It automatically organizes and groups all your apps contextually. You can choose how many pages you want to display and hide rarely used apps for a less cluttered home screen and quicker access to App Library. It also provides a search bar at the top, with all apps sorted alphabetically and a quick way to search for apps.</p>
<p><img src="/assets/img/articles/2020-06-26-WWDC20-summary/app-library.webp" alt="">
<em>Image source: <a href="https://www.apple.com/ios/ios-14-preview/">App Library</a></em></p>
<h3>Widgets</h3>
<p>Apple added widgets to the iOS 14 home screen, allowing you to freely position your widgets alongside your apps on the home screen. There are three different sizes to choose from, depending on how important the content of a widget is to you. When you drag and drop your widget, space between the app icons will automatically be managed based on the position and size of the widget. You can also place a stack of widgets on your home screen and navigate through it using a swipe gesture. As opposed to Today widgets, the new home screen widgets are written exclusively using SwiftUI.</p>
<p><img src="/assets/img/articles/2020-06-26-WWDC20-summary/widgets.webp" alt="">
<em>Image source: <a href="https://www.apple.com/ios/ios-14-preview/">Widgets</a></em></p>
<h3>Picture-in-Picture</h3>
<p>Apple added Picture-in-Picture support. While watching a video or talking to a friend on FaceTime, it allows you to access other apps on your iOS device at the same time. The below image shows a PiP video displayed alongside the Notes app.</p>
<p><img src="/assets/img/articles/2020-06-26-WWDC20-summary/picture-in-picture.webp" alt="">
<em>Image source: <a href="https://www.apple.com/ios/ios-14-preview/">Picture in Picture</a></em></p>
<h3>Siri</h3>
<p>Much like the new overlay for incoming calls, Apple redesigned Siri's interface to be less intrusive by not covering the whole screen. Addressing Siri now summons an indicator at the bottom of the screen:</p>
<p><img src="/assets/img/articles/2020-06-26-WWDC20-summary/siri.webp" alt="">
<em>Image source: <a href="https://www.apple.com/ios/ios-14-preview/">Siri</a></em></p>
<p>More ways to send messages using Siri are:</p>
<ul>
<li>by Recording audio</li>
<li>by using your voice for keyboard dictation</li>
</ul>
<h3>The Translate App</h3>
<p>Apple designed the Translate App for conversations. It works completely offline and keeps your conversations private. You can translate your voice and text from and to any of the languages below. In a conversation it can automatically detect who is speaking based on the language that is spoken, and add the translated text to the right side of the conversation.</p>
<p><img src="/assets/img/articles/2020-06-26-WWDC20-summary/translate.webp" alt="">
<em>Image source: <a href="https://www.apple.com/ios/ios-14-preview/">Translate App</a></em></p>
<h3>Messages</h3>
<ul>
<li><strong>Conversations:</strong>
You can now pin conversations and keep the most important messages at the top by swiping right on a conversation and pinning it. For multiple conversations, you can edit pinned content from the menu.</li>
</ul>
<p><img src="/assets/img/articles/2020-06-26-WWDC20-summary/pin-conversations.webp" alt="">
<em>Image source: <a href="https://www.apple.com/ios/ios-14-preview/">Conversations</a></em></p>
<ul>
<li><strong>Memoji:</strong> Apple added new Memoji customization options, including new hairstyles, face covers, and more.</li>
</ul>
<p><img src="/assets/img/articles/2020-06-26-WWDC20-summary/memoji.webp" alt="">
<em>Image source: <a href="https://www.apple.com/ios/ios-14-preview/">Memoji</a></em></p>
<ul>
<li><strong>Groups:</strong> In group conversations, you are now able to add <code>inline replies</code> and <code>mentions</code>. This means you can reply to specific messages and start a thread. You can mention anyone in a group conversation and also update the settings to only be notified when you are mentioned in a group conversation. You can customize your group photo and inside a conversation, you can see the profile pictures of group members aligned around the group photo at the top.</li>
</ul>
<p><img src="/assets/img/articles/2020-06-26-WWDC20-summary/group-messages.webp" alt="">
<em>Image source: <a href="https://www.apple.com/ios/ios-14-preview/">Groups</a></em></p>
<h3>Maps</h3>
<p>Apple has added some welcome features to Maps:</p>
<ul>
<li><strong>Guides</strong> help you explore new places and can be saved for offline usage.</li>
<li><strong>Cycling Directions</strong> offer information about elevation, quiet or busy roads, steep passages coming up, and stairs along the route.</li>
<li><strong>EV routing</strong> shows charging stations along the route which are compatible with your electric vehicle.</li>
</ul>
<h2>Highlights For Developers (and Users)</h2>
<h3><a name="swift-5.3">What's New in Swift 5.3</a></h3><a name="swift-5.3">
<p>Every year Apple adds a slew of new features to the Swift Standard library and 2020 was no exception. Some of the highlights from Swift 5.3 include:</p>
</a><ul><a name="swift-5.3">
<li>Updates to the standard library adding support for a preview packages</li>
<li>Updates to enums, fx synthesized conformance to the <code>Comparable</code> protocol and enum cases as protocol witnesses</li>
<li>Support for multiple trailing closures</li>
<li>increased availability of implicit <code>self</code> in closures</li>
<li>Float16</li>
</a><li><a name="swift-5.3">Multi-pattern catch clauses
You can read more about what's new in Swift </a><a href="/en2020-06-29-Whats-new-in-Swift">here.</a></li>
</ul>
<h3>SwiftUI 2.0</h3>
<p>When Apple introduced SwiftUI at last year's WWDC, it left a lot of developers with mixed feelings. On the one, everyone was excited about the paradigm shift to a fresh, concise, and declarative approach to building user interfaces. On the other hand, it still left a lot to be desired, lacking some critical features. In short, it was not ready for the main stage.</p>
<p>This year, Apple added a lot features developers have been asking for. Some highlights include:</p>
<ul>
<li><code>LazyVStack</code>, <code>LazyHStack</code>, and <code>List</code> load content on-demand as the user scrolls, much like table and collection views in UIKit</li>
<li>Leveraging Swift 5.3's <code>@main</code>, there is a new entry point to applications. SwiftUI's new <code>App</code>, <code>Scene</code>, and <code>WindowGroup</code> structs eliminate the need for AppDelegates and SceneDelegates, allowing you to build your entire user interface just using SwiftUI</li>
<li>The new <code>@StateObject</code> property wrapper allows for creating reference types inside views while ensuring they don't get released for the lifetime of the view</li>
<li><code>ProgressView</code> adds native activity indicators and progress bars</li>
<li><code>LazyVGrid</code> and <code>LazyHGrid</code> somewhat address the lack of collection views</li>
<li><code>TextEditor</code> is the equivalent to UIKit's UITextView</li>
<li>Better control over scroll views, such as determining the current or scrolling to a specific offset has been added in the form of <code>ScrollViewReader</code> and <code>ScrollViewProxy</code></li>
</ul>
<h3><a href="/en2020-06-26-Introducing-App-Clips">App Clips</a></h3>
<p>App clips offer a promising new way for businesses to increase the discoverability of their apps and services while at the same time eliminating the need to install a full app.</p>
<p>At a glance, app clips:</p>
<ul>
<li>cannot be larger than 10 MB after app thinning</li>
<li>can share data with containing app using app group containers</li>
<li>are not added to home screen, not backed up</li>
<li>are deleted from device after a period of inactivity</li>
<li>can be built using either UIKit or the new SwiftUI app lifecycle</li>
</ul>
<h3>Advances in <a href="/en2020-06-23-A-first-look-at-Apples-new-Augmented-Reality-features">ARKit</a></h3>
<p>Apple has further extended its lead over the competition in AR. This year they introduced features making use of the LiDAR scanner that was added to the iPad Pro earlier this year and that will potentially come to high-end iPhone models this fall. Some highlights include:</p>
<ul>
<li>The Depth API offers per-pixel depth information about the user's surroundings, allowing for more precise measurements and object occlusion</li>
<li>Set location anchors to real-world geo-coordinates to enable AR experiences at specific locations</li>
<li>Face tracking using the front camera does not require a TrueDepth camera any longer, provided your device has at least an A12 Bionic chip</li>
</ul>
<h3>HealthKit</h3>
<h4>Electrocardiogram (ECG)</h4>
<p>Third-party apps are now able to read ECG data that can be recorded with the Apple Watch since watchOS 5.2.</p>
<p><img src="/assets/img/articles/2020-06-26-WWDC20-summary/healthkit-ecg.webp" alt="">
<em>Image source: <a href="https://developer.apple.com/wwdc20/10182">What's new in HealthKit, Apple WWDC20 session</a></em></p>
<p>Apple has added <code>HKElectrocardiogram</code> to the iOS 14 SDK:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">class</span> <span class="hljs-title class_">HKElectrocardiogram</span>: <span class="hljs-title class_">HKSample</span> {
  <span class="hljs-keyword">var</span> classification: <span class="hljs-type">HKElectrocardiogram</span>.<span class="hljs-type">Classification</span>
  <span class="hljs-keyword">var</span> symptomsStatus: <span class="hljs-type">HKElectrocardiogram</span>.<span class="hljs-type">SymptomsStatus</span>
  <span class="hljs-keyword">var</span> averageHeartRate: <span class="hljs-type">HKQuantity</span>?
  <span class="hljs-keyword">var</span> samplingFrequency: <span class="hljs-type">HKQuantity</span>?
  <span class="hljs-keyword">var</span> numberOfVoltageMeasurements: <span class="hljs-type">Int</span>
}
</code></pre>
<h4>Mobility Data Types</h4>
<p>New mobility data types have been added:</p>
<ul>
<li>Walking speed and step length</li>
<li>Walking asymmetry and double support percentage</li>
<li>Stair ascent and descent speed</li>
<li>Six minute walk test distance</li>
</ul>
<h4>Symptom Samples</h4>
<p>Apple has added symptom samples accessible to developers to HealthKit:</p>
<ul>
<li>Mood Changes</li>
<li>Night Sweats</li>
<li>Diarrhea</li>
<li>Wheezing</li>
<li>Bloating</li>
<li>Tiredness or Fatigue</li>
<li>Bladder Incontinence</li>
<li>Nausea</li>
<li>Sleep Changes</li>
<li>Sinus Congestion</li>
</ul>
<p><img src="/assets/img/articles/2020-06-26-WWDC20-summary/healthkit-symptoms.webp" alt="">
<em>Image source: <a href="https://developer.apple.com/wwdc20/10182">What's new in HealthKit, Apple WWDC20 session</a></em></p>
<h3>Security and privacy</h3>
<h4>Encrypted DNS</h4>
<p>With iOS 14 and macOS 11 Apple will support <strong>DoT</strong> (DNS over TLS) as well as <strong>DoH</strong> (DNS over HTTPS).
By enabling this, communication with DNS servers will be encrypted. Where normal DNS traffic would communicate using clear text (allowing others to track and profile users) both DoT and DoH prevent any third-parties from tracking DNS queries by encrypting the entire communication.</p>
<p>Encrypted DNS can be enabled system-wide by setting up a DNS server that supports this or on application-level by developers who want to use their own DNS for resolving addresses.</p>
<h4>Private MAC address</h4>
<p>A MAC (Media Access Control) address is a unique identifier that is assigned to the network interface of your device. With iOS 14 you will be able to enable "Private MAC address" for WIFI which will change the MAC address periodically and also whenever you switch networks. <strong>This will prevent systems from tracking you (/your device).</strong></p>
<p>Of course, you can decide to use the real MAC address where applicable. For example it might make sense to do so for your home or work network if the routers use it to recognize your device and grant access.</p>
<h4>Privacy for Photos, Location, Contacts</h4>
<ul>
<li>
<p><strong><a href="/en2020-06-29-Privacy-for-Photos-Access">Photos</a>:</strong> iOS 14 introduces new access levels permissions for Photo Library which allows you to share specific photos/videos or give access to your entire photo library.</p>
</li>
<li>
<p><strong>Location:</strong> With iOS 14, you can now grant an app access to your approximate location, rather than your precise location.</p>
</li>
<li>
<p><strong>Contacts:</strong> With iOS 14, instead of sharing your entire Contacts list in third-party apps, you can now type individual names to automatically fill their corresponding phone numbers, addresses, and other information. The autofill happens on your device, and contacts are not shared with third-party developers without your consent.</p>
</li>
</ul>
<h3>Accessibility</h3>
<p>This year Apple continues to focus a lot on one of the most important topics nowadays: <strong>Accessibility</strong>. There are a couple of highly recommended sessions talking about improving accessibility by optimizing for <strong>Voice Over</strong>, <strong>Switch Control</strong> as well as making the <strong>visually accessible</strong>.</p>
<p>You'll find the <a href="https://developer.apple.com/news/?id=xpew8919">relevant sessions here</a>. They are all great, but I found <a href="(https://developer.apple.com/wwdc20/10019)">this one</a> by <a href="https://twitter.com/Sommer">Sommer Panage</a> particularly interesting and eye-opening.</p>
<p><strong>Be inclusive. Not exclusive!</strong></p>
<h3><a name="swift-package-manager">Swift Package Manager</a></h3><a name="swift-package-manager">
<h4>Binary Dependencies</h4>
<p>If you are using the new manifest version <code>// swift-tools-version:5.3</code> you can include binary dependencies in your package. These are pre-compiled libraries for each supported architecture and therefore won't expose the source code. Once you have set it up other users can include your package as if it was a regular package.</p>
<ul>
<li>✅ Great if you want to share a closed source framework using SPM</li>
<li>✅ Your library packages are protected by a checksum</li>
<li>✅ Developers can see your method signatures and documentation</li>
<li>❌ Only available for Apple platforms – no Linux support</li>
</ul>
<p>Also, before you start shipping binary dependencies only, bear in mind that you should have a very good business reason to do so; the general drawbacks of using compiled libraries still apply:</p>
<ul>
<li>❌ Debugging is way harder</li>
<li>❌ A package user cannot change the code to make it fit for their purpose or fix an issue</li>
<li>❌ If a new architecture (e.g. a new iPhone) has been released you cannot support it until the package owner compiles a binary for the new architecture</li>
</ul>
<p>More information:</p>
</a><ul><a name="swift-package-manager">
</a><li><a name="swift-package-manager"></a><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0272-swiftpm-binary-dependencies.md">SE-0272 - Package Manager Binary Dependencies</a></li>
</ul>
<h4>Resources &#x26; Localization</h4>
<p>Also new with Swift 5.3 is the option to ship resources and localized resources with a Swift Package. This proves great if you want to ship resources, such as images, HTML, or other content as well as localizations with your package.</p>
<ul>
<li>✅ Add Asset catalogs, HTML files, storyboards, databases, resources, even sample code as part of your package</li>
<li>✅ Provide localized content with your package</li>
<li>✅ You can define how they should be processed, e.g. if they should be compiled (code), optimized (images) or just copied (e.g. sample source code)</li>
<li>❌ A developer using your package cannot access your resources directly; if you need them to get access to specific resources, you can add static APIs for them</li>
</ul>
<p>More information:</p>
<ul>
<li><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0271-package-manager-resources.md">SE-0271 - Package Manager Resources</a></li>
<li><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0278-package-manager-localized-resources.md">SE-0278 - Package Manager Localized Resources</a></li>
</ul>
<h3><a name="swift-numerics">Swift Numerics</a></h3><a name="swift-numerics">
</a><p><a name="swift-numerics"></a><a href="https://github.com/apple/swift-numerics">Swift Numerics</a> is a new package that introduces a numerical API for Swift. It comes with two stand alone modules <code>RealModule</code> and <code>ComplexModule</code>. The <code>RealModule</code> defines four protocols:</p>
<ul>
<li><code>ElementaryFunctions</code> -> Basic arithmetic functions including the most common geometrical functions we all know and love from school</li>
<li><code>RealFunctions</code> -> Adds a series of more complex functions that are difficult to implement for complex numbers.</li>
<li><code>AlgebraicField</code> -> An extension to the Switch <code>SignedNumeric</code> that allows you to represent a number as a field and introduces reciprocal values</li>
<li><code>Real</code> -> The <code>Real</code> protocol combines all of the above and makes sure that they can be applied to all <code>FloatingPoint</code> conforming types, such as integers, floats, doubles, and any other types that will be added in the future</li>
</ul>
<p>The <code>ComplexModule</code> is build on top of the <code>RealModule</code> and adds a new <code>struct Complex&#x3C;RealType> where RealType: Real</code>. It support most of the arithmetic functions already provided for real numbers as well as a set of functions only relevant for complex values.</p>
<h3>Swift on Server</h3>
<p>Swift on Server has received a lot of love recently by Apple. They are heavily invested in <a href="https://swift.org/server/">SSWG</a> (Swift Server Working Group) where people from Apple together with others and our friends from <a href="https://vapor.codes">Vapor</a> provide overall technical direction, support and curate packages and make the (Swift) world a better place.</p>
<p>In this year's WWDC Apple has mentioned <a href="https://vapor.codes">Vapor</a> in their Platform State of the Union and designated a whole session for <a href="https://developer.apple.com/wwdc20/10644">using Swift using AWS Lambda</a>.</p>
<p>Apart from <a href="https://github.com/apple/swift-nio">Swift NIO</a>, <a href="https://github.com/swift-server/swift-aws-lambda-runtime/">Swift AWS Lambda runtime</a> and <a href="https://github.com/apple/swift-crypto">Swift Crypto</a> many of the new features introduced in this years WWDC are very useful for Server-side Swift development, e.g. the updates to <a href="#swift-package-manager">Swift Package Manager</a>, <a href="#swift-5.3">Swift 5.3</a> or <a href="#swift-numerics">Swift Numerics</a>.</p>
<h2>WWDC Articles</h2>
<ul>
<li><a href="/en2020-06-23-A-first-look-at-Apples-new-Augmented-Reality-features">A first look at Apple's new Augmented Reality features</a></li>
<li><a href="/en2020-06-29-Whats-new-in-Swift">What's new in Swift</a></li>
<li><a href="/en2020-06-26-Introducing-App-Clips">Introducing App Clips</a>
<!-- - [Serverless Swift with Lambdas on AWS](/en2020-06-23-A-first-look-at-Apples-new-Augmented-Reality-features) -->
<!-- - [What's new in SwiftUI](/en2020-06-23-A-first-look-at-Apples-new-Augmented-Reality-features) -->
</li>
<li>More to come next week</li>
</ul>
<p><em>Article Photo by <a href="https://www.apple.com/newsroom/images/live-action/wwdc/Apple_wwdc2020_03132020_big.webp.large.jpg">Apple</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[3D model presentation in games (props)]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/06/23/3D-model-presentation-in-the-game(props)</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/06/23/3D-model-presentation-in-the-game(props)</guid>
            <pubDate>Tue, 23 Jun 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><strong>Props are items that can interact with players in the game and have a certain influence on the attributes of the game characters.</strong><br>
There are two important criteria for judging whether an item is a prop:</p>
<ul>
<li>First, can it interact with players?</li>
<li>Second, does the use of this item affect the character's attributes?</li>
</ul>
<p>In games, props can be divided into three types according to the way they are used: <code>use type</code>, <code>equipment type</code> and <code>plot type</code>.</p>
<p>Let's have a look at an equipment prop as an example:</p>
<p>Equipment props, as the name implies, are things that can be equipped on the body. Different props are designed according to different characters and used in the game.</p>
<h3>Step 1: Original painting</h3>
<p>In order to design props, we first need the original painting (Figure 1). Take the props model as a concrete picture to build several specific visual frames for you to further create the 3D model.</p>
<p><img src="/assets/img/articles/2020-06-23-3D-model-presentation-in-the-game/1.webp" alt="">
<em>Figure 1</em></p>
<h3>Step 2: Prime model</h3>
<p>The 3D model includes two kinds of modes: prime and color. The white model is added after the 3D model is built, which is a simple prime model which cannot be used in the game (Figure 2).
<img src="/assets/img/articles/2020-06-23-3D-model-presentation-in-the-game/2.webp" alt="">
<em>Figure 2</em></p>
<h3>Step 3: Color model</h3>
<p>The color model is the artistic effect (as shown in Figure 3), that is finally presented in the game after the model is mapped (the model is put on colorful and textured clothes).
PS: UV should be developed before drawing the map of the model (Figure 4). It is convenient to draw the map by tiling the model (the plain model wears clothes).</p>
<p><img src="/assets/img/articles/2020-06-23-3D-model-presentation-in-the-game/3.webp" alt="">
<em>Figure 3</em></p>
<p><img src="/assets/img/articles/2020-06-23-3D-model-presentation-in-the-game/4.webp" alt="">
<em>Figure 4</em></p>
<p><img src="/assets/img/articles/2020-06-23-3D-model-presentation-in-the-game/5.webp" alt="">
<em>Figure 5</em></p>
<p>3D characters, props and scenes in games are produced following these three steps.</p>
<p>I hope that this introduction will let everyone understand the process of 3D model production in games.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A first look at Apple's new Augmented Reality features]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/06/23/A-first-look-at-Apples-new-Augmented-Reality-features</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/06/23/A-first-look-at-Apples-new-Augmented-Reality-features</guid>
            <pubDate>Tue, 23 Jun 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Apple's <a href="https://developer.apple.com/wwdc20/">WWDC</a> event is greatly anticipated in the Apple developers community every year. When it comes to the augmented reality world, we only got a quick glimpse of what is new in yesterday's keynotes. Here is an overview of what we know so far, with more details to unravel this week after the engineering sessions. You can find a list of AR recommended sessions to check out from this year's WWDC at the end of the article.</p>
<h2><a href="https://developer.apple.com/augmented-reality/arkit/">ARKit 4</a></h2>
<p>ARKit helps developers build powerful augmented reality experiences for millions of users worldwide. Here are the main 3 new features announced this year:</p>
<h3>Depth API</h3>
<p>The new Depth API powered by the LiDAR scanner available on the iPad Pro gives developers access to use per-pixel depth information about the surrounding environment. This will make occlusion and placement of 3D content even more realistic. Some examples of how this could be used:</p>
<ul>
<li>Taking more precise measurements</li>
<li>Applying effects to a user’s environment</li>
<li>Taking body measurements for more accurate virtual try-ons</li>
<li>Testing how your room will look like with different wall colors</li>
</ul>
<p>3D depth map created using the output from the Depth API:
<img src="/assets/img/articles/2020-06-23-A-first-look-at-Apples-new-Augmented-Reality-features/depthAPI.webp" alt="">
<em>Image source: Apple Platforms State of the Union</em></p>
<p><em>Available on iPad Pro 11-inch (2nd generation) and iPad Pro 12.9-inch (4th generation)</em></p>
<h3>Location Anchors</h3>
<p>A new session configuration is available for tracking geographic locations: <a href="https://developer.apple.com/documentation/arkit/argeotrackingconfiguration">ARGeoTrackingConfiguration</a> which will combine GPS, the device's compass and world-tracking features to enable back-camera AR experiences to specific locations.</p>
<p>Location anchors (<a href="https://developer.apple.com/documentation/arkit/argeoanchor">ARGeoAnchor</a>) will be used to specify the latitude, longitude, and optionally, altitude. For example, when you would be close to a landmark, the app will reveal a virtual signpost with historical facts. What makes me really excited about this new feature is the ease of developing AR street routes now by placing a group of location anchors.</p>
<p>To place location anchors with precision, GPS data is not sufficient. ARKit will download batches of imagery that depict the physical environment in that area and assist the session with determining the user’s precise geographic location.</p>
<p>This localization imagery captures the view mostly from public streets and routes accessible by car. As a result, geo tracking will be available in limited cities and locations where Apple collected localization imagery in advance.</p>
<p><em>Requires iPhone XS, iPhone XS Max, iPhone XR, or later. Available in select cities and areas</em></p>
<h3>Expanded Face Tracking Support</h3>
<p>Face tracking will now be supported on the front-facing camera on any device with A12 Bionic chip and later. Face filters are a big success nowadays on popular apps like Instagram, Snapchat or TikTok so opening up this feature to more devices is a great addition. You will be able to track three faces at once using the TrueDepth camera.</p>
<hr>
<p>You can read about more about ARKit's features in <a href="/en2019-06-26-ARKit-3-brings-more-power-to-Apples-Augmented-Reality">this article about ARKit 3</a>.</p>
<h2><a href="https://developer.apple.com/augmented-reality/realitykit/">RealityKit</a></h2>
<p>Introduced at last year's WWDC, RealityKit is Apple's rendering, animation, physics, and audio Swift framework built from the ground up with augmented reality in mind. Latest improvements include video textures, scene understanding using the LiDAR scanner on iPad Pro, Location Anchors, face tracking, and improved debugging tools.</p>
<h3>Video Textures</h3>
<p>Video textures can now be added to any part of a scene in RealityKit, bringing objects, surfaces, and even characters to life. Example use cases of this new feature:</p>
<ul>
<li>Animated virtual TV screens with rich videos</li>
<li>Making virtual characters smile</li>
</ul>
<h3>Improved Object Occlusion Rendering</h3>
<p>The LiDAR scanner comes with many powerful use cases and being able to place virtual objects more accurately, like under a table or behind a wall, it will support a much more seamless AR experience. A quick glimpse of how this will work was presented in the Platforms State of the Union keynote and it showed how crisp the definition is.</p>
<p><img src="/assets/img/articles/2020-06-23-A-first-look-at-Apples-new-Augmented-Reality-features/bench.webp" alt="">
<em>Video source: Apple Platforms State of the Union</em></p>
<h3>Automatic Updates</h3>
<p>Location Anchoring, extended support for face tracking and improved object occlusion rendering are available for apps using RealityKit automatically due to its native ARKit integration.</p>
<h2><a href="http://openusd.org/">USD</a></h2>
<p>Universal Scene Description is the technology behind USDZ, the AR focused file format introduced by Apple at WWDC 2018. A new proposed schema and structure updates to the standard will help us with building AR content with interactive properties like anchoring, physics, behaviours, 3D text and spatial audio.</p>
<h2>Recommended AR WWDC20 sessions</h2>
<ul>
<li><a href="https://developer.apple.com/videos/play/wwdc2020/10611/">Explore ARKit 4</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2020/10612/">What's new in RealityKit</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2020/10601/">The artist’s AR toolkit</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2020/10613">What's new in USD</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2020/10604">Shop online with AR Quick Look</a></li>
</ul>
<h2>Resources</h2>
<ul>
<li><a href="https://developer.apple.com/augmented-reality/">Augmented Reality - Apple Developer</a></li>
<li><a href="/en2019-10-07-Using-USDZ-for-a-better-AR-experience">Using USDZ for a better AR experience</a></li>
<li><a href="/en2019-12-31-How-to-make-an-Augmented-Reality-decorating-experience-app-with-AR-Quick-Look">How to make an augmented reality decorating experience app with AR Quick Look</a></li>
<li><a href="/en2020-04-26-how-to-convert-3d-models-to-usdz-files-using-apples-reality-converter">How to convert 3D models to USDZ files using Apple's Reality Converter</a></li>
<li><a href="https://www.apple.com/newsroom/2020/06/ipados-14-introduces-new-features-designed-specifically-for-ipad/">iPadOS 14 introduces new features designed specifically for iPad</a></li>
</ul>
<p><em>Article Photo by <a href="https://www.apple.com/newsroom/2020/06/ipados-14-introduces-new-features-designed-specifically-for-ipad/">Apple</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[MBT (Model-based testing)]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/06/23/MBT(Model-based-Testing)</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/06/23/MBT(Model-based-Testing)</guid>
            <pubDate>Tue, 23 Jun 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Model-based testing (MBT) is a technology that is sometimes advertised as "automated test design".</p>
<p>You can find the project on Github <a href="https://github.com/CXingL/MBT_CX">here</a>.</p>
<h2>Concept</h2>
<p>The MBT tool generates test cases from a test model specified by the user. Without the test model, the tool cannot generate any test cases. Without a good test model, the tool cannot generate good test cases. Therefore, in MBT testing, the task changed from test design to test model design. Rather than designing a single test set, we designed a test model for generating any number of test cases.</p>
<h4>Take Baidu search as an example</h4>
<ul>
<li>Make a model diagram <a href="https://github.com/GraphWalker/graphwalker-project/wiki/How-to-create-a-model">GraphWalker modeling rules</a>, and then put the model diagram file in the model folder.</li>
</ul>
<p>In this model diagram, start is the beginning of the model, enter homePage (<a href="http://www.baidu.com">www.baidu.com</a>), and then enter English or Chinese to search. After verifying the search results, it will return to homePage and continue to perform the search until the exit condition of the program is reached (for example, the entire path is executed or a certain node is reached or executed to a fixed number of times).
<img src="/assets/img/articles/2020-06-23-Model-based-Testing/model.webp" alt=""></p>
<ul>
<li>
<p>Execute <code>python3 run_model.py -t model/example.graphml</code> in the MBT directory to check whether the model graph is correct and confirm that there are no errors or endless loops;</p>
</li>
<li>
<p>Then execute <code>python3 run_model.py -f model/example.graphml</code>, a file example_web.py (model graph file name_test platform.py) will be generated in the page_script directory</p>
</li>
<li>
<p>Open the py file generated in the previous step and follow the selenium rules to complete the script:</p>
</li>
</ul>
<p><img src="/assets/img/articles/2020-06-23-Model-based-Testing/script.webp" alt=""></p>
<ul>
<li>After the script is completed, execute <code>pytest</code> in the MBT directory to start the test, and test the effect:</li>
</ul>
<p><img src="/assets/img/articles/2020-06-23-Model-based-Testing/example.webp" alt=""></p>
<p><em>You can open the test_main.py file for some simple settings before executing the test. For example, if you want to set the running browser, test execution speed, or more test files, you can choose to skip some tests in test_main, etc.</em></p>
<ul>
<li>After the test is completed, a test report will be generated in the report directory. Open report.html to view the test results.</li>
</ul>
<h2>Operating environment</h2>
<h4>1. yEd download (optional)</h4>
<p>Software for viewing and editing model diagrams. The model diagram files are all in the model folder, with the file suffix .graphml.</p>
<p>Download address: <a href="https://www.yworks.com/downloads-yEd">https://www.yworks.com/downloads-yEd</a></p>
<p>Can also be used online: <a href="https://www.yworks.com/yed-live/">https://www.yworks.com/yed-live/</a></p>
<h4>2. Install and configure the Java environment (required)</h4>
<p>Install and configure <a href="https://www.java.com/en/download/help/index_installing.xml">Java</a> environment, Java8 is recommended.</p>
<h4>3. python3 (required)</h4>
<p>Install and configure <a href="https://www.python.org/download/releases/3.0/">Python3</a> environment</p>
<h4>4. jq (required)</h4>
<p>Command line json processing tool to filter test cases generated by graphwalker, installation method: <code>brew install jq</code> (requires brew to be installed first). For other installation methods, please check the official documentation: <a href="https://stedolan.github.io/jq/">https://stedolan.github.io/jq/</a></p>
<h4>5. Selenium environment (web-side testing is required, Android and iOS are not required)</h4>
<p>Selenium: browser automatic test tool, after installing Python3, enter <code>pip3 install selenium</code> in the terminal to install</p>
<h4>6. chromedriver (required for testing Chrome on the web)</h4>
<ul>
<li>Download <a href="https://sites.google.com/a/chromium.org/chromedriver/downloads">https://sites.google.com/a/chromium.org/chromedriver/downloads</a></li>
<li>Move the uncompressed chromedriver to the <code>/usr/local/bin</code> directory</li>
</ul>
<h4>7. Appium environment (required for Android and iOS tests)</h4>
<p>There are many contents, Android and iOS are different, please check the official Appium documentation for details:
<a href="http://appium.io/docs/en/about-appium/getting-started/">http://appium.io/docs/en/about-appium/getting-started/</a></p>
<h4>8. Execute <code>pip3 install -r requirements.txt</code> in the MBT directory to install the libraries used in the framework</h4>
<h2>Usage</h2>
<h4>Web side</h4>
<ol>
<li>Modify the content in test_main.py, adjust some test parameters and select the content to be tested, etc. (the test that does not need to be executed can be uncommented by @pytest.mark.skip("nothing"))</li>
<li>Execute in the MBT folder: pytest starts the test</li>
<li>After the test is completed, the test report will be generated in the report folder</li>
</ol>
<h4>Android and iOS</h4>
<ol>
<li>Connect Android, iOS device or simulator, Android prepare REVINYL test apk, install iOS REVINYL</li>
<li>Start the Appium service</li>
<li>Modify the content in test_main.py, adjust some test parameters and select the content to be tested (
Android puts the test apk into the application folder, and changes the app in test_main.py to the name of the apk)</li>
<li>Execute in the MBT folder: pytest starts the test</li>
<li>After the test is completed, the test report will be generated in the report folder</li>
</ol>
<h4>Other</h4>
<p>If an error occurs during iOS use, it is similar to</p>
<blockquote>
<p>E selenium.common.exceptions.WebDriverException: Message: An unknown server-side error occurred while processing the command. Original error: Error Domain=com.facebook.WebDriverAgent Code=1 "The element'"Cancel" Button' is not visible on the screen and thus is not interactable" UserInfo={NSLocalizedDescription=The element'"Cancel" Button' is not visible on the screen and thus is not interactable}<br>
Please try to upgrade Appium or iOS version, see:
<a href="https://github.com/facebook/WebDriverAgent/issues/914">https://github.com/facebook/WebDriverAgent/issues/914</a></p>
</blockquote>
<p>@pytest.mark.skip("nothing") will skip the test, just comment it out</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/L5DxWLmywmM">freestocks</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Women who code connect digital - 2020]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/06/19/Women-who-code</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/06/19/Women-who-code</guid>
            <pubDate>Fri, 19 Jun 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>In June 2020, <a href="https://connectdigital.womenwhocode.dev/">WomenWhoCode</a> hosted a 3 days event for the women involved in the tech World. This is the 5th edition and the first time they did it completely virtually. And kudos for them that they managed to make it free of charge for everyone. As expected, the content of the event was so diverse and inspiring, that there's no way you could've left the conference space without any curiosity in one unknown area or with a question answered or even with the motivation to get involved more in this beautiful and alive community.</p>
<p>And, since we live in such unique times, every evening started with talks about burning subjects such as <em>mental health in times of Corona</em>, <em>black lives matter</em> and <em>diversity and acceptance</em>.</p>
<p><strong>Day 1 - Code</strong></p>
<p>The talks were grouped in such a way so all the WWC tracks were represented and the participants were able to follow subjects that grab the most their interest. They also encouraged everyone to connect, having separate "rooms" and sessions between talks, where participants were able to meet and discuss and build/extend the network of WWC :) . It was also fun to get involved in a "speed dating" format of 3 minutes random networking sessions or to compete to win conference t-shirts.</p>
<p>The day began with raising awareness of the gravity racism impacts our lives and what we can do about it to make the world a better place. Staying informed (don't tolerate iniquity just because it doesn't affect you directly), taking actions when injustice happens around you (inform authorities, signing petitions, writing emails to those in power are a just a few small things that each one of us can do).</p>
<p>After this emotional moment, the tech sessions started and all of the topics were almost entirely focused on different technologies that we use in our days. All of the talks are available <a href="https://www.youtube.com/playlist?list=PLVcEZG2JPVhc5GNgV_VQe80w-gJP_k3Ox">here</a>. Some of the topics:</p>
<ul>
<li>ReactJs vs VueJs</li>
<li>Serverless Data pipelines in AWS</li>
<li>Effective web apps monitoring, etc.</li>
</ul>
<p>The evening ended with advices on what we can do to give ourselves space in a very men dominant world, to make our voices heard and to diminish the inequality in tech carriers based on gender criteria.</p>
<p><strong>Day 2 - Career</strong></p>
<p>The second day was about how to encourage women to get into tech without having the fear that it's a men's world, untouchable, difficult or impossible to be part of. Topics like</p>
<ul>
<li>Mastering an interview with thinking in patterns (pointers, Heap, dynamic programming, etc)</li>
<li>Time complexity and data:
<ul>
<li>Big O Notations and how to calculate the complexity of different algorithms</li>
<li>Data structures: Hashmaps, Heaps, Trees you know, all those basic but not always simple theory :)</li>
</ul>
</li>
<li>Stories of women that changed career in their 40s
made the evening inspiring.</li>
</ul>
<p>Again, there were some very practical advices related to recruitment process:</p>
<ul>
<li>eliminate the pressure that recruiters can bring in their process to make you accept an offer (don't fall into the time pressure argument just to accept offers that don't suit your needs). Take your time and don't rush on accepting/denying an offer.</li>
<li>connect with the team before accepting an offer</li>
<li>during the technical interviews ask questions that will make the interviewers understand your logic. don't jump immediately in writing the code.</li>
</ul>
<p>Talks are available <a href="https://www.youtube.com/playlist?list=PLVcEZG2JPVhcqhyjIthVDmyKyyATGAMPC">here</a></p>
<p><strong>Day 3 - Community</strong></p>
<p>Third day was about giving something back to community. We live almost scary times and Covid-19 challenged us all physically, psychically, emotionally and financially. It is amazing to see how people united in this times to help those in needs, how much effort, energy and time some of these women wanted to sacrifice to feel that they truly help, that they contribute with something valuable to the well-being of their community. They worked hard, countless hours, many times unpaid to deliver tracing tools or platforms to help governmental systems to face this crisis.</p>
<p>And as it usually happens, everyone could discover other areas of interest and other events or learning sessions they can attend in the future. For me, this time it was machine learning. Another 3 sessions, introducing interested people into this subject.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Flutter size limit container summary]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/05/27/Flutter-size-limit-container-summary</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/05/27/Flutter-size-limit-container-summary</guid>
            <pubDate>Wed, 27 May 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Flutter's size-restricted container components include <code>ConstrainedBox</code>, <code>UnconstrainedBox</code>, <code>SizedBox</code>, <code>AspectRatio</code>, <code>FractionallySizedBox</code>, <code>LimitedBox</code>, and <code>Container</code>. These components can constrain the size of sub-components, one by one introduced below.</p>
<h2>ConstrainedBox</h2>
<p>The <code>ConstrainedBox</code> component constrains the maximum width and height of the subcomponents. If a component has a width and height of 300, it is wrapped in a <code>ConstrainedBox</code>, and a maximum width and height constraint is added to the <code>ConstrainedBox</code>. The usage is as follows:</p>
<pre><code class="hljs language-dart">ConstrainedBox (
  constraints: BoxConstraints (maxHeight: <span class="hljs-number">60</span>, maxWidth: <span class="hljs-number">200</span>),
  child: Container (height: <span class="hljs-number">300</span>, width: <span class="hljs-number">300</span>, color: Colors.red),
)
</code></pre>
<p>At this point, the subcomponent cannot break through the maximum width and height set by <code>BoxConstraints</code>, and the effect is as follows:</p>
<p>![image](/assets/img/articles/2020-05-27-Flutter-size-limit-container-summary/image1.webp</p>
<p>The default values of <code>BoxConstraints</code> are as follows:</p>
<pre><code class="hljs language-dart"><span class="hljs-keyword">const</span> BoxConstraints ({
  <span class="hljs-keyword">this</span>.minWidth = <span class="hljs-number">0.0</span>,
  <span class="hljs-keyword">this</span>.maxWidth = <span class="hljs-built_in">double</span>.infinity,
  <span class="hljs-keyword">this</span>.minHeight = <span class="hljs-number">0.0</span>,
  <span class="hljs-keyword">this</span>.maxHeight = <span class="hljs-built_in">double</span>.infinity,
});
</code></pre>
<p><code>BoxConstraints</code> provides convenient construction functions for developers to call, such as <code>BoxConstraints.tight (Size size)</code> and <code>BoxConstraints.expand ()</code>.
If <code>BoxConstraints</code> are nested, there are 2 <code>ConstrainedBoxes</code> as follows:</p>
<pre><code class="hljs language-dart">ConstrainedBox (
  constraints: BoxConstraints (maxHeight: <span class="hljs-number">60</span>, maxWidth: <span class="hljs-number">200</span>),
  child: ConstrainedBox (
    constraints: BoxConstraints (maxHeight: <span class="hljs-number">100</span>, maxWidth: <span class="hljs-number">240</span>),
    child: Container (height: <span class="hljs-number">300</span>, width: <span class="hljs-number">300</span>, color: Colors.red),
  ),
)
</code></pre>
<p>Taking the maximum width as an example, the <code>maxHeight</code> value of the first <code>BoxConstraints</code> is 60, which means that the maximum height of the child controls is 60, and the <code>maxHeight</code> value of the second <code>BoxConstraints</code> is 100. Since the second <code>BoxConstraints</code> is also subject to the first Constraints, so the maximum height of the second <code>BoxConstraints</code> can only be 60, the maximum height of the final subcomponent is 60, and the maximum width of the same is 200, so the final value of the multi-level <code>BoxConstraints</code> nested constraint maximum value is equal to multiple <code>BoxConstraints</code> constraints The minimum value. Similarly, the minimum value of nested constraints is equal to the maximum value of multiple <code>BoxConstraints</code> constraints.</p>
<h2>UnconstrainedBox</h2>
<p>The <code>UnconstrainedBox</code> component does not impose any constraints on the child components. For example, there is a parent component whose size is 200x200, and the child component is <code>UnconstrainedBox</code>. The <code>UnconstrainedBox</code> wraps a 300x300 component. The code is as follows:</p>
<pre><code class="hljs language-dart">Container (
  height: <span class="hljs-number">200</span>,
  width: <span class="hljs-number">200</span>,
  child: UnconstrainedBox (
    child: Container (height: <span class="hljs-number">300</span>, width: <span class="hljs-number">300</span>, color: Colors.red),
  ),
)
</code></pre>
<p>The effect is as follows:</p>
<p>![image](/assets/img/articles/2020-05-27-Flutter-size-limit-container-summary/image2.webp</p>
<p>Note: The yellow area indicates that the child control is beyond the area of the parent control. The yellow area will only exist in debug mode, and only the red area in release mode.
Although <code>UnconstrainedBox</code> does not limit the size of its child controls, it is still constrained by the parent control, and the area beyond the parent control will be intercepted.
<code>UnconstrainedBox</code> allows to set the alignment, the usage is as follows:</p>
<pre><code class="hljs language-dart">UnconstrainedBox (
  alignment: Alignment.topLeft,
  ...
)
</code></pre>
<p>The effect is as follows:</p>
<p>![image](/assets/img/articles/2020-05-27-Flutter-size-limit-container-summary/image3.webp</p>
<p>Compared with the previous picture, this time the left and upper sides did not exceed the area, and the right and lower sides exceeded 100px.</p>
<h2>SizedBox</h2>
<p><code>SizedBox</code> is a component with a fixed width and height, directly specify the specific width and height, the usage is as follows:</p>
<pre><code class="hljs language-dart">SizedBox (
  height: <span class="hljs-number">60</span>,
  width: <span class="hljs-number">200</span>,
  child: RaisedButton (
    child: Text (<span class="hljs-string">'this is SizedBox'</span>),
  ),
)
</code></pre>
<p>We can also set an infinite size, as follows:</p>
<pre><code class="hljs language-dart">SizedBox (
  height: <span class="hljs-built_in">double</span>.infinity,
  width: <span class="hljs-built_in">double</span>.infinity,
  ...
)
</code></pre>
<p>Although the infinite size is set, will the child controls be infinitely long? No, no, the child control will still be constrained by the parent component, will expand to the size of the parent component, and there is a convenient way to set this method:</p>
<pre><code class="hljs language-dart">SizedBox.expand (
  child: RaisedButton (
    child: Text (<span class="hljs-string">'this is SizedBox'</span>),
  ),
)
</code></pre>
<p><code>SizedBox</code> can have no sub-components, but it will still take up space, so <code>SizedBox</code> is very suitable for controlling the gap between 2 components, the usage is as follows:</p>
<pre><code class="hljs language-dart">Column (
  children: &#x3C;Widget> [
    Container (height: <span class="hljs-number">30</span>,),
    SizedBox (height: <span class="hljs-number">10</span>,),
    Container (height: <span class="hljs-number">30</span>,),
  ],
)
</code></pre>
<h2>AspectRatio</h2>
<p>The <code>AspectRatio</code> component is a component with a fixed aspect ratio. If the width of the component is fixed, the height is expected to be 1/2 of the width. <code>AspectRatio</code> can be used to achieve this effect.</p>
<pre><code class="hljs language-dart">AspectRatio (
  aspectRatio: <span class="hljs-number">2</span>/<span class="hljs-number">1</span>,
  child: Container (color: Colors.red),
)
</code></pre>
<p>The <code>aspectRatio</code> parameter is the aspect ratio. It can be written directly as a fraction or as a decimal, but it is recommended to write it as a fraction, which is more readable. The effect is as follows:</p>
<p>![image](/assets/img/articles/2020-05-27-Flutter-size-limit-container-summary/image4.webp</p>
<h2>FractionallySizedBox</h2>
<p>When we need a control whose size is relative, for example, the width of the current button accounts for 70% of the parent component, we can use <code>FractionallySizedBox</code> to achieve this effect.
Wrap the child control with <code>FractionallySizedBox</code>, set the <code>widthFactor</code> width coefficient or <code>heightFactor</code> height coefficient, the coefficient value range is 0-1, 0.7 means 70% of the parent component, the usage is as follows:</p>
<pre><code class="hljs language-dart">FractionallySizedBox (
  widthFactor: <span class="hljs-number">.7</span>,
  child: RaisedButton (
    child: Text (<span class="hljs-string">'button'</span>),
  ),
)
</code></pre>
<p>The position of the sub-component is controlled by the <code>alignment</code> parameter. The default is center, and the usage is as follows:</p>
<pre><code class="hljs language-dart">FractionallySizedBox (
  alignment: Alignment.centerLeft,
  ...
)
</code></pre>
<p>If you want the interval between 2 controls to be 10% of the current parent control, you can use <code>FractionallySizedBox</code> without child controls, the usage is as follows:</p>
<pre><code class="hljs language-dart">Container (
  height: <span class="hljs-number">200</span>,
  color: Colors.grey,
  child: Column (
    children: &#x3C;Widget> [
      Container (
        height: <span class="hljs-number">50</span>,
        color: Colors.red,
      ),
      Flexible (
        child: FractionallySizedBox (
          heightFactor: <span class="hljs-number">.1</span>,
        ),
      ),
      Container (
        height: <span class="hljs-number">50</span>,
        color: Colors.blue,
      ),
    ],
  ),
)
</code></pre>
<p>The effect is as follows:</p>
<p>![image](/assets/img/articles/2020-05-27-Flutter-size-limit-container-summary/image5.webp</p>
<h2>LimitedBox</h2>
<p>The <code>LimitedBox</code> component limits its size when it is not constrained by the parent component. What does it mean by not being constrained by the parent component? Like the other components introduced in this article, they will constrain the child components. The unconstrained parent components are <code>ListView</code>, <code>Row</code>, <code>Column</code>, etc. If the parent component of the <code>LimitedBox</code> is constrained, the <code>LimitedBox</code> will not do anything at this time. Think that without this component, the code is as follows:</p>
<pre><code class="hljs language-dart">Container (
  height: <span class="hljs-number">100</span>,
  width: <span class="hljs-number">100</span>,
  child: LimitedBox (
    maxHeight: <span class="hljs-number">50</span>,
    maxWidth: <span class="hljs-number">100</span>,
    child: Container (color: Colors.green,),
  ),
)
</code></pre>
<p>The effect is as follows:</p>
<p>![image](/assets/img/articles/2020-05-27-Flutter-size-limit-container-summary/image6.webp</p>
<p>The width and height set by the <code>LimitedBox</code> are not square. At this time, the effect is square, indicating that the <code>LimitedBox</code> has no effect.
Add the <code>Container</code> component directly in the <code>ListView</code> as follows:</p>
<pre><code class="hljs language-dart">ListView (
  children: &#x3C;Widget> [
    Container (
      color: Colors.green,
    ),
    Container (
      color: Colors.red,
    ),
  ],
)
</code></pre>
<p>At this time, you will find that there is nothing, because when the container is not constrained, the size will be set to 0, just wrap the <code>Container</code> in the <code>LimitedBox</code>:</p>
<pre><code class="hljs language-dart">ListView (
  children: &#x3C;Widget> [
    LimitedBox (
      maxHeight: <span class="hljs-number">100</span>,
      child: Container (
        color: Colors.green,
      ),
    ),
    LimitedBox (
      maxHeight: <span class="hljs-number">100</span>,
      child: Container (
        color: Colors.red,
      ),
    ),
  ],
)
</code></pre>
<p>effect:</p>
<p>![image](/assets/img/articles/2020-05-27-Flutter-size-limit-container-summary/image7.webp</p>
<h2>Container</h2>
<p>The <code>Container</code> component should be one of the most commonly used components. The <code>Container</code> component can directly set its width and height. The usage is as follows:</p>
<pre><code class="hljs language-dart">Container (
  height: <span class="hljs-number">100</span>,
  width: <span class="hljs-number">100</span>,
  ...
)
</code></pre>
<p>The <code>Container</code> component is the one with the most attributes in these components, and of course the one with the most complex usage. There is an opportunity to write another article introduction, <a href="https://api.flutter.dev/flutter/widgets/Container-class.html">Official Document</a>.</p>
<h2>Summary</h2>
<p>With so many constrained container components, which one should you use? Summarized as follows:</p>
<p><code>ConstrainedBox</code>: It is suitable for setting the maximum / small width and height, the size of the sub-component since the component size, but it cannot exceed the set limit.<br>
<code>UnconstrainedBox</code>: Not used much, as a child component of ConstrainedBox can "break through" the constraints of ConstrainedBox, and parts that exceed the limit will be intercepted.<br>
<code>SizedBox</code>: It is suitable for the case of fixed width and height, and is often used as a gap between two components.<br>
<code>AspectRatio</code>: Applicable to fixed aspect ratio.<br>
<code>FractionallySizedBox</code>: Applies to the percentage of parent components.<br>
<code>LimitedBox</code>: Applicable when there is no parent component constraint.<br>
<code>Container</code>: Applicable to situations where there are not only size constraints, but also decorations (colors, borders, etc.), inner and outer margins, etc.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[LLDB summary]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/05/27/LLDB-summary</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/05/27/LLDB-summary</guid>
            <pubDate>Wed, 27 May 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>LLDB，Low Level Debugger, is a lightweight, high-performance debugger that is built into Xcode by default. Being able to use it well will make our development more efficient with less effort.</p>
<p>We often use LLDB's <code>po</code> / <code>breakpoint</code> in development, you can get a lot done with only those two tools. But this is just a small part of LLDB.</p>
<p>This article introduces the basis of LLDB and also shows how we can make it even more powerful by using an extension of LLDB.</p>
<h2>LLDB basis</h2>
<h3><code>p</code>, <code>po</code>, <code>print</code>, <code>expression</code></h3>
<p>Let's start by introducing the relationship between these common commands.</p>
<p><code>expression</code> is described as follows:</p>
<p>Evaluate an expression on the current thread. Displays any returned value with LLDB's default formatting.</p>
<p>It can be abbreviated as <code>e</code>.</p>
<p><img src="/assets/img/articles/2020-05-27-LLDB-summary/1.webp" alt=""></p>
<p>We can see that <code>expression</code> has the following main functions:</p>
<ol>
<li>Print information about object.</li>
<li>The statement execution, such as: <code>expression a = 100</code>, same here, you can try <code>expression self.view.layer.backgroundColor = [UIColor redColor].cgColor</code>, also can change the background color(Use <code>expression -- (void)[CATransaction flush]</code> to refresh UI).</li>
<li>Define and use the LLDB variable through the <code>$</code>, such as: <code>expression int $b = 99</code>.</li>
</ol>
<p><img src="/assets/img/articles/2020-05-27-LLDB-summary/1.webp" alt=""></p>
<p>Maybe <code>p</code> is too easy to associate with <code>print</code>, many people will think that <code>p</code> is the abbreviation of <code>print</code>, and <code>po</code> is short for <code>print object</code>, but it is not.</p>
<p><code>p</code> and <code>print</code> are both short for <code>expression --</code>，as can be viewed using the <code>help</code> command.</p>
<p><img src="/assets/img/articles/2020-05-27-LLDB-summary/2.webp" alt=""></p>
<p><code>po</code> is also not short for <code>print object</code> (There are no commands named <code>print object</code>), it's an abbreviation for <code>expression -O --</code>. <code>expression -O --</code> means the description of an object, which print the description of a variable.</p>
<p><img src="/assets/img/articles/2020-05-27-LLDB-summary/3.webp" alt=""></p>
<p><img src="/assets/img/articles/2020-05-27-LLDB-summary/4.webp" alt=""></p>
<p>Therefore, select the appropriate print command as needed in the actual development.</p>
<p>When printing the value of the variable, we can also use <code>print/&#x3C; FMT ></code> or the simplified <code>p/&#x3C; FMT ></code> to specify the print format, such as printing hexadecimal:</p>
<p><img src="/assets/img/articles/2020-05-27-LLDB-summary/6.webp" alt=""></p>
<p><code>x</code> stands for hexadecimal format and <code>t</code> for binary format. For other format types, click <a href="https://sourceware.org/gdb/onlinedocs/gdb/Output-Formats.html">here</a>.</p>
<h3><code>breakpoint</code></h3>
<p>The parameters commonly used to set breakpoints and their meanings are as follows:</p>



































<table><thead><tr><th>Short</th><th>Full</th><th>Meaning</th></tr></thead><tbody><tr><td>-f</td><td>--file</td><td>The file name</td></tr><tr><td>-l</td><td>--line</td><td>Line number</td></tr><tr><td>-n</td><td>--name</td><td>The method name</td></tr><tr><td>-S</td><td>--selector</td><td>SEL</td></tr><tr><td>-r</td><td>--func-regex</td><td>Methods regular</td></tr></tbody></table>
<p>For example：</p>
<ul>
<li>
<p>Set a breakpoint at line 28 of the <code>ViewController.m</code>.</p>
<p><code>breakpoint set -f ViewController.m -l 28</code></p>
</li>
<li>
<p>Set a breakpoint for method <code>click1:</code>.</p>
<p><code>breakpoint set -n click1:</code></p>
</li>
<li>
<p>Set breakpoints for SEL's <code>click2:</code>.</p>
<p><code>breakpoint set -S click2:</code></p>
</li>
<li>
<p>breakpoints where click is included.</p>
<p><code>breakpoint set -r click</code></p>
</li>
<li>
<p>Set a breakpoint where the click is included in the <code>ViewController.m</code>.</p>
<p><code>breakpoint set -f ViewController.m -r click</code></p>
<p><code>breakpoint set</code> can be written as <code>b</code>.</p>
</li>
<li>
<p>View all current breakpoints</p>
<p><code>breakpoint list // short: br list</code></p>
</li>
<li>
<p>Set the breakpoint enable/disable/delete</p>
<p><code>br enable 2.1</code></p>
<p><code>br disable 2.1</code></p>
<p><code>br delete 2.1</code> (Because of that a single breakpoint in a breakpoint group cannot be deleted separately. Just disable the first breakpoint of the second breakpoint group.)</p>
<p><code>br delete 2</code> (Delete the second breakpoint group.)</p>
</li>
</ul>
<p><img src="/assets/img/articles/2020-05-27-LLDB-summary/5.webp" alt=""></p>
<h2>Other common commands for LLDB</h2>
<h4><code>image list</code></h4>
<p>The word "image" here does not mean a picture.
It can be understood that each MachO is an image, the main program is an image, and each dynamic library linked by the main program is an image.</p>
<p>The image list is to print out all the image information in the App, and the address of each image information is the first address of this image in memory, namely the ASLR of this image.</p>
<h4><code>bt</code></h4>
<p>To view the function stack.</p>
<p>Example:</p>
<pre><code class="hljs language-objectivec">- (<span class="hljs-keyword">IBAction</span>)click1:(<span class="hljs-type">id</span>)sender {
    [<span class="hljs-keyword">self</span> click2:sender];
}

- (<span class="hljs-type">void</span>)click2:(<span class="hljs-type">id</span>)sender {
    [<span class="hljs-keyword">self</span> click3:sender];
}

- (<span class="hljs-type">void</span>)click3:(<span class="hljs-type">id</span>)sender {
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"Set a breakpoint here"</span>);
}
</code></pre>
<p>Enter <code>bt</code> to see the call relationship between functions.</p>
<p>Use frame <code>select [stack number]</code> to view the details of the stack, including the memory address of the caller, the method called, the memory address of the parameters, and so on.</p>
<p><img src="/assets/img/articles/2020-05-27-LLDB-summary/7.webp" alt=""></p>
<p>You can also use the <code>up</code> and <code>down</code> commands to see information about adjacent call stacks.</p>
<p><img src="/assets/img/articles/2020-05-27-LLDB-summary/8.webp" alt=""></p>
<h4><code>c</code>, <code>n</code>, <code>s</code>, <code>finish</code></h4>
<p><img src="/assets/img/articles/2020-05-27-LLDB-summary/9.webp" alt=""></p>
<ul>
<li><code>c</code></li>
</ul>
<p>Which means continue.</p>
<p>Cancels the suspension of the program, allowing the program to execute normally (either all the way down or at the next breakpoint).</p>
<p>We can also use the <code>process continue</code> or <code>continue</code> command in LLDB.</p>
<ul>
<li><code>n</code></li>
</ul>
<p>Which means step over.</p>
<p>Execute a line of code as a black box. If this line of code is a function call, instead of jumping into the function, it will execute the function and continue.</p>
<p>We can also use the <code>thread step-over</code> or <code>next</code> command.</p>
<ul>
<li><code>s</code></li>
</ul>
<p>Which means step into.</p>
<p>If we want to jump into a function call to debug or check the program's execution. When the current line is not a function call, the <code>n</code> and <code>s</code> effects are the same.</p>
<p>We can also use the <code>thread step-in</code> or <code>step</code> command.</p>
<ul>
<li><code>finish</code></li>
</ul>
<p>Which means step out.</p>
<p>If we've ever accidentally jumped into a function and you actually want to skip it, the common reaction is to repeatedly run <code>n</code> until the function returns. In this case, the step out continues to the next return statement (until a stack frame ends) and then stops again.</p>
<p>We can also use the <code>thread step-out</code> command.</p>
<h2>LLDB extension</h2>
<p>All of the above are the functions of LLDB that come with Xcode.</p>
<p>Next is the use of plugins to extend LLDB to make it simpler and more powerful.</p>
<h4>Chisel</h4>
<p>Chisel is a collection of LLDB commands to assist in the debugging of iOS apps.</p>
<p>Install Chisel using homebrew:</p>
<pre><code class="hljs language-sql">brew <span class="hljs-keyword">update</span>
brew install chisel
</code></pre>
<p>If <code>.lldbinit</code> file doesn't exist you can create it and open it from the terminal.</p>
<pre><code class="hljs language-arduino">touch .lldbinit
open .lldbinit
</code></pre>
<p>Then add the following line to your <code>~/.lldbinit</code> file.</p>
<pre><code class="hljs language-groovy"># ~/.lldbinit
...
command script <span class="hljs-keyword">import</span> <span class="hljs-regexp">/usr/</span>local<span class="hljs-regexp">/Cellar/</span>chisel<span class="hljs-regexp">/2.0.0/</span>libexec/fblldb.py
</code></pre>
<p>Alternatively, download Chisel and add the following line to your <code>~/.lldbinit</code> file.</p>
<pre><code class="hljs language-bash"><span class="hljs-comment"># ~/.lldbinit</span>
...
<span class="hljs-built_in">command</span> script import /path/to/fbchisellldb.py
</code></pre>
<p>The commands will be available the next time <code>Xcode</code> starts.</p>
<p>Let's try out some of Chisel's extensions to LLDB.</p>
<ul>
<li><strong>pviews</strong></li>
</ul>
<p>Recursively retrieves all view class objects and prints them out according to the view hierarchy.</p>
<p><img src="/assets/img/articles/2020-05-27-LLDB-summary/10.webp" alt=""></p>
<ul>
<li><strong>pvc</strong></li>
</ul>
<p>Prints all current controller objects and hierarchical relationships.</p>
<p>Let's try to jump to <code>NextViewController</code> when we click on the screen blank.</p>
<pre><code class="hljs language-objectivec">-(<span class="hljs-type">void</span>)touchesBegan:(<span class="hljs-built_in">NSSet</span>&#x3C;<span class="hljs-built_in">UITouch</span> *> *)touches withEvent:(<span class="hljs-keyword">nullable</span> <span class="hljs-built_in">UIEvent</span> *)event {
    <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@"Move to another viewcontroller here"</span>);
    NextViewController *nextVC = [[NextViewController alloc] init];
    [<span class="hljs-keyword">self</span>.navigationController pushViewController:nextVC animated:<span class="hljs-literal">YES</span>];
}
</code></pre>
<p><img src="/assets/img/articles/2020-05-27-LLDB-summary/11.webp" alt=""></p>
<ul>
<li><strong>caflush</strong></li>
</ul>
<p>To refresh the UI, we may change the layout of the UI controls during debugging, and then directly use the caflush to refresh.</p>
<p>It is the same as <code>expression -- (void)[CATransaction flush]</code></p>
<ul>
<li><strong>fv</strong> &#x26; <strong>fvc</strong></li>
</ul>
<p>Find a view/view controller in the hierarchy whose class name matches the provided regex.</p>
<p><img src="/assets/img/articles/2020-05-27-LLDB-summary/12.webp" alt=""></p>
<ul>
<li><strong>taplog</strong></li>
</ul>
<p>Input taplog, and you'll see that the program is working. Click on any button, and it will print out the information about the button you clicked.</p>
<p><img src="/assets/img/articles/2020-05-27-LLDB-summary/13.webp" alt=""></p>
<p>This is very helpful for reverse debugging, locating directly to the memory address of the control you clicked on. With the memory address, you can do a lot of things.</p>
<ul>
<li><strong>presponder</strong></li>
</ul>
<p>Print out the responder response chain.</p>
<p><img src="/assets/img/articles/2020-05-27-LLDB-summary/14.webp" alt=""></p>
<ul>
<li><strong>pclass</strong></li>
</ul>
<p>Prints the inheritance relationship of the class to which the object belongs.</p>
<p><img src="/assets/img/articles/2020-05-27-LLDB-summary/15.webp" alt=""></p>
<ul>
<li><strong>pactions</strong></li>
</ul>
<p>Find the actions for the button response directly from the memory address of the button.</p>
<p><img src="/assets/img/articles/2020-05-27-LLDB-summary/16.webp" alt=""></p>
<ul>
<li><strong>pmethods</strong></li>
</ul>
<p>Print the class and instance methods of a class. Similar to <code>class-dump</code>.</p>
<p><img src="/assets/img/articles/2020-05-27-LLDB-summary/17.webp" alt=""></p>
<ul>
<li><strong>flicker</strong></li>
</ul>
<p>Let the control corresponding to the memory address flash on the phone.</p>
<ul>
<li><strong>vs</strong></li>
</ul>
<p>Make the memory address corresponding to the control to translucent red, and enter edit mode, use</p>
<ul>
<li>w: Navigate to the parent view of the current view.</li>
<li>s: Navigate to the first subview of the current view.</li>
<li>a: Navigate to the previous sibling view of the current view.</li>
<li>d: Navigate to the next sibling of the current view.</li>
<li>p: Prints the information of the view located.</li>
<li>q: Quit edit mode.</li>
</ul>
<p><img src="/assets/img/articles/2020-05-27-LLDB-summary/2.webp" alt=""></p>
<p>In addition, more functions can be referred to:</p>
<p><a href="https://github.com/facebook/chisel">https://github.com/facebook/chisel</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Tagging feedback messages using Deep Learning]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/05/25/Using-deep-learning-to-label-feedback-messages</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/05/25/Using-deep-learning-to-label-feedback-messages</guid>
            <pubDate>Mon, 25 May 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>At Nodes we use Deep Learning to classify thousands of user-generated feedback messages across our digital solutions. We do this to create actionable insights for our clients, giving them a way to decipher large amounts of data that otherwise would be very expensive to manually label.</p>
<p>You should read this if you are interested in</p>
<ul>
<li>Machine Learning in production</li>
<li>Natural Language Processing</li>
<li>TensorFlow using Keras</li>
<li>Hand-drawn illustrations of questionable aesthetic quality</li>
</ul>
<h2>What's this about?</h2>
<p>At Nodes we collect large amounts of user generated data through our feedback module. This is an optional module that some of our clients choose to activate in the applications we make for them. The module can be set to prompt users to provide feedback at specific times, like after a period of sustained use, at a certain date, or when some use-pattern emerges.</p>
<p>Since 2017 this module has been active in several of our applications, and since then <strong>42000</strong> feedback messages have made their way through to our system, and this number keeps increasing every day.</p>
<p>The end-goal of this solution is to offer our clients a detailed overview of how their applications are doing, as phrased by the users. Ultimately, this allows for a data-driven approach to development, where product owners can prioritise the bugs that are most frequency reported, and as solutions are released they can monitor the development of certain kinds of user-reported issues.</p>
<p>While this solution requires several parts, this blogpost will focus on machine learning. Because the data is text messages, we will be looking at how to process text data with <strong>Natural Language Processing</strong>. The essence of NLP is to make words, sentences or entire documents into vectors so we can apply standard machine learning methods to them.</p>
<h2>Why are we doing this?</h2>
<p>Our primary focus with this project is to empower our clients with valuable and actionable insights based on the messages users provide. To achieve this we identified two important questions our solution needed to address:</p>
<ol>
<li>What is the core content of this message?</li>
<li>What kind of specific functionality - if any - is this message about?</li>
</ol>
<p>In our analysis of the data, which I will expand on later, we identified four different kinds of messages: <strong>bugs</strong>, <strong>usability</strong>, <strong>update</strong> and <strong>feedback</strong>.</p>
<ul>
<li>
<p><strong>Bugs</strong> are usually the messages that underline that some functionality is not working as intended, or that an interaction with the application was unsuccessful.</p>
</li>
<li>
<p><strong>Usability</strong> messages are often directed at how users find interaction unintuitive, or can't find certain functionality where they expected it.</p>
</li>
<li>
<p><strong>Update</strong> messages are characterised by users pointing out that a functionality used to work and that a recent update to the solution broke this functionality, or made is less user-friendly.</p>
</li>
<li>
<p><strong>Feedback</strong>, are the rare messages where users take their time and suggest new functionality or changes to the solution.</p>
</li>
</ul>
<p>Take these example sentences:</p>
<p><img src="/assets/img/articles/2020-05-25-Using-deep-learning-to-label-feedback-messages/example_sentences.webp" alt="Example sentences"></p>
<p>As you can tell, both sentences have aspects of bugs and usability issues. Because of the nature of these types of multifaceted messages, it makes a lot of sense to allow for multiple labels.</p>
<p>For example, a bug can come around as a consequence of an update, so it seems appropriate to have both the <strong>bug</strong> and <strong>update</strong> label. In machine learning lingo, when something we want to classify has several labels, we call it a <strong>multi-label problem</strong>.</p>
<p>As you can imagine, reading, understanding and labelling thousands of messages is a daunting manual task. So, instead of spending precious resources on manual labour, why not let a machine do the work?</p>
<h2>The solution (Yes, its deep learning)</h2>
<p>Since this is a ML post, I wont go through the details of production, but quickly summarise the core of the infrastructure.</p>
<p><img src="/assets/img/articles/2020-05-25-Using-deep-learning-to-label-feedback-messages/aws_setup.webp" alt="AWS Lambda setup"></p>
<p>In short, messages come in from all our different solutions and are stored in our backend (NStack). For each message, NStack sends the free-form message to the ML API, that lives in a AWS Lambda function, where the four steps below are executed.</p>
<ol>
<li>
<p>Detects the input language.</p>
</li>
<li>
<p>Translates the message to english.</p>
</li>
<li>
<p>Returns the translated message, the predicted label and any keywords that pertain to functionality.</p>
</li>
<li>
<p>This is stored in NStack and used as data in a dashboard.</p>
</li>
</ol>
<p>The API and ML model lives in the cloud as a serverless AWS Lambda function. How to deploy models like this on AWS Lambda will be the focus of a later blog-post, so stay tuned.</p>
<h3>Using semi-supervised clustering to generate labels</h3>
<p>Our first problem is that the data was not annotated. This means that we dont know what categories the messages belong to. This information is essential, because that's the data we want to use to train our model.</p>
<p>To solve this problem, we used a simple approach of generating labels based clustering and then confirming edge cases manually. Hence the name <em>semi-supervised</em> because the labels are generated part supervised (manually) and part unsupervised (clustering).</p>
<p>Before we can cluster anything, we need to turn our messages into something our clustering algorithms can understand. We often call this step preprocessing.</p>
<h3>Preprocessing</h3>
<p>With NLP problems, preprocessing can be quite different depending on what you’re trying to achieve. It's beyond the scope of this post to explain all the nuances, but I encourage anyone interesting in starting with NLP to check out this excellent <a href="https://course.spacy.io/en/chapter1">guide to spaCy</a> a NLP framework for Python.</p>
<p>Let's get get into it!</p>
<p>To do simple clustering, we need to complete three key steps:</p>
<ol>
<li>Clean each message</li>
<li>Remove stop words in each sentence</li>
<li>Tokenise the sentences</li>
</ol>
<p>Each of those steps can be illustrated with simple examples.</p>
<p>Consider this made-up sentence:</p>
<blockquote>
<p>“<em>I cant find the bøtton that closes my 5,, tabs when they are OPEN!!</em>”</p>
</blockquote>
<p>Cleaning the text is basically about standardisation. Removing superfluous characters such as <code>!/&#x26;#</code>, numbers, extra spaces, making everything lowercase, etc. So the sentence becomes:</p>
<blockquote>
<p>“<em>i cant find the btton that closes my tabs when they are open</em>”</p>
</blockquote>
<p>Stop words are words like “is”, “an”, “the” etc. They often bring little information to our model, so we remove them. After removing stop words, the sentence would look like:</p>
<blockquote>
<p>“<em>i cant find btton closes my tabs open</em>”</p>
</blockquote>
<p>Tokenization is the task of chopping up a sentence into pieces called <strong>tokens</strong>. For our sentence, there are eight unique tokens because there are eight different words after removing the stop words. If the same word appears twice, it will be counted in the hash table with frequency two.</p>
<p>Before we can use clustering, we apply a method called <a href="http://www.tfidf.com/">TFIDF</a> (term frequency–inverse document frequency). This method is a product of how frequent a word is in a document, multiplied with how unique (inverse frequency) it is in the entire collection of messages.</p>
<p>Using the TFIDF distribution, we apply a simple K-Means clustering algorithm to identify the clusters. As illustrated below, we're trying to get our algorithm to draw a sensible squiggly line that separates messages in a manner that is semantically meaningful.</p>
<p><img src="/assets/img/articles/2020-05-25-Using-deep-learning-to-label-feedback-messages/cluster_1.webp" alt="Message clusters"></p>
<p>After some experimentation with defining the clusters and manual inspection of sentences, we decide our four main clusters are <strong>bug, usability, update</strong> and <strong>feedback.</strong></p>
<p>As an example of what we found in the clusters, consider the sentence</p>
<blockquote>
<p>“<em>I dont like the new drop down menu.</em>”</p>
</blockquote>
<p>This sentence is semantically different from</p>
<blockquote>
<p>“<em>The new drop down menu does not work</em>”</p>
</blockquote>
<p>because it allures to usability or design issue (the user does not like the design) versus a bug (the menu does not work).</p>
<p>The rest of the effort here is to manually go through most of the sentences where clusters overlap and manually decide on their labels.</p>
<p><img src="/assets/img/articles/2020-05-25-Using-deep-learning-to-label-feedback-messages/cluster_2.webp" alt="Contested labels"></p>
<p>We manually went through approximately 1000 sentences and labelled them manually. This means that <strong>we labelled less than 2 percent of our data manually</strong> and 98% automatically.</p>
<h3>The Deep Neural Network</h3>
<p>After the step above, we have the labels we need to train our machine learning model.</p>
<p>Now you might ask: Why not just use the unsupervised method to classify future messages as well?</p>
<p>Well, we can't be bothered to run our semi-supervised approach every single time a new message comes into our system. The reason we cant be bothered to do this is because K-Means clustering is a <strong>greedy</strong> algorithm with a time complexity of $$O(n^2)$$, which is computer science language for slow.</p>
<p>Also, we manually tagged a lot of messages, so we would like our machine learning model to: <strong>a</strong>) Learn the patterns that our KMeans clustering found but still be computationally cheap to run inference on. <strong>b</strong>) Be able to learn from the messages we tagged manually.</p>
<p>To achieve this we use a <strong>Long Short-Term Memory</strong> (LSTM) model. This is a specific type of <a href="https://en.wikipedia.org/wiki/Recurrent_neural_network">Recurrent Neural Network</a> (RNN) that - unlike feed forward neural networks such as the <a href="https://en.wikipedia.org/wiki/Perceptron">Perceptron</a> - allows for <strong>feedback connections</strong>. Feedback connections in sequence allow for memory, something a feed forward network does not have.</p>
<p>Deep learning models that have memory are very useful when the <em>order of the input matters</em>, which is very much the case for sentences (unless you are Yoda).</p>
<p>![Example sentences](/assets/img/articles/2020-05-25-Using-deep-learning-to-label-feedback-messages/yoda.webp</p>
<p>Let's do as Master Yoda says and remember our imports.</p>
<pre><code class="hljs language-python"><span class="hljs-keyword">import</span> tensorflow <span class="hljs-keyword">as</span> tf
<span class="hljs-keyword">import</span> numpy <span class="hljs-keyword">as</span> np

<span class="hljs-keyword">from</span> tensorflow.keras.preprocessing.text <span class="hljs-keyword">import</span> Tokenizer
<span class="hljs-keyword">from</span> tensorflow.keras.preprocessing.sequence <span class="hljs-keyword">import</span> pad_sequences
<span class="hljs-keyword">from</span> tensorflow.keras.models <span class="hljs-keyword">import</span> Sequential
<span class="hljs-keyword">from</span> tensorflow.keras.layers <span class="hljs-keyword">import</span> Dense, Dropout, Activation, Embedding, SpatialDropout1D, LSTM
<span class="hljs-keyword">from</span> tensorflow.keras.optimizers <span class="hljs-keyword">import</span> SGD
<span class="hljs-keyword">from</span> tensorflow.keras.callbacks <span class="hljs-keyword">import</span> EarlyStopping

<span class="hljs-keyword">import</span> tensorflow_docs <span class="hljs-keyword">as</span> tfdocs
<span class="hljs-keyword">import</span> tensorflow_docs.plots
<span class="hljs-keyword">import</span> tensorflow_docs.modeling


<span class="hljs-keyword">from</span> sklearn.model_selection <span class="hljs-keyword">import</span> train_test_split

<span class="hljs-keyword">import</span> matplotlib.pyplot <span class="hljs-keyword">as</span> plt
</code></pre>
<h3>Steps before training</h3>
<p>Building a deep learning model with LSTM layers is relatively simple with the <a href="https://www.tensorflow.org/guide/keras">Keras API for TensorFlow 2.0</a>.</p>
<p>From their own site:</p>
<blockquote>
<p><code>tf.keras</code> is TensorFlow's high-level API for building and training deep learning models. It's used for fast prototyping, state-of-the-art research, and production, ...</p>
</blockquote>
<p>To train the model, we apply the same preprocessing steps as we did when we did the clustering, with one additional step.</p>
<p>Before training the model, <strong>all sentences need to be equal length</strong>. Thos is done by choosing a maximum length that a sentence can be, and then either removing tokens or padding them with zeros.</p>
<p>The following code shows the four steps: Cleaning, removing stop words, tokenisation and padding.</p>
<pre><code class="hljs language-python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">get_train_test_sequences</span>(<span class="hljs-params">messages, labels</span>):

    tokenized_messages = []
    tokenized_labels = []

    <span class="hljs-keyword">for</span> m, l <span class="hljs-keyword">in</span> <span class="hljs-built_in">zip</span>(messages, labels):
        tm, ll = remove_stopwords(m, l)

        tokenized_messages.append(tm)
        tokenized_labels.append(ll)

    X_train, X_test, y_train, y_test = train_test_split(tokenized_messages, tokenized_labels, test_size=<span class="hljs-number">0.2</span>, random_state=<span class="hljs-number">2020</span>)

    tokenizer = Tokenizer(num_words = VOCAB_SIZE, oov_token=OOV_TOK)
    tokenizer.fit_on_texts(X_train)

    word_index = tokenizer.word_index

    train_sequences = tokenizer.texts_to_sequences(X_train)
    train_padded = pad_sequences(train_sequences, maxlen=MAX_LENGTH, padding=PADDING_TYPE, truncating=TRUNC_TYPE)

    <span class="hljs-built_in">print</span>(<span class="hljs-built_in">len</span>(train_sequences))
    <span class="hljs-built_in">print</span>(train_padded.shape)

    test_sequences = tokenizer.texts_to_sequences(X_test)
    test_padded = pad_sequences(test_sequences, maxlen=MAX_LENGTH, padding=PADDING_TYPE, truncating=TRUNC_TYPE)

    <span class="hljs-built_in">print</span>(<span class="hljs-built_in">len</span>(test_sequences))
    <span class="hljs-built_in">print</span>(test_padded.shape)

    <span class="hljs-keyword">return</span> train_padded, test_padded, np.array(y_train), np.array(y_test)
</code></pre>
<p>This function takes in clean messages, removes stop words, splits the data into test and training sets, tokenises, adds padding and returns them.</p>
<p>Looking at a randomly chosen sentence after these steps, we see that it is arranged as sequence of tokens (just numbers) followed by zeros until we get to the desired padding length of 20. No matter how long our sentences are, they will be represented by a length of 20 tokens.</p>
<pre><code class="hljs language-python">array([   <span class="hljs-number">5</span>, <span class="hljs-number">1039</span>, <span class="hljs-number">1657</span>,   <span class="hljs-number">82</span>,  <span class="hljs-number">227</span>,  <span class="hljs-number">562</span>,  <span class="hljs-number">229</span>,  <span class="hljs-number">855</span>,   <span class="hljs-number">65</span>, <span class="hljs-number">227</span>,   <span class="hljs-number">18</span>, <span class="hljs-number">0</span>,    <span class="hljs-number">0</span>,    <span class="hljs-number">0</span>,    <span class="hljs-number">0</span>,    <span class="hljs-number">0</span>,    <span class="hljs-number">0</span>,    <span class="hljs-number">0</span>,    <span class="hljs-number">0</span>,    <span class="hljs-number">0</span>], dtype=int32)
</code></pre>
<p>Arrays like the above one is the training input for our LSTM model. Similarly, the training labels are simply lists that correspond to one, or several, of the labels, like this:</p>
<pre><code class="hljs language-python">array([<span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>])
</code></pre>
<p>Where <code>array([1, 0, 0, 0])</code> is binary encoding for <code>bug</code>. Remember, that a message can have several tags, so this could also look like this:</p>
<pre><code class="hljs language-python">array([<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>])
</code></pre>
<p>Which would mean the sentence was both a bug and a usability issue.</p>
<p>Now we are ready for training!</p>
<h3>Building and training the LSTM</h3>
<p>As mentioned earlier, Keras is a high-level API for TensorFlow that allows putting a lot of functionality into a few lines of code. The real model that we use in production is slightly more complicated, but I have removed some complexity in order to make it easier to explain.</p>
<p>Thus, behold a minimal viable example of the model training code:</p>
<pre><code class="hljs language-python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">train_model</span>():

    model = Sequential()
    model.add(Embedding(VOCAB_SIZE, EMBEDDING_DIM, input_length=train_padded.shape[<span class="hljs-number">1</span>]))
    model.add(SpatialDropout1D(<span class="hljs-number">0.2</span>))
    model.add(LSTM(<span class="hljs-number">100</span>, dropout=<span class="hljs-number">0.2</span>, recurrent_dropout=<span class="hljs-number">0.2</span>))
    model.add(Dense(y_train.shape[<span class="hljs-number">1</span>], activation=<span class="hljs-string">'sigmoid'</span>))

    sgd = SGD(lr=<span class="hljs-number">0.01</span>, decay=<span class="hljs-number">1e-6</span>, momentum=<span class="hljs-number">0.9</span>, nesterov=<span class="hljs-literal">True</span>)

    model.<span class="hljs-built_in">compile</span>(loss=<span class="hljs-string">'binary_crossentropy'</span>,
                  optimizer=sgd)

    early_stop = EarlyStopping(monitor=<span class="hljs-string">'val_loss'</span>, patience=<span class="hljs-number">3</span>, min_delta=<span class="hljs-number">0.0001</span>)

    history = model.fit(train_padded,
                        y_train,
                        epochs = <span class="hljs-number">750</span>,
                        batch_size = <span class="hljs-number">64</span>,
                        validation_split = <span class="hljs-number">0.2</span>,
                        callbacks = [early_stop, tfdocs.modeling.EpochDots()])

    <span class="hljs-keyword">return</span> model, history
</code></pre>
<p>So we are training a relatively advanced model in something like ten lines of code. Impressive!</p>
<p>However, loads of things are happening behind the scenes here. Lets look at a few of them in detail.</p>
<h4>1. Model architecture</h4>
<p>Keras allows us to explore our small model by simply calling <code>model.summary()</code>.</p>
<pre><code class="hljs language-bash">Model: <span class="hljs-string">"sequential"</span>
_________________________________________________________________
Layer (<span class="hljs-built_in">type</span>)                 Output Shape              Param <span class="hljs-comment">#</span>
=================================================================
embedding (Embedding)        (None, 20, 64)            3200000
_________________________________________________________________
spatial_dropout1d (SpatialDr (None, 20, 64)            0
_________________________________________________________________
lstm (LSTM)                  (None, 100)               66000
_________________________________________________________________
dense (Dense)                (None, 4)                 404
=================================================================
Total params: 3,266,404
Trainable params: 3,266,404
Non-trainable params: 0
</code></pre>
<p>First, we define a sequential model by calling <code>model = Sequential()</code>. Then we add layers.</p>
<p>The first layer is a <code>Embedding</code> layer, and is interesting in its own right. This layer takes a sentence and learns a representation (basically a function) of that sentence. This has a lot of advantages over other methods, can you can read more about <a href="https://machinelearningmastery.com/use-word-embedding-layers-deep-learning-keras/">here</a>.</p>
<p>The second layer is a <code>SpatialDropout1D</code> layer. Coarsely put, this layer remove features from the data, which has been <a href="https://arxiv.org/abs/1207.0580">shown to</a> help greatly in preventing <a href="https://en.wikipedia.org/wiki/Overfitting">overfitting</a>.</p>
<p>Our third layer is the <code>LSTM</code> layer. I wont go into detail with what's the layer as its quite complex, but look here for <a href="https://towardsdatascience.com/understanding-lstm-and-its-quick-implementation-in-keras-for-sentiment-analysis-af410fd85b47">a more detailed blogpost</a>. The <code>100</code> parameter is the number of units, which dimensions the output space for our next layer.</p>
<p>The fourth and last layer is the output of the model. This is where we get the classification, e.g. <code>[1, 1, 0, 0]</code> for a sentence which has the bug and usability tags.</p>
<p>One important thing to notice is the choice of activation function. This function is what sits between the layers and "interprets" the output between the layers.</p>
<p>When doing multi-label classification like we are doing, its important not to have an activation function that decreases the probability of one class as a function of the probability of the other classes. If had two outputs that were mutually exclusive, like predicting if a sentence was <em>either</em> a bug or a usability issue, it would be fine to use something like <code>softmax</code> that does exactly this.</p>
<h4>2. Parameter optimisation</h4>
<p>Training the model happens with gradient descent. If you dont know what that is, you should do some <a href="https://lmgtfy.com/?q=what+is+gradient+descent">googling</a> as this method is at the core of 95% of methods for optimising parameters in machine learning.</p>
<p>We choose stochastic gradient descent with a learning rate of <code>0.01</code>.</p>
<pre><code class="hljs language-python">sgd = SGD(lr=<span class="hljs-number">0.01</span>, decay=<span class="hljs-number">1e-6</span>, momentum=<span class="hljs-number">0.9</span>, nesterov=<span class="hljs-literal">True</span>)
</code></pre>
<p>By defining the model and our choice of optimiser, we are ready to compile our model.</p>
<pre><code class="hljs language-python">
model.<span class="hljs-built_in">compile</span>(loss=<span class="hljs-string">'binary_crossentropy'</span>,
                  optimizer=sgd)

early_stop = EarlyStopping(monitor=<span class="hljs-string">'val_loss'</span>, patience=<span class="hljs-number">3</span>, min_delta=<span class="hljs-number">0.0001</span>)

history = model.fit(train_padded,
                        y_train,
                        epochs = <span class="hljs-number">750</span>,
                        batch_size = <span class="hljs-number">64</span>,
                        validation_split = <span class="hljs-number">0.2</span>,
                        callbacks = [early_stop, tfdocs.modeling.EpochDots()])

</code></pre>
<p>There are two important things to notice here. <strong>First</strong>, our choice of loss function. <strong>Second</strong> the <code>EarlyStopping</code> call.</p>
<p>The loss function is the function that we are trying to minimise by choosing certain parameters for our neural network - this is the essence of <em>learning</em> in machine learning.</p>
<p>The choice of loss function is pivotal, but also a quite mathematical subject. You can read a great visual explanation of binary cross entropy <a href="https://towardsdatascience.com/understanding-binary-cross-entropy-log-loss-a-visual-explanation-a3ac6025181a">here</a>.</p>
<p>Early stopping is an important trick we use to prevent overfitting. It simply stops model training when the loss has not improved in a number of epochs. This decreases the probability of the model running for too many epochs, which in some cases leads to overfitting the training data.</p>
<p>Looking at test run of the model, we see that early stopping is invoked after epoch 38, because the model loss hasn't improved for the last three rounds.</p>
<h4>3. Training</h4>
<p>Training is an iterative process where our algorithm is constantly choosing new parameters with the constant aim of minimising the loss function. By doing this, the model is learning an effective representation of the training data, which is the goal of all this effort. After 38 epochs of 750 iterations, our model grind to a halt because of our early stop criteria explained above.</p>
<pre><code class="hljs language-bash">Epoch 36/750
19752/19752 [==============================] - 27s 1ms/sample - loss: 0.1240 - val_loss: 0.1286
Epoch 37/750
19752/19752 [==============================] - 28s 1ms/sample - loss: 0.1213 - val_loss: 0.1250
Epoch 38/750
19752/19752 [==============================] - 28s 1ms/sample - loss: 0.1191 - val_loss: 0.1296
</code></pre>
<p>Lets looks at the loss that our loss function outputs over the 38 epochs we are training the model.</p>
<p>![Loss over time](/assets/img/articles/2020-05-25-Using-deep-learning-to-label-feedback-messages/loss.webp</p>
<p>The training and test loss decrease steadily, and slowly stops to improve a lot after the 35th epoch. Because it does not see further improvement, our early stop rule halts the training.</p>
<h4>4. Evaluating our model</h4>
<p>Now we have a trained model we can use to generate predictions with on our test set, to see how well the model does on data it has never seen before (the test set).</p>
<pre><code class="hljs language-python">preds = model.predict(test_padded)

preds[preds>=<span class="hljs-number">0.5</span>] = <span class="hljs-number">1</span>
preds[preds&#x3C;<span class="hljs-number">0.5</span>] = <span class="hljs-number">0</span>

_ = <span class="hljs-built_in">abs</span>(preds - y_test)

<span class="hljs-built_in">sum</span>(_)

array([<span class="hljs-number">364.</span>, <span class="hljs-number">530.</span>, <span class="hljs-number">114.</span>,  <span class="hljs-number">93.</span>])
</code></pre>
<p>These numbers show that out of 6173 messages in the test set, our model labels 363 bug, 530 usability, 114 update and 93 feedback messages wrong. This translates to an accuracy of 95%, 93%, 98% and 99% respectively for the four classes. Not too bad for a relatively simple setup!</p>
<p>Is accuracy the right or the only metric we should look at when training a model? Definitely not - but evaluating a model like this takes some effort, and that's for another day.</p>
<h2>Summary and next steps</h2>
<p>In the above, I sketched the overarching approach we at Nodes take to labelling all the feedback users provide when they interact with our digital solutions. To do this I demonstrated how to use natural language processing to process text so it can be fed to a clustering algorithm that automatically identifies clusters in the text. After resolving some edge cases manually, I showed how to feed this data to a deep learning model that learns the structure of each sentence and is able to classify new messages as either being either a bug, a usability issue, an issue with an update or constructive feedback.</p>
<p>The aim of all of this is to provide a solution that generates actionable insights into what users are saying about the digital solutions we build for our clients. Our next step is to build a dashboard that summarises this information and makes it available for product owners across our digital solutions.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[iOS Conferences in time of Coronavirus]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/05/08/iOS-Conferences-in-time-of-Coronavirus</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/05/08/iOS-Conferences-in-time-of-Coronavirus</guid>
            <pubDate>Fri, 08 May 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>I'm a big fan of conferences. And not necessarily for the sessions, which are usually recorded and available online afterwards. But more for meeting other people who do the same thing as I do. For that huge amount of creative energy you get afterwards. For the chance of meeting your heroes.</p>
<p>The first part of 2020 (and let's hope it's only the first part) is marked by the Coronavirus outbreak, a worldwide pandemic that has a huge impact on - among others - public gatherings. Thus making it impossible to have conferences as we used to know them. <strong>Out of the 31 conferences I'm following for this year, 3 have already happened in-person, 1 has been postponed to September, 16 have been cancelled, 5 have switched to an online format and 6 are unsure</strong> (either expected but not officially announced nor cancelled, or announced, but no ticket sales yet). A detailed situation <a href="https://gist.github.com/mariusc/0e6d481d7a30bb704fab9bef22ad2319">can be found here</a>.</p>
<h2>Attending a conference from your living room</h2>
<p>So what do those conferences do? Either cancel, postpone or try a new format, online. I've "attended" one such online conference in April this year. My experience wasn't probably everyone's experience, and the conference was on a topic that's of interest to me, but not my main interest. The talks were streamed online. There was a Slack workspace, channels for asking questions to the speakers after their talks, channels to get to know each other, channels to discuss specific themes, a general "conference room" channel, where people would add claps 👏 after each live-streamed talk was finished, there were opportunities to get to know other people, it really was incredible how the organizers tried to think of everything and they did their best.</p>
<p>Even so, I enjoyed much more the experience of an in-person conference. The interaction on Slack seemed a bit forced, and, as an introvert, I found it hard and even intimidating to write something on Slack. At an in-person conference, it's easier, even for introverts. There are groups already formed of people talking, you find a smaller one with only 3 people who stand in a semi-open position (not a full closed circle) and join. Can't really do that on slack. Either you write to everyone, which is probably an introvert's nightmare, or to only one person you don't know, which might be even worse. However, the biggest issue I found was actually committing to the conference.</p>
<blockquote>
<p>It's hard to focus 100% on a conference when you're attending from your living room.</p>
</blockquote>
<p>As I've <a href="https://mariusc.github.io/2018-01-22-conferences-are-not-dead.html">written before</a>, almost all of the conferences record the talks and post them online afterwards. Some even offer live streaming. So why do people still pay a lot of money to travel to a different city and attend a conference? For what's become known as the "hallway track": they want to meet, talk to and connect in person with other people who do the same thing as they do. And that's hard to obtain if the conference is all online.</p>
<h2>WWDC 2020</h2>
<p>More details about WWDC have been announced a few days ago, but for something that's in 1.5 months, we still know surprisingly little. And I'm guessing that's because Apple is still working on the format. So far, we know it's going to start on June 22, it will have, as usual, the keynote, Platforms State of the Union, sessions and labs [1]. But it probably won't also have a way of queueing with other developers the night before the keynote. Or <a href="https://twitter.com/parrots">Curtis Herbert</a>'s WWDC running or climbing events. Or the Bash, the WWDC Hike, the parties, or the night of Dim Sum, or <a href="https://daveverwer.com/blog/the-story-behind-picswithdave/">#PicsWithDave</a>, or any other social activity that made WWDC an unforgettable experience for most of the developers who got the chance to attend it. All the events that created that FOMO during WWDC. <a href="https://twitter.com/daveverwer">Dave Verwer</a> has an interesting analysis of <a href="https://iosdevsurvey.com/2019/is-wwdc-already-a-virtual-conference/">what a virtual WWDC could mean</a>.</p>
<p>Is this good or bad? I like conferences. But not everyone does. Most developers don't go to conferences. It could be because of cost reasons, because attending an in-person conference doesn't bring them that much value, because they don't want to travel that much or travel to the US, or for any other reason. I don't go to WWDC because I find it hard to justify to myself spending that much money on a conference. So won't it actually be better, in the grand scheme of things, that WWDC is online? Instead of a randomly selected elite of devs who get to go to WWDC and make all the rest of us jealous? Time will tell. But I, for one, am looking forward to WWDC 2020.</p>
<p>[1] - <a href="https://www.apple.com/newsroom/2020/05/apple-to-host-virtual-worldwide-developers-conference-beginning-june-22.html">https://www.apple.com/newsroom/2020/05/apple-to-host-virtual-worldwide-developers-conference-beginning-june-22.html</a></p>
<hr>
<p>Article photo by <a href="https://unsplash.com/@headwayio">Headway</a> on <a href="https://unsplash.com/s/photos/conference">Unsplash</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Differences Between Mini Program Development and Web Development]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/05/06/Differences-Between-Mini-Program-Development-and-Web-Development</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/05/06/Differences-Between-Mini-Program-Development-and-Web-Development</guid>
            <pubDate>Wed, 06 May 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Web development is built using HTML, CSS and JavaScript, where HTML describes the structure of the page, CSS determines the appearance of the page, and JS defines the interaction between the page and the user.</p>
<p>Mini program development is built using WXML, WXSS and JavaScript, where WXML is the equivalent of HTML, but more simple than HTML, mainly reflected in simplifying and standardizing the sticky notes.</p>
<p>WXSS has most of the features of CSS, but incorporates some new features and modifications for the Mini Program, JS Logic Interaction only uses the core of JavaScript.</p>
<p>From the above, Web development and Mini program development depend on JavaScript, but there are some differences, as follows:</p>
<p>DOM: Document Object Model</p>
<p>BOM: Browser Object Model</p>
<p>Because there are no DOM Objects and BOM Objects in the Mini Program, it is impossible for some libraries familiar to front-end developers, such as jQuery and Zepto, to run in Mini Programs.</p>
<p>Web development, rendering threads and scripting threads are mutually exclusive, so long-time script running may make a page unresponsive. In a Mini Program, rendering threads and scripting threads run separately.</p>
<p>The former runs in JSCore without a full browser object, thus lacking the relevant DOM and BOM APIs.</p>
<p>Meanwhile, the JSCore environment is different from the NodeJS environment, which means that some NPM packages cannot run in Mini Programs.</p>
<p>The differences between the three operating environments are as follows:</p>
<p>Web developers work with IE/Chrome/QQ browsers on PC, and Safari/Chrome browsers as well as various WebViews on iOS or Android systems on mobile devices.</p>
<p>Mini Program developers work with WeChat on iOS, Android, and WeChat DevTools.</p>
<p>The three operating environments of the Mini Program as follows:</p>

























<table><thead><tr><th>Runtime Environment</th><th align="center">Logic Layer</th><th align="right">Rendering Layer</th></tr></thead><tbody><tr><td>iOS</td><td align="center">JavaScriptCore</td><td align="right">WKWebView</td></tr><tr><td>Android</td><td align="center">V8</td><td align="right">Chromium custom kernel</td></tr><tr><td>WeChat DevTools</td><td align="center">NWJS</td><td align="right">Chrome WebView</td></tr></tbody></table>
<p>When developing web pages, Web developers only need to use browsers with some auxiliary tools or editors.</p>
<p>The development of Mini Programs involves applying for Mini Program accounts, installing WeChat DevTools, configuring projects, and so on.</p>
<p><em>Article Photo by <a href="https://image.baidu.com">baidu</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Mini Programs Introduction]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/05/06/Mini-Programs-Introduction</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/05/06/Mini-Programs-Introduction</guid>
            <pubDate>Wed, 06 May 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>WeChat Mini Programs are a new way of connecting users and services. It‘s a new development capability that developers can learn quickly.</p>
<p>They are easy to access and share on WeChat, delivering excellent user experience.</p>
<h2>Technical Development of a Mini Program</h2>
<p>Rome was not built in a day, and Mini Programs were not invented in a day.</p>
<p>First contact Mini Program is due to in the circle of friends saw a jump to jump link, so has a doubt on the Mini Program, what is the Mini Program?</p>
<p>Mini Programs started with WeChat, but why does the WeChat platform develop Mini Programs?</p>
<p>So that developers can get a better experience with WeChat.</p>
<p>How Mini Programs make a better experience for developers:</p>
<ul>
<li>Fast loading</li>
<li>More powerful capabilities</li>
<li>Native experience</li>
<li>Easy and secure WeChat data exposure</li>
<li>Efficient and simple development</li>
</ul>
<p>As WebView in WeChat became increasingly important as an entry to mobile web pages, that is why WeChat developed the JS APIs.</p>
<p>For example, demo 1：Preview images using <code>WeixinJSBridge</code>.</p>
<pre><code class="hljs language-javascript"><span class="hljs-title class_">WeiXinJSBridge</span>.<span class="hljs-title function_">invoke</span>(
  <span class="hljs-string">"imagePreview"</span>,
  {
    <span class="hljs-attr">current</span>: <span class="hljs-string">"http://inews.gtimg.com/newsapp_bt/0/1693121381/641"</span>,
    <span class="hljs-attr">urls</span>: [
      <span class="hljs-string">"https://img1.gtimg.com/10/1048/104857/10485726_980x1200_0.jpg"</span>,
      <span class="hljs-string">"https://img1.gtimg.com/10/1048/104857/10485729_980x1200_0.jpg"</span>,
      <span class="hljs-string">"https://img1.gtimg.com/10/1048/104857/10485731_980x1200_0.jpg"</span>,
    ],
  },
  <span class="hljs-keyword">function</span> (<span class="hljs-params">res</span>) {
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(res.<span class="hljs-property">err_msg</span>);
  }
);
</code></pre>
<p>Demo 1 implements a JS API that users WeChat native components to view images. This method is much simpler and more efficient than additionally introducing a JS image preview component library.</p>
<p>But WeChat never officially exposed such APIs, they were only provided internally for some Tencent services.</p>
<p>So later WeChat released a Web development kit called JS-SDK, which included dozens of APIs for such scenarios as taking photos, recording, speech recognition, QR code, map, payment, sharing, and coupons etc.</p>
<p>Those WeChat's native capabilities allowed web developers to do what they couldn't do before.</p>
<p>Demo 2 also shows how to view images by calling a native component.</p>
<pre><code class="hljs language-javascript">wx.<span class="hljs-title function_">previewImage</span>({
  <span class="hljs-attr">current</span>: <span class="hljs-string">"https://img1.gtimg.com/10/1048/104857/10485726_980x1200_0.jpg"</span>,
  <span class="hljs-attr">urls</span>: [
    <span class="hljs-string">"https://img1.gtimg.com/10/1048/104857/10485726_980x1200_0.jpg"</span>,
    <span class="hljs-string">"https://img1.gtimg.com/10/1048/104857/10485729_980x1200_0.jpg"</span>,
    <span class="hljs-string">"https://img1.gtimg.com/10/1048/104857/10485731_980x1200_0.jpg"</span>,
  ],
  <span class="hljs-attr">success</span>: <span class="hljs-keyword">function</span> (<span class="hljs-params">res</span>) {
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(res);
  },
});
</code></pre>
<p>Actually, built on WeixinJSBridge and incorporating some new features, JS-SDK was open to all developers, instead of being only available to internal developers, and became popular among developers quickly.</p>
<p>JS-SDK gave web developers more capabilities by exposing WeChat APIs. However, users still had an unsatisfactory experience when using mobile web pages.</p>
<p>When a user visited a web page, a white screen occurred before the page was displayed on the browser. On a mobile device, this problem was more severe due to lower device performance and network speed.</p>
<p>To help web developers solve this problem, our team designed an enhanced version of JS-SDK, which had an important feature called "WeChat Web Resource Offline Storage".</p>
<p>This design was somewhat similar to HTML5 Application Cache, but eliminated some of HTML5's limitations.</p>
<p>In internal trials, WeChat Web Resource Offline Storage could solve some problems, but white screen still occurred in some complicated cases, for example, when a page was loaded with a large number of CSS or JavaScript files.</p>
<p>In addition to the white screen, insufficiency of clear action feedback affected the web user experience.</p>
<p>This was mainly reflected in tap delay, and page switching without smooth transitions.</p>
<p>Because of the above problems of JS-SDK, in order to provide a better experience for developers, the Mini Program development documentation is provided in the WeChat platform.</p>
<p><em>Article Photo by <a href="https://image.baidu.com">baidu</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introduction to Domain-Driven Design]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/05/04/Intro-to-Domain-Driven-Design</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/05/04/Intro-to-Domain-Driven-Design</guid>
            <pubDate>Mon, 04 May 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Domain-Driven Design (DDD) has been around for well over a decade, and its popularity keeps on growing. Eric Evans coined the term in his book: <a href="http://dddcommunity.org/book/evans_2003/">"Domain-Driven Design: Tackling Complexity in the Heart of Software."</a> A lot of recent interest probably correlates with the rise of microservices, since it provides many practices and patterns for building them effectively. But as useful as it can be to use them together, nothing is compelling you to do so. You can reap the benefits of DDD with a wide variety of system and software architectures.</p>
<p>To paraphrase <a href="https://dddcommunity.org/learning-ddd/what_is_ddd/">DDD Comunity</a>, DDD is an approach to software development, aiming to produce an implementation deeply reflecting the core business concepts. The main parts of the code should resemble the language used by domain experts, and be easily explainable to non-technical staff. This level of clarity makes rigorous documentation unnecessary. As the business evolves, this code should too. While it's not particularly suitable for simple applications, DDD shines in complex domains.</p>
<p>DDD is not a methodology nor a strict set of rules. The whole team should practice it - the technical staff will fail if other project members don't get involved. Some parts of it are intentionally open to interpretation, while others are more thoroughly defined. That makes DDD applicable in many domains and allows its further evolution.</p>
<p>The target audience for this article are people usually involved in software projects, such as domain experts, project managers, product owners, clients, UX/UI designers, QA specialists, developers, architects, etc. You will be given some ideas on how to share knowledge and reason about the business domain. You will see how to partition projects for an implementation that's understandable, testable, maintainable, and adjustable to new requirements. I will provide some examples along the way to make the introduced concepts easier to digest. In the end, we will take a quick look at some of DDD's technical aspects, so developers and architects can start experimenting for themselves.</p>
<h2>The basics</h2>
<p>We will begin with DDD's less technical, strategic side. It is instrumental in shaping your project, affecting how the implementation is organized and even how the code is written. It also affects team communication, making sure it's effective with a minimal chance of misinterpretation. That brings us to the first of the two most significant concepts in DDD - ubiquitous language.</p>
<h2>Ubiquitous language</h2>
<p>Domain experts often have their jargon or a distinct vocabulary. When they work together with the technical team to establish a common, rigorous language, one that is unambiguous and effortlessly used, that becomes the ubiquitous language. It should be clear, thoroughly defined, well understood, and nurtured by involved parties. There is also no point at which a ubiquitous language becomes final. It continually evolves as the understanding of the domain changes.</p>
<p>The technical team should pay close attention to how domain experts communicate about the business. Developers should make sure to accurately translate that to code and avoid introducing any new terminology that would be hard to accustom to by others.</p>
<p>While documenting ubiquitous language might be useful in some cases, you should generally avoid doing that. The team's already familiar with it anyway, and documentation only requires additional work and continuous maintenance.</p>
<p>Also, be careful. The term "ubiquitous" might mislead you into thinking the language extends over the whole domain. I have initially thought so myself. I will explain why this is not the case as we look into the second essential DDD concept - bounded contexts.</p>
<h2>Bounded contexts</h2>
<p>If you take any company and observe communication within different departments, it will quickly become clear each one uses a different language. They are covering distinct aspects of the business, after all. You might also notice some shared terminology, yet the meaning of each shared term may vary between departments. The same word might have completely different meanings - an "account" can be both a system user account for IT and a bank account for finance.</p>
<p>Let's imagine a chain of domestic appliance stores. Consider the meaning of a particular washing machine model (note: this example will be used throughout the article).</p>
<ul>
<li>Marketing needs consumer-oriented information about the machine for the online catalog, such as the manufacturer name, model number, price, water consumption, energy efficiency, capacity, load type, a photo, etc.</li>
<li>Service keeps track of warranty information, manufacturer contacts, list of replacement parts, service manual, etc.</li>
<li>At checkout, salespeople need the manufacturer name, the model number, and the price of the washing machine multiplied by the number of units sold.</li>
<li>Logistics tracks the number of washing machines stored in warehouses, their locations, orders, supplier information, etc.</li>
<li>HR doesn't need any data about the washing machines or any other product sold.</li>
</ul>
<p>Marketing obviously has no use for a list of replacement parts. Salespeople similarly don't need the washing machine supplier information. Service doesn't care about the number of new washing machines in warehouses. You get the point. Other departments equally don't need most data managed by HR, such as employee retirement plans, etc.</p>
<p>The main thing to note here is that we're referring to different notions of the washing machine across several contexts. Every context gives it a distinct meaning and attributes.</p>
<p>We will usually find multiple such contexts in a particular business. If we constrain a ubiquitous language inside one and form a domain model based on it, this becomes a <em>bounded context</em>. There will be multiple bounded contexts in a business domain, each having its distinct ubiquitous language and domain model. They impose lingual and logical, but not technical constraints.</p>
<p>Bounded contexts are associated with another important concept: <strong>subdomains</strong>. They represent different segments of the business domain. Business aspects of competitive advantage belong within the <em>core domain</em>. <em>Supporting subdomains</em> are related to non-core business segments. <em>Generic subdomains</em> are needed by the other two to function but don't provide any competitive advantage, such as bookkeeping, for example.</p>
<p>But what is the difference between subdomains and bounded contexts? Many people have a hard time drawing a clear line between the two. Subdomains describe different responsibilities of the business domain, while bounded contexts model the current state of the system. In his book, <a href="https://www.informit.com/store/implementing-domain-driven-design-9780321834577">"Implementing Domain-Driven Design,"</a> Vaughn Vernon places subdomains in the problem space and bounded contexts in the solution space. In an ideal scenario, they would align. In the real world, however, there is often something like a legacy system whose bounded context spans across two or more subdomains.</p>
<p>Note that, although I've used business departments as examples of general context, they're not always going to match your subdomains and bounded contexts. As an example, a bounded context for user access control doesn't reflect any department, yet most software products depend on it. Its ubiquitous language with terms for logging in, signing up, resetting the password, etc. will be used across the business.</p>
<h2>The planning</h2>
<p>Now that we've grasped the main ideas, let's see how to put them to practice.</p>
<h2>Context maps</h2>
<p>Many of you are probably working on an existing project where you might want to apply DDD. Context maps help either layout your existing domain and bounded contexts or plan a completely new software product. Let's try to make one for our imaginary appliance store domain.</p>
<p>![Context map](/assets/img/articles/2020-05-04-Intro-to-Domain-Driven-Design/context-map.webp</p>
<p>The very first thing you'll notice is my horrible handwriting, for which I'm very sorry. However, there is a good reason for not using modeling software and making beautiful diagrams: this is not the final iteration of the context map, and you'll throw (erase) these away often as the team's understanding of the domain improves. Papers and whiteboards are your friends. The same rule about formally documenting ubiquitous languages also applies here - create such documentation only if you genuinely need it, and then make sure you maintain it. The code should still be the primary source of truth.</p>
<p>In the context map given above, we see a giant bubble representing the domain. The smaller bubbles in full lines represent bounded contexts. The segments separated with dashed lines represent subdomains. In this theoretical scenario, most of the bounded contexts align with their subdomains.</p>
<p>However, ignoring <em>Store</em>, there is also one outlier - <em>Sales &#x26; Marketing</em>. The departments are different enough to be found in two domains, but the current implementation still places them in the same bounded context. Splitting it up would probably be recommended based on the outline of the subdomains. But maybe they're lingually so unified that we should merge the domains and leave the bounded context as-is? That is something the project team needs to settle upon and represent in the future iterations of the context map.</p>
<p>Although we should make sure to keep bounded contexts properly decoupled, they won't make much sense if they don't communicate. We use connecting lines to indicate their relations. We could probably draw some more in our example, but, as we already mentioned - this is not the final context map of our domain. You can see that most of the bounded contexts link to <em>Access Control</em>. As we want to simplify user management by having a central place for that, it is reasonable to have such relations.</p>
<p>There are multiple types of relational patterns. Although knowing them is essential, they're also out of the scope of this post. Similarly, there is more to be said about context maps. If you'd like more information, please refer to the references at the end.</p>
<h2>One bounded context per team?</h2>
<p>You might read or hear that there should be a team allocated to each bounded context. That is a very appealing scenario, but very often, it's simply not realistic. So should a team working in multiple bounded contexts develop a shared language for all of them?</p>
<p><strong>NO!</strong> Instead, the team should learn multiple ubiquitous languages. They should exclusively use the language of the bounded context in which they're currently working. <a href="https://softwareengineering.stackexchange.com/a/308973">This SO answer</a> has a great way of putting it - the team should become <em>multi-lingual</em>.</p>
<h2>A constant state of flux</h2>
<p>Once more, I'd like to point out that all of the mentioned concepts don't assume a final state. Your business domain will continuously change, and your software needs to adapt. Your bounded contexts and ubiquitous languages will evolve with the business, new learnings, and people. You will correct and redraw context maps to accommodate these changes, and you'll accordingly adjust your implementation.</p>
<h2>The implementation</h2>
<p>So far, we've covered a lot of ground without any technical detail. That's for a good reason, as having a solid grasp of the <em>strategic</em> basics is crucial to success with DDD.</p>
<p>On the other hand, there is also a lot on the <em>tactical</em> side of DDD. You'll have a substantial amount of flexibility in building your solution, but there are still multiple commonly used patterns, practices, and architectures you should know.</p>
<h2>Coding in the ubiquitous language</h2>
<p>Now it's time to see how ubiquitous languages and code come together. Remember, we said it's critical to listen to domain experts. Let's hear a seller in our appliance store speak about their process:</p>
<blockquote>
<p>I need to check out every product either by scanning the barcode on the packaging or entering the product code manually. After I complete the sale, I need to print out the receipt for the buyer.</p>
</blockquote>
<p>Now let's see some code implementing this scenario. For simplicity, we won't cover the "unhappy path" like error cases or customers canceling the purchase. Some elements, like barcode scanning, will be asynchronous or found in places different from the rest. We won't concern ourselves with this, but rather focus on the important - the lingual and logical aspects.</p>
<pre><code class="hljs language-java"><span class="hljs-comment">// Checkout using barcode obtained from a scanner device or camera</span>
seller.checkOutUsingBarcode(barcode, receipt);
</code></pre>
<pre><code class="hljs language-java"><span class="hljs-comment">// Alternative checkout method in case barcode scanning fails</span>
seller.checkOutUsingProductCode(productCode, receipt);
</code></pre>
<pre><code class="hljs language-java">seller.completeSale(receipt);
seller.printOutReceipt(receipt);
</code></pre>
<p>These examples are trivial, but they should be enough for a first impression. Compare them against the seller's description of the process and notice the similarities.</p>
<p>There are some other questions to consider here:</p>
<ol>
<li>Is the seller a <code>User</code> type or a <code>Seller</code> type?</li>
<li>What is the correct bounded context?</li>
<li>How are the barcodes and the product codes validated?</li>
</ol>
<p>One sensible answer to the first two questions would be <code>Seller</code> in the <em>Sales &#x26; Marketing</em> context. You might find this confusing since we also need to have access control for sellers. After all, they need to have the ability to sign in and reset passwords like everybody else. Let's reconsider - do we pick <code>User</code> or <code>Seller</code>? Well, both. Your <code>User</code> from <em>Access Control</em> will map to the <code>Seller</code> in <em>Sales &#x26; Marketing</em>. They'll have some common properties that identify the represented employee, but they will also differ in others. Note that, implementation-wise, one is not the other, i.e., there is no inheritance relationship between the types.</p>
<p>Let's quickly look at a counter-example. Imagine a system where the <code>User</code> type holds all employee information. There is no <code>Seller</code> or any other employee type. Each salesperson, marketer, warehouse worker, bookkeeper, etc. would be a <code>User</code>, and the class would end up being massive to support all the required properties. It becomes even worse when you start persisting it in databases or adding methods like the above <code>checkoutUsingProductCode</code>. Such code is hard to test, change, and maintain. Yet, this is very often seen in software systems when developers start taking <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY</a> to the extreme.</p>
<p>Regarding the last question about validating bar- and product codes, the most important thing is to make sure your method's callers don't have to do any validation themselves. Instead of passing an integer or a string, it would be better to introduce new types (e.g., <code>Barcode</code> and <code>ProductCode</code>), even if they only have a single field and a few simple methods. The big benefit is that methods accepting such types as parameters can use them straight away, as their validity is always assured. That also prevents validation implementation detail from leaking into these methods.</p>
<pre><code class="hljs language-java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Barcode</span> {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String barcode;

    <span class="hljs-keyword">public</span> <span class="hljs-title function_">Barcode</span><span class="hljs-params">(String barcode)</span> {
        <span class="hljs-keyword">if</span> (isInvalidBarcode(barcode)) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">IllegalArgumentException</span>(<span class="hljs-string">"The provided barcode is not valid"</span>);
        }

        <span class="hljs-built_in">this</span>.barcode = barcode;
    }

    ...

    <span class="hljs-keyword">private</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">isInvalidBarcode</span><span class="hljs-params">(String barcode)</span> {
        <span class="hljs-comment">// Check the validity of the barcode according to standards used in the store</span>
        ...
    }
}
</code></pre>
<p>Our <code>Seller</code> class would then look something like this:</p>
<pre><code class="hljs language-java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Seller</span> {
    ...

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">checkOutUsingBarcode</span><span class="hljs-params">(Barcode barcode, Receipt receipt)</span> { ... }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">checkOutUsingProductCode</span><span class="hljs-params">(ProductCode productCode, Receipt receipt)</span> { ... }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">completeSale</span><span class="hljs-params">(Receipt receipt)</span> { ... }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">printOutReceipt</span><span class="hljs-params">(Receipt receipt)</span> { ... }
}
</code></pre>
<h2>Decoupling bounded contexts</h2>
<p>With <code>User</code> and <code>Seller</code>, we explained how objects "cross" context boundaries by mapping to another type.</p>
<p>You can keep the boundaries conceptual and not impose them anywhere in the project configuration. You might split contexts by package (or namespace) and let developers know they shouldn't use a class belonging to a particular package outside of it. It might work for your team or in smaller projects, but in general, this often leads to cutting corners and a <a href="https://en.wikipedia.org/wiki/Code_smell">smellier</a> codebase once the deadlines start nearing.</p>
<p>You'll probably be better off with imposing some stricter boundaries. For this, you can use modules or similar features in your build system or programming language, making only select classes from one module available to the other. When the project scales up, you can split these modules up into (micro)services.</p>
<p>DDD doesn't impose any particular architecture as long as your domain logic is in focus, although the <a href="https://en.wikipedia.org/wiki/Hexagonal_architecture_(software)">hexagonal architecture</a> is quite popular among practitioners. <a href="https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html">Clean architecture</a> is another strong candidate, but you can also adapt architectures such as <a href="https://en.wikipedia.org/wiki/Service-oriented_architecture">SOA</a> and others to fit the purpose.</p>
<h2>Final word</h2>
<p>You won't be surprised to hear there's much more ground to cover. I haven't even mentioned aggregates, event storming, entity and value objects, event sourcing, CQRS, and many other topics. My goal was to give you the gist and pique your interest.</p>
<p>If you decide DDD is the right choice for your project, Vaughn Vernon's excellent book goes into a lot of detail on both strategic and tactical patterns (link in references). Before you start wondering why I also haven't mentioned Eric Evans' famous "blue book" here, the only reason is I still haven't gotten to read it at the time of writing. Rest assured, it's already on the list. :)</p>
<p>I hope the article motivated you to learn more and that it will stimulate DDD in more projects, both existing and new.</p>
<h2>References</h2>
<ul>
<li><a href="https://dddcommunity.org/learning-ddd/what_is_ddd/">DDD Comunity - What is Domain-Driven Design?</a></li>
<li><a href="https://www.microsoftpressstore.com/articles/article.aspx?p=2248811">Dino Esposito &#x26; Andrea Saltarello - Discovering the Domain Architecture</a></li>
<li><a href="https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215/ref=sr_1_1?ie=UTF8&#x26;s=books&#x26;qid=1238687848&#x26;sr=8-1">Eric Evans - "Domain-Driven Design: Tackling Complexity in the Heart of Software" (book)</a></li>
<li><a href="https://domainlanguage.com/wp-content/uploads/2016/05/DDD_Reference_2015-03.pdf">Eric Evans - Domain-Driven Design Reference</a></li>
<li><a href="https://blog.jonathanoliver.com/ddd-strategic-design-core-supporting-and-generic-subdomains/">Jonathan Oliver - DDD: Strategic Design: Core, Supporting, and Generic Subdomains</a></li>
<li><a href="https://www.martinfowler.com/bliki/UbiquitousLanguage.html">Martin Fowler - Ubiquitous Language</a></li>
<li><a href="http://www.methodsandtools.com/archive/archive.php?id=97">Methods &#x26; Tools - An Introduction to Domain-Driven Design</a></li>
<li><a href="https://codeburst.io/ddd-strategic-patterns-how-to-define-bounded-contexts-2dc70927976e">Vadim Samokhin - How to define bounded contexts</a></li>
<li><a href="https://www.informit.com/store/implementing-domain-driven-design-9780321834577">Vaughn Vernon - "Implementing Domain-Driven Design" (book)</a></li>
</ul>
<hr>
<p>Title photo by <a href="https://unsplash.com/@ari_spada?utm_source=unsplash&#x26;utm_medium=referral&#x26;utm_content=creditCopyText">Ari Spada</a> on <a href="https://unsplash.com/photos/wVoMvN5NsU0?utm_source=unsplash&#x26;utm_medium=referral&#x26;utm_content=creditCopyText">Unsplash</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to convert 3D models to USDZ files using Apple's Reality Converter]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/04/26/how-to-convert-3d-models-to-usdz-files-using-apples-reality-converter</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/04/26/how-to-convert-3d-models-to-usdz-files-using-apples-reality-converter</guid>
            <pubDate>Sun, 26 Apr 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Earlier this year, Apple released the tool any ARKit enthusiast has been waiting for: <code>Reality Converter</code>. <strong>Keep in mind this is a Beta Release as of the moment of this article</strong>.</p>
<p>You can convert common 3D files like <strong>.gltf, .obj, .fbx</strong> using the Reality Converter, but you should expect to have occasional conversion fidelity issues due to different features available across formats. So far I had a pleasant experience using this tool and today I will show you how easy it is now to convert your existing 3D models to the USDZ file format.</p>
<p>If you want to learn more about the USDZ file format, you can check out <a href="/en2019-10-07-Using-USDZ-for-a-better-AR-experience">this article</a> where you can also see the previous way to convert files to USDZ using the <a href="https://developer.apple.com/download/more/?=USDPython">usdz command line tool</a>. The usdz command line tool still has some extra functionality and sample scripts so it is worth checking it out.</p>
<p><a href="https://developer.apple.com/services-account/download?path=/Applications/Reality_Converter/Reality_Converter_Beta.dmg">Download Reality Converter beta from here</a></p>
<p><em>You will need to have <strong>macOS 10.15.2</strong> or later to be able to use Reality Converter.</em></p>
<hr>
<h2>Finding 3D models</h2>
<p>Before we dive into the tutorial, if you don't have a 3D model yet, I can recommend 2 websites you can use to find a test model:</p>
<ul>
<li><a href="https://sketchfab.com/feed">Sketchfab</a></li>
<li><a href="https://poly.google.com/">Poly Google</a></li>
</ul>
<p><em>Both of those websites also offer the option to download models as USDZ files directly.</em></p>
<hr>
<h2>Converting .gltf files</h2>
<p>Converting a .gltf model is as easy as drag and dropping it into Reality Converter. Just remember to drag and drop the whole model folder that contains the materials as well.</p>
<p><div class="embed-container">
      <iframe
          src="https://player.vimeo.com/video/406283680"
          width="700"
          height="480"
          frameborder="0"
          webkitallowfullscreen
          mozallowfullscreen
          allowfullscreen>
      </iframe>
    </div></p>
<p>It is worth mentioning some key differences between USD and .gltf that Apple wrote in the release notes:</p>
<blockquote>
<p>Upon conversion, vertex colors in .gltf files will be stored inside the mesh prim (under the displayColor
property). However, these values will not be used for any surface calculations. It is recommended that you
use your content creation app to “bake” vertex colors to textures instead.</p>
</blockquote>
<blockquote>
<p>RealityConverter and AR Quicklook currently do not support the opacityThreshold property, so remove all
gradients from the desired opacity masks and only use black and white for masked/unmasked areas.</p>
</blockquote>
<blockquote>
<p>If your .gltf model uses double-sided geometry, then duplicate your geometry in your content creation
app and flip it before converting it in Reality Converter.</p>
</blockquote>
<h2>Converting .fbx files</h2>
<p>To convert .fbx files, you will need to install the .fbx python SDK from [here](<a href="https://www.autodesk.com/">https://www.autodesk.com/</a>
developer-network/platform-technologies/fbx-sdk-2020-0>). After installation, restart Reality Converter and you will be able to convert .fbx to USDZ.</p>
<hr>
<p>Now that we successfully converted our models, let's have a look at the tool's options:</p>
<h2>Environments</h2>
<p>In the <code>Environment</code> tab you can preview your USDZ objects under different environmental lighting.</p>
<p><img src="/assets/img/articles/2020-04-26-how-to-convert-3d-models-to-usdz-files-using-apples-reality-converter/rc-environments.webp" alt=""></p>
<h2>Materials</h2>
<p>In Reality Converter you can modify the materials yourself. In the <code>Materials</code> tab, you can navigate between the available materials of the model and modify them if you wish. You even have a <strong>Reset</strong> button to get you back to the original version of the object if needed. Here is an example of how I turn one of my trees to a pink color:</p>
<p><img src="/assets/img/articles/2020-04-26-how-to-convert-3d-models-to-usdz-files-using-apples-reality-converter/rc-materials.webp" alt=""></p>
<h2>Properties</h2>
<p>My favourite feature of the Reality Converter is the ability to change the base units of the model. I have personally encountered multiple times when the size of the model didn't have the right base unit (for example a plant pot with a size of 100 meters instead of 100 centimetres).</p>
<p><img src="/assets/img/articles/2020-04-26-how-to-convert-3d-models-to-usdz-files-using-apples-reality-converter/rc-base-unit.webp" alt=""></p>
<p>Under <code>Properties</code> you can also add copyright metadata.</p>
<h2>Share</h2>
<p>You also have quick access to share your model and I have seen this extremely helpful for testing the object before exporting it.
You can email the model to yourself and open it from the Mail app or share it to your Notes app and open it from your Notes iPhone app.</p>
<p><img src="/assets/img/articles/2020-04-26-how-to-convert-3d-models-to-usdz-files-using-apples-reality-converter/preview.webp" alt=""></p>
<h2>Exporting the model to USDZ</h2>
<p>Last step after you converted and made all modifications needed to your model is to export it by selecting <code>Export</code> from the <code>File</code> menu.</p>
<h2>Shortcuts</h2>
<p>There are a few shortcuts to keep in mind:</p>
<ul>
<li><code>F</code> - Frame the object</li>
<li><code>Command + G</code> - Show/hide the grid</li>
<li><code>Space</code> - Play/pause the animation (if you have one on your model)</li>
<li><code>Command + E</code> - Export the model to USDZ</li>
</ul>
<h2>Resources</h2>
<ul>
<li><a href="/en2019-12-31-How-to-make-an-Augmented-Reality-decorating-experience-app-with-AR-Quick-Look">How to make an Augmented Reality decorating experience app with AR Quick Look</a></li>
<li><a href="https://graphics.pixar.com/usd/docs/Usdz-File-Format-Specification.html">USDZ file format specification</a></li>
<li><a href="https://download.developer.apple.com/Applications/Reality_Converter/Reality_Converter_Beta_Readme.pdf">Reality Converter Beta Release Notes</a></li>
<li><a href="https://skfb.ly/6RJvx">Forest model used in this tutorial</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Things iOS Developers should know before starting Flutter]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/04/21/Things-iOS-developer-should-know-before-starting-flutter</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/04/21/Things-iOS-developer-should-know-before-starting-flutter</guid>
            <pubDate>Tue, 21 Apr 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Flutter is a cross-platform development framework that allows us to create iOS and Android apps using a single programming language called Dart, which was released by Google itself.</p>
<p>I would like to share a few things I wish I would have known before starting Flutter as an iOS developer.</p>
<h2>Dart programming language</h2>
<p>Like Android uses Java/Kotlin and iOS uses Objective C/Swift programming languages to build Android and iOS apps, Flutter uses Dart programing language. You can Get Started from <a href="https://dart.dev/guides/language/language-tour">here</a> with learning Dart.</p>
<p>Dart supports all the features for the Object-oriented programing paradigm(OOP). It is a C-like language with brackets and semicolons. It is uneasy to grasp for swift developers, but it is not hard to learn and use.</p>
<h2>DartPad and CodePen</h2>
<p>In iOS we have <strong>Playground</strong>, to test our Swift code. Similarly, we have <a href="https://dartpad.dev/">DartPad</a> and <a href="https://codepen.io/about">CodePen</a> for the Dart programming language.</p>
<ul>
<li>
<p><strong>DartPad:</strong> It is an online editor that runs Dart programs directly in our browser and also renders a real preview of a screen/widget. It's an easy way to test some of the <strong>business logic</strong> of Flutter apps. We can experiment with our Dart code in this editor.</p>
</li>
<li>
<p><strong>CodePen:</strong> CodePen is an online editor for HTML, CSS and JavaScript. Recently it was announced that CodePen supports Flutter. One of the advantage of CodePen over DartPad is that DartPad uses GitHub <a href="https://gist.github.com/">gists</a> to publicly share our code while in CodePen we can share our code directly by sharing Url.</p>
</li>
</ul>
<h2>IDEs</h2>
<p>For Android developers, it's easy as Flutter supports Android Studio. For iOS devs - maybe not harder, but a little different than Xcode. Flutter is available on different IDEs. The two main code editors are:</p>
<ul>
<li>
<p><strong>Android Studio(IntelliJ):</strong> It's a complete software with everything already integrated. You have to download Flutter and Dart plugins and set up Flutter SDK to start.</p>
</li>
<li>
<p><strong>VSCode:</strong> It is used for developing Flutter and Web applications. It is lightweight and fast. You should first install Dart and Flutter SDK extensions from VS Code market and then set up your SDK</p>
</li>
</ul>
<p>For more detail click <a href="https://flutter.dev/docs/get-started/editor?tab=androidstudio">here</a></p>
<h2>Widgets</h2>
<p>Flutter uses the concept of the <strong>Widgets</strong> which can be used to build complex user interfaces. It is easy to create our own widgets or customize existing widgets. You can browse a catalog of Flutter’s widgets <a href="https://flutter.dev/docs/development/ui/widgets">here</a> and view, for example, <a href="https://flutter.dev/docs/development/ui/widgets/material">Material Design widgets</a> for Android and <a href="https://flutter.dev/docs/development/ui/widgets/cupertino">Cupertino widgets</a> for iOS.</p>
<p>Flutter uses <a href="https://flutter.dev/docs/get-started/flutter-for/declarative">declarative UI</a>.
SwiftUI looks a lot like Flutter but SwiftUI requires iOS 13 and works only with Apple Products. Whereas Flutter is not dependent on OS version and works cross-platform i.e Flutter works with the earlier version of iOS.</p>
<p>Every component in Flutter is known as a <strong>Widget</strong>. The Flutter apps are created by combining these widgets (like building blocks) to form a widget tree. Even the final app we get is also a widget.</p>
<p>For Example - Text Widget</p>
<pre><code class="hljs language-dart">Text(
     <span class="hljs-string">'Hello World'</span>,
      style: TextStyle(
        fontSize: <span class="hljs-number">18.0</span>,
        color: Colors.grey[<span class="hljs-number">600</span>],
      ),
    )
</code></pre>
<p><strong>Note:</strong> Every program starts with <strong>main</strong> function which is the entry point of our application.</p>
<h2>Stateless and Stateful widget</h2>
<p>The Flutter UI is basically a tree of stateless or stateful widgets.</p>
<ul>
<li>
<p><code>Stateless Widgets:</code> These widgets are immutable, so they cannot change their state during runtime. i.e won't change when a user interacts with them.</p>
<p>For example - Text, Icon, etc are the stateless widgets that will remain static.</p>
</li>
<li>
<p><code>Stateful widget:</code> These widgets are mutable, they change over time. These widgets can change their state multiple times and can be redrawn on to the screen any number of times while the app is in action.</p>
<p>For example - Checkbox, Radio, etc are the stateful widgets that change as a user interacts with them.</p>
</li>
</ul>
<h2>JIT (Just-In-Time)</h2>
<p>Dart uses JIT compilation. Flutter allows for fast app reloading by providing a <strong>Hot Reload</strong> option. You don't have to <strong>restart</strong> the app for every change. Just press the <code>Hot Reload</code> button on the toolbar or press <code>cmd-s</code>(Save All) to the running <strong>Dart VM</strong> to send incremental changes of source code, which will reload the changes on device or simulator in few seconds.</p>
<h2>Dependency management</h2>
<p>The dependency management for iOS depends on CocoaPods or Carthage. Flutter uses its own dependency management system called Pub. The <code>pubspec.yaml</code> file is inbuilt with the Flutter apps so that it's easy to add new dependencies as needed for the development.</p>
<p>For example - I want to use the <a href="https://pub.dev/packages/equatable#-readme-tab-">Equatable</a> Flutter package.</p>
<p><strong>Note:</strong> For every Flutter package please check out the <strong>installing</strong> option. For an equatable package, I'll copy the text from <a href="https://pub.dev/packages/equatable#-installing-tab-">here</a> and paste it in pubspec.yaml.</p>
<p>My pubspec.yaml file will look like this</p>
<pre><code class="hljs language-dart">dependencies:
  equatable: ^<span class="hljs-number">1.1</span><span class="hljs-number">.1</span>
</code></pre>
<p>and then run command <code>pub get</code> on the terminal to install the package.</p>
<p><strong>For using Assets and Fonts:</strong> Drag and drop your assets and fonts in your app and add them to pubspec.yaml file.</p>
<p>For example - I've created two folders: <code>assets</code> and <code>fonts</code> and inside, I've added the image <code>image1.jpg</code> and font <code>IndieFlower-Regular.ttf</code>. My pubspec.yaml file will look like this:</p>
<pre><code class="hljs language-dart">assets:
    - assets/image1.jpg

fonts:
   - family: IndieFlower
     fonts:
       - asset: fonts/IndieFlower-Regular.ttf
</code></pre>
<p>and then run command <code>pub get</code> on terminal to use them.</p>
<p><strong>Importing files and packages</strong></p>
<p>If you want to use any files or packages in your current file, then you have to import that file or package.</p>
<p>For example - I want to use the <code>equatable</code> package. Then I will import like this:</p>
<pre><code class="hljs language-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:equatable/equatable.dart'</span>;
</code></pre>
<h2>Flutter example</h2>
<p>You can install Flutter SDK by following instructions from <a href="https://flutter.dev/docs/get-started/install">here</a></p>
<p>Create <strong>New Flutter Application</strong>. You can see the Sample App that Flutter has created for us and when you run it looks like this:</p>
<p><img src="/assets/img/articles/2020-04-21-Things-iOS-developer-should-know-before-starting-flutter/sampleApp.webp" alt="">
<img src="/assets/img/articles/2020-04-21-Things-iOS-developer-should-know-before-starting-flutter/tree.webp" alt=""></p>
<p>If you compare the simulator screenshot and code with the above tree, I hope you will understand the basics of UI structure. The code looks like this:</p>
<pre><code class="hljs language-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;

<span class="hljs-keyword">void</span> main() {
  runApp(MyApp());
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> MaterialApp(
      title: <span class="hljs-string">'Flutter Demo'</span>,
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: <span class="hljs-string">'Flutter Demo Home Page'</span>),
    );
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyHomePage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  MyHomePage({Key key, <span class="hljs-keyword">this</span>.title}) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> title;

  <span class="hljs-meta">@override</span>
  _MyHomePageState createState() => _MyHomePageState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_MyHomePageState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&#x3C;<span class="hljs-title">MyHomePage</span>> </span>{
  <span class="hljs-built_in">int</span> _counter = <span class="hljs-number">0</span>;

  <span class="hljs-keyword">void</span> _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: &#x3C;Widget>[
            Text(
              <span class="hljs-string">'You have pushed the button this many times:'</span>,
            ),
            Text(
              <span class="hljs-string">'<span class="hljs-subst">$_counter</span>'</span>,
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: <span class="hljs-string">'Increment'</span>,
        child: Icon(Icons.add),
      ),
    );
  }
}

</code></pre>
<h2>References</h2>
<p><a href="https://flutter.dev/">flutter.dev</a></p>
<p><a href="https://www.raywenderlich.com/4529993-getting-started-with-flutter#toc-anchor-001">Ray Wenderlich - Getting started with Flutter</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building a COVID-19 Tracing app in a week]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/04/03/Covid-19-Tracing-App-iOS</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/04/03/Covid-19-Tracing-App-iOS</guid>
            <pubDate>Fri, 03 Apr 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>We wanted to create a prototype app to help people to find out if they contacted anybody infected by the COVID-19 disease. It will use wireless technologies searching near by and notify users.
In this post we're going to talk about the thoughts behind this app, why we made it native and why we think our expertise is helpful in a situation like we find ourselves in right now.
Also we're going to talk about how the app is built, what different things we had in mind, and what challenges we faced during the implementation of the prototype.</p>
<h2>How does proximity tracing work?</h2>
<p>The concept is simple: We want to be able to see who a user has recently come in contact with. We can accomplish this by using bluetooth built into our phones. Bluetooth lets us send short range broadcast signals that other phones can see. When your phone detects a broadcast from another phone, we can simply make a note of the other phone's unique identifier and a timestamp of when the interaction occurred. Collecting this data creates a network, which can be used to trace interactions that people have had. During a global pandemic like COVID-19, this concept is very helpful in locating potential sources of the virus and can assist in limiting further spread.</p>
<h2>What about privacy?</h2>
<p>An important part of building an application like this is prioritizing a user's privacy. We don't want to track a person's movement and location, nor do we want users to be able to see where other users have been and who they have contacted. It is necessary that the data being recorded is anonymous, and that no location, medical, or personal information is required. If a user chooses to mark themselves as infected, the network will know that an identifier is infected, not a person.</p>
<h2>What are the different options when working with proximity?</h2>
<p>Proximity tracing is not a new concept, and as a result there are several options when looking into implementing a solution that needs it.</p>
<p>The most straightforward approach is to implement the logic yourself. Using the native bluetooth frameworks, all of the features are written from scratch. This work needs to be done for each platform and aligned to keep feature parity, and requires a large investment up front to build, and more work to maintain.</p>
<p>Peripherals are also an option- using technology that is already built for proximity sensing, e.g. iBeacons. The problem is that for the most part, peripherals and the protocols they use are built to only work with one platform. iBeacons can't work with Android, for example.</p>
<p>There are also several services that provide much of the solution for you.</p>
<p><a href="http://p2pkit.io/">p2pkit</a> is a multiplatform solution that provides an SDK that handles all of the implementation details for you. The application just needs to listen for broadcasts from other devices, and p2pkit reports the device ID when in range. An API call is made to verify the app with the account it is tied to (for billing reasons) but no other network requests are made. The SDK is old and could use an update, but the solution itself works well for its purpose.</p>
<p>Another platform solution is <a href="https://newaer.com/">NewAer</a>. This platform provides a vast amount of bluetooth related services, including proximity tracing. There are native SDKs for iOS and Android, although they are quite old. Pricing is set based on requests to the NewAer API.</p>
<p>We chose p2pkit in our implementation. Speed in setting up our product is critical, and we did not have the capacity to create a solution from scratch. The implementation is simple and we were up and running in less than half a day on both platforms. NewAer's feature set is overkill for our needs, and the old SDK code is a concern, as well as the need for network requests to their API. We may choose to write our own solution in the future, as the p2pkit SDKs are also old and created some less-than-ideal implementation details in our mobile apps.</p>
<h2>Why native is the right approach</h2>
<p>When building an app that relies on device hardware, native iOS and Android is the obvious choice. Cross platform technologies rely on wrapping hardware libraries, which create extra complexity and more places for things to go wrong. It also means if you want to use an external service, you will need to write and maintain a wrapper around their platform SDKs. The advantage of cross platform technologies is code reuse between platforms, and when working with device specific features, most of that advantage is lost. So there is little benefit to gain, and many chances for problems to arise, when using a non-native cross platform framework.</p>
<h2>Challenges and discoveries we encountered implementing the prototype</h2>
<p>As mentioned above, we chose to go with p2pkit for the prototype. So we started out setting up a base project, and added the framework.</p>
<h3>Bridging-Header</h3>
<p>Adding this framework can be done both with CocoaPods and manually. First we added the framework with CocoaPods, but that didn't seem to work - the static classes contained in the framework weren't accessible.
Then we implemented the framework manually, including all the additional dependencies mentioned in the documentation, and then saw that a <code>Bridging-Header</code> was needed. So, using CocoaPods is easier afterall, so we decided to remove the manually added frameworks again, and add p2pkit with CocoaPods, and now include the bridging-header as well. This made the classes accessible, and we were ready to continue.
In case anyone is in doubt, a <code>Bridging-Header</code> is a bridge between Objective-C code and Swift. So if a framework is written in Objective-C and is being used in a Swift project, the bridging-header is nessesary for the framework to be recognized.</p>
<h3>Background work</h3>
<p>After getting the framework up and running, we needed to find out how to still discover new devices, while being in the background, and even when the app has been force quit.
Luckily p2pkit supports the CoreBluetooth State Restoration API for iOS, which allows the app to still to continue discovering and be discovered even if the application is in the background. But - when force quitting the app, it stops broadcasting, which we had to find a solution to.</p>
<p>First, there's some thought that we had to keep in mind, when choosing the right approach. We want the phone to wake up while being terminated, to send out a broadcast, and also to receive a broadcast sent by another device, which also might have the app terminated. So we needed all devices with the app installed, to wake up at the same time, to send/receive this information to/from eachother.</p>
<p>Here's the options we went through:</p>
<h4>Android</h4>
<p>Android has started to restrict background services since Android 6. They introduced something called Doze Mode which basically prevents apps from accessing network and performing CPU-intensive work.
It is a good thing for users to extend their devices' battery life. There are few things to do to adapt our app to Doze mode.</p>
<h5>1.Foreground Service</h5>
<p>We can not use background services since the introduction of Doze Mode. If we want to do some work even when the app is force quit, foreground services is the only way to keep the app process running.
So we attached p2pkit to a foreground service, this way we can listen to events from p2pkit when the app is in the background or closed.</p>
<h5>2.Scheduling Periodic Jobs</h5>
<p>Android Operating System still can kill our foreground service if it needs more resources, so we need to make sure our service is running.
To check if the service is running, we scheduled 15 minutes interval periodic job. Basically this job will check if the service is running or not, and when it is not running it will start it again.<br>
For scheduling background jobs, we used Google's Android Jetpack library <a href="https://developer.android.com/topic/libraries/architecture/workmanager">WorkManager</a>.</p>
<h5>Result</h5>
<p>Because of the restrictions of the Android, we have to use both foreground service and periodic jobs to make sure our prototype will continue listening events from p2pkit.</p>
<h4>iOS</h4>
<h5>1.Background fetch</h5>
<p>Background fetch will let your app run in the background for about 30 seconds at scheduled intervals. The issue with this feature in our setup is, that you can't schedule it to run at exact times. Here's a snippet from Apples documentation:</p>
<blockquote>
<p>The system waits until network and power conditions are good, so you should be able to retrieve adequate amounts of data quickly.</p>
</blockquote>
<p>So basically iOS is deciding when it's the right time to open the app up and do the broadcasting. That's not good enough for us, since we need all devices to wake up at the same time - so let's move on to the next option.</p>
<h5>2.Remote (push)notifications</h5>
<p>Remote push notifications can be used to wake up the app, even though it's terminated. So this could be a possible solution, since push notifications can be silent, which means the user won't even notice that one is received. So far it sounds promising, but - this option would take that we send out push notifications from a backend in a cronjob, e.g. every 5 minutes. That doesn't sound too clean. Also we are not guaranteed that the push messages are delivered at the exact same time to all devices, which leads us back to the issue we had with <code>background fetch</code>.</p>
<h5>3.Local notifications</h5>
<p>Local notifications can be used in a similar way as remote push notifications, and will also open up the app shortly, and be able to run some code even though the app is terminated. Also we're able to schedule the notifications to fire every 5min in the hour, so everyone, with the same timezone at least, should receive these notifications at the same time, unless their clock on the phone is out of sync, of course. This sounds promising! Again there's a but. This time it's about the silence. We can't make a local push notification silent, as it's possible with remote notifications. So this means that the user would receive a notification from the app every 5 minutes, which probably would make them uninstall the app pretty quickly. So this wouldn't be a good solution either.</p>
<h5>Result</h5>
<p>We ended up going with local notifications, but not the recurring ones. We schedule a local notification 5 seconds after the app is terminated, reminding the user that for the app to work, is has to be running in the background. In that way, people who by mistake force quit the app are reminded to open it again. We're under the impression that people who have this app download it and use it actively because they want to participate. Also it seems more correct, letting the user decide if they want to broadcast or not, and not let the app run in the background, when they think it's properly closed.</p>
<h3>Distancing</h3>
<p>When talking proximity ranging, it's hard to convert signal strength directly to distance. p2pkit provides 5 levels of proximity strength:</p>
<ul>
<li><code>ExtremelyWeak</code></li>
<li><code>Weak</code></li>
<li><code>Medium</code></li>
<li><code>Strong</code></li>
<li><code>Immediate</code></li>
</ul>
<p>We tested <code>Immediate</code> first, to see how that worked out. At first it worked out pretty well - the distance before dropping to <code>Strong</code> was about 1.5 meters, but sometimes also even less. So we thought that it was a little too short of a distance, and tried with <code>Medium</code>. Medium seemed to reach around 5-6 meters before dropping to <code>Weak</code>, so logically we tried with <code>Strong</code> which turned out to be the best option, with a reach around 2-3 meters before dropping. So <code>Strong</code> it is!</p>
<h3>Customization</h3>
<p>We wanted to added a custom alertview for when bluetooth is not enabled, to ask the user to turn it on. It turns out that it’s not as easy as it sounds, since it's only possible to go directly to the phone's bluetooth settings through the native popup provided by the <code>CBCentralManager</code> when initializing it. Since the direct path to the bluetooth settings is going through a “non-public URL scheme”, it's not possible to link to this from a custom popup, as it likely will get your app rejected by Apple.</p>
<p>Our solution to this is to initialize the <code>CBCentralManager</code> with an option to opt out of the native popup (<code>options: [CBCentralManagerOptionShowPowerAlertKey: false]</code>) at first, then wait for the response in the delegate method <code>centralManagerDidUpdateState(_ central: CBCentralManager)</code>, to check if the status is <code>poweredOff</code>, and if it is, we show a custom popup with some more informative information, than the one Apple provides. When the user taps the button in the custom popup, we deinitialize the <code>CBCentralManager</code>, and initialize it again with the options set to display the native popup. That way we can give the user proper information at first, and then show the native afterwards.</p>
<h3>Broadcasting cross platforms</h3>
<p>For this to app to be useful, it has to be able to work cross platforms. We built an identical Android application, but found out there's some restrictions in bundleId's on Android, not allowing <code>-</code> to be used in the naming, such as <code>dk.nodes.my-awesome-app</code>. So on the Android platform it would have to be <code>dk.nodes.my_awesome_app</code>. Fine enough - we'll change the bundleId to <code>dk.nodes.my_awesome_app</code> on iOS then - until we found out that it's not possible to have <code>_</code> in the bundleId on the iOS platform. So in the end de decided to camelcase (<code>dk.nodes.myAwesomeApp</code>) the bundleId's to equalize them, since p2pkit needs the bundleId to process the broadcasts on their server properly.</p>
<h2>Conclusion</h2>
<p>Since we had a week to build this app, we chose an already existing framework instead of building our own. This obviously comes with some challenges as we already mentioned in this post, but all in all p2pkit was pretty suitable for our needs. We've noticed some differences in signal strengths across platforms and other smaller inconsistencies, that we're unable to look further into, since we don't have access to the source code. This is alright for now, but in the future we're probably going to build our own framework, as having full control of the code makes it much easier to work with when proceeding.
Our prototype app is definitely functional, and we're looking forward to dig more into this project - both because of it's good purpose, but also because it's an interesting type of app and a lot of fun working on solutions like this. Stay safe out there!</p>
<p>Documentation:</p>
<ul>
<li><a href="https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_background/updating_your_app_with_background_app_refresh">https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_background/updating_your_app_with_background_app_refresh</a></li>
<li><a href="https://developer.android.com/training/monitoring-device-state/doze-standby">https://developer.android.com/training/monitoring-device-state/doze-standby</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Android Image Framework Comparison]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/03/30/android-image-framework-comparison</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/03/30/android-image-framework-comparison</guid>
            <pubDate>Mon, 30 Mar 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>As an Android developer you have a lot of options when it comes to adding images to your app. In this blog we will give you a quick comparison between Glide (<a href="https://github.com/bumptech/glide">https://github.com/bumptech/glide</a>), Picasso(<a href="https://square.github.io/picasso/">https://square.github.io/picasso/</a>), Fresco (<a href="https://frescolib.org/docs/index.html">https://frescolib.org/docs/index.html</a>) and Coil (<a href="https://coil-kt.github.io/coil/getting_started/">https://coil-kt.github.io/coil/getting_started/</a>). We will look at the four different image frameworks and score them based on three different categories:</p>
<p><strong>Setup and simple usage</strong> - How easy is the framework to install and setup to just get an image loaded.</p>
<p><strong>RecyclerView</strong> - How easy is it to integrate into a RecyclerView where you have different images loaded, imagine something like a facebook feed.</p>
<p><strong>Transformation</strong> - How easy is it for the framework to add transformations, in the case of the test, we will just add corner rounding.</p>
<p>In the bottom of each category we will rate the different frameworks on a scale of 1 to 5, where 1 is the lowest and 5 is the highest.</p>
<h2>Setup and simple usage</h2>
<p>For this category we looked up the framework in question and followed the instructions to get started.</p>
<h3>Glide and Picasso</h3>
<p>We are ranking these two frameworks together as they are eerily similar in regards to setup and how they work.</p>
<p>For both of these frameworks all you have to do is to include the implementation of the given framework in your build.gradle file and then you are good to go.</p>
<p>They both follow the same structure for loading images which can be seen below:</p>
<pre><code class="hljs language-kotlin">(Glide|Picasso).with(CONTEXT)
    .load(URL)
    .into(IMAGEVIEW)
</code></pre>
<p>Both of these frameworks are just using Androids regular ImageView to load the images into.</p>
<h3>Fresco</h3>
<p>Fresco works a bit differently when compared to Glide and Picasso.</p>
<p>We also have to include the implementation in the build.gradle file for this framework. It however has a different step required, where you need to add an initialization step in the application part of your app.</p>
<p><code>Fresco.initialize(this)</code></p>
<p>This framework also uses its own specialized class to load images in with: <code>SimpleDraweeView</code></p>
<p>This would require some refactoring for users who have already set up most of their views with the standard <code>ImageView</code>.</p>
<p>Once you have set up all the right views, you can load images by the code described below:</p>
<p><code>ImageView.setImageURI(URL)</code></p>
<h3>Coil</h3>
<p>Coil follows the same procedure as the frameworks above. We will need to add the implementation in the build.gradle file. We have a small twist for this framework however, since we need to make sure the code is compiled for java 8. This however is done in two easy steps. We need to add the compileOptions part to the Android part of the build file. Afterwards we need the add a task below. This is all very well documented on the frameworks github page and shouldn't be a problem for anyone who has worked with Android before.</p>
<pre><code class="hljs language-groovy">android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
    kotlinOptions {
        jvmTarget = <span class="hljs-string">"1.8"</span>
    }
}
</code></pre>
<p>Once the framework is setup it is very easy to use. It uses the standard <code>ImageView</code> for displaying the pictures and the image is simply loaded in as can be seen in the code snippet below:</p>
<p><code>ImageView.load(URL)</code></p>
<p>Since Coil just adds an extension to the existing ImageView, this makes it very easy if you have already added some logic for ImageViews in your code.</p>
<h3>Score</h3>

















<table><thead><tr><th>Glide</th><th>Picasso</th><th>Fresco</th><th>Coil</th></tr></thead><tbody><tr><td>5</td><td>5</td><td>2</td><td>5</td></tr></tbody></table>
<h2>RecyclerView</h2>
<h3>Glide and Picasso</h3>
<p>Both of these frameworks are easy to adapt, all you need to do is add the image loading to the bind part of the code of the adapter and they both work like a charm. Both frameworks also provide easy access to a centercrop functionality which makes the elements in the RecyclerView uniform</p>
<h3>Fresco</h3>
<p>Work like described in the previous section all that needs to be done is adding the loading part into the adapter.</p>
<p>This framework also has a centercrop functionality, however it is not as intuitive as Glides or Picassos</p>
<h3>Coil</h3>
<p>Works just like the others: add the image loading to your bind function and you are set. The centercropping on this framework requires a bit more fiddling, but was still easy to setup.</p>
<h3>Score</h3>

















<table><thead><tr><th>Glide</th><th>Picasso</th><th>Fresco</th><th>Coil</th></tr></thead><tbody><tr><td>5</td><td>5</td><td>5</td><td>5</td></tr></tbody></table>
<h2>Transformation</h2>
<p>In this section we will try to make each of the frameworks add a 16 pixel rounded corners transformation.</p>
<h3>Glide</h3>
<p>On Glide this was very easy to achieve, all the tools needed were in the framework you have added to project. A simple example can be seen below to achieve the wanted result.</p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">val</span> requestOptions = RequestOptions()
requestOptions = requestOptions.transforms(CenterCrop(), RoundedCorners(<span class="hljs-number">16</span>))

Glide
    .with(CONTEXT)
    .load(URL)
    .apply(requestOptions)
    .into(IMAGEVIEW)
</code></pre>
<h3>Picasso</h3>
<p>In Picasso we did not have this transformation out of the box. So you would either have to create your own transformation or find a third party lib. We made it work with the following lib: <a href="https://github.com/wasabeef/picasso-transformations">https://github.com/wasabeef/picasso-transformations</a></p>
<p>Once the lib is added to project all you need is to add the transformation to the loading, this can be seen in the code snippet below:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">val</span> transformation = RoundedCornersTransformation(<span class="hljs-number">16</span>, <span class="hljs-number">0</span>)

Picasso
	.with(CONTEXT)
    .load(URL)
    .centerCrop()
    .transform(transformation)
    .fit()
    .into(mImage)
</code></pre>
<h3>Fresco</h3>
<p>In the Fresco framework transformation is handled differently than Glide or Picasso. Since we have our own ImageView class in this framework the rounding of the corners is done in the layout file and very easy to add. Another upside of this is that you would have less clutter in the code.</p>
<h3>Coil</h3>
<p>In Coil we tried to add the same behavior as in the other examples. However due to the way center crop is working in this framework, this gave some issues which we were not able solve in this blog.</p>
<p>Initially all the image were set up in an RecyclerView where they had the scaleType of centerCrop, this would result in uneven rounding of the corners where some of the images had rounding while others didn't.</p>
<p>An examples of the code can be seen below:</p>
<pre><code class="hljs language-kotlin">mImage.load(item.url) {
    placeholder(R.drawable.ic_launcher_foreground)
    scale(Scale.FILL)
    transformations(RoundedCornersTransformation(<span class="hljs-number">16f</span>))
}
</code></pre>
<h3>Score</h3>

















<table><thead><tr><th>Glide</th><th>Picasso</th><th>Fresco</th><th>Coil</th></tr></thead><tbody><tr><td>4</td><td>4</td><td>5</td><td>2</td></tr></tbody></table>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Combine networking with a hint of SwiftUI]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/03/16/Combine-networking-with-a-hint-of-swiftUI</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/03/16/Combine-networking-with-a-hint-of-swiftUI</guid>
            <pubDate>Mon, 16 Mar 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>In this blogpost we will examine what Combine is and how it can be used. Combine is a huge topic, and obviously, we will not cover everything here.
So, continue reading to learn how we can use Combine to make network requests in a very general and efficient way, and finally display the fetched data in a <code>List</code>, using <code>SwiftUI</code>.</p>
<h2>What is Combine?</h2>
<p>At WWDC 2019 Apple introduced the new framework Combine, which is their take on reactive programming for Swift.
The term "reactive programming" probably deserves its own blogpost, but for now, let's keep it simple, and say that reactive programming is like an observer/observable pattern. You subscribe to, in this case, a publisher, and when the publisher gets the data you're waiting for, it informs a subscriber, which is then able to act on that newly fetched data.</p>
<p>As Combine was released in 2019, the minimum iOS version is 13, so for Combine to run with your app, you must specify a minimum target of iOS 13+.</p>
<h2>When to use Combine, and how does it work?</h2>
<p>Reactive programming is mainly used to handle asynchronous datastreams inside your app, such as API calls (which is what this blogpost will cover). It can also be used if your UI has to wait for some action to happen, before being updated.</p>
<p>The way it works is described as a dataflow pipeline. There are three elements involved in this pipeline: publishers, operators and subscribers. Before diving into the actual elements, here is a simple overview of how they work:</p>
<blockquote>
<p>The <code>subscriber</code> asks the <code>publisher</code> for some data, which sends it through an <code>operator</code> before it ends up at the subscriber who requested it.</p>
</blockquote>
<h3>Publishers</h3>
<p>Simply, the publisher provides the data and an error if necessary. The data will be delivered as an object defined by us, and we can also handle custom errors.
There are 2 types of publishers:</p>
<ul>
<li><code>Just</code>: initialised from a value that only provides a single result (no error)</li>
<li><code>Future</code>: initialised with a closure that eventually resolves to a single output value or a failure completion</li>
</ul>
<p><strong>Subjects</strong> are a special kind of publisher, that is used to send specific values to one or multiple subscribers at the same time. There are two types of built-in subjects with Combine; <code>CurrentValueSubject</code> &#x26; <code>PassthroughSubject</code>. They act similarly, the difference being currentValueSubject remembers and requires an initial state, where passthroughSubject does not.</p>
<h3>Subscribers</h3>
<p>The subscriber asks the publisher for data. It's able to cancel the request if needed, which terminates a subscription and shuts down all the stream processing prior to any Completion sent by the publisher.
There are two types of subscribers build into Combine; <code>Assign</code> and <code>Sink</code>.
<code>.assign</code> assigns values to objects, like assigning a string to a labels text property directly.
<code>.sink</code> defines a closure, that accepts the value from the publisher when it's read.
In this blogpost's example, we will only use <code>.sink</code>.</p>
<h3>Operators</h3>
<p>An operator is a middle ground between the <code>publisher</code> and the <code>subscriber</code>, and because of this, it acts as both. When a publisher talks to the operator, it's acting as a subscriber, and when the subscriber talks to the operator, it's acting as a publisher.
The operators are used to change the data inside the pipeline. That could be if we want to filter out nil-values, change a timestamp etc. before returning. <code>Operator</code> is actually just a convenience name for higher order functions that you probably already know, such as <code>.map</code> , <code>.filter</code>, <code>.reduce</code> etc.</p>
<h2>Let's dig into some code 🧑‍💻</h2>
<p>Alright, now that we have the basic knowledge (kinda) sorted, let's dive a bit into the code that makes Combine useful as a reactive programming-framework.</p>
<p>In this example, we will use <code>The Movie Database API</code>, which takes that we have an <code>api_key</code>. So first of all, you need to go to their documentation and generate a token, which you will need to get actual data from their API: <a href="https://developers.themoviedb.org/3/authentication/how-do-i-generate-a-session-id">The Movie Database API documentation</a></p>
<p>Then create a new Xcode project, and make sure to pick <code>SwiftUI</code> as our <code>User Interface</code>.</p>
<p>![Project Creation](/assets/img/articles/2020-03-16-Combine-networking-with-a-hint-of-swiftUI/projectCreation.webp</p>
<p><em>💡 If you're following the example, please be aware that you might be met with compiler errors, before completing all steps</em></p>
<p>Alright, so this leaves us with an all fresh project, with the SwiftUI basic setup.
For good sakes measure, you might want to change the filename to <code>MoviesView</code>.
Afterwards, change the boilerplate code to look like this:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> SwiftUI

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">MoviesView</span>: <span class="hljs-title class_">View</span> {

    <span class="hljs-comment">// 1</span>
    <span class="hljs-meta">@ObservedObject</span> <span class="hljs-keyword">var</span> viewModel <span class="hljs-operator">=</span> <span class="hljs-type">MovieViewModel</span>()

    <span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
        <span class="hljs-type">List</span>(viewModel.movies) { movie <span class="hljs-keyword">in</span> <span class="hljs-comment">// 2</span>
            <span class="hljs-type">HStack</span> {
                <span class="hljs-type">VStack</span>(alignment: .leading) {
                    <span class="hljs-type">Text</span>(movie.title) <span class="hljs-comment">// 3a</span>
                        .font(.headline)
                    <span class="hljs-type">Text</span>(movie.originalTitle) <span class="hljs-comment">// 3b</span>
                        .font(.subheadline)
                }
            }
        }
    }
}

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">ContentView_Previews</span>: <span class="hljs-title class_">PreviewProvider</span> {

    <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> previews: <span class="hljs-keyword">some</span> <span class="hljs-type">View</span> {
        <span class="hljs-type">MoviesView</span>()
    }
}
</code></pre>
<p>Explanation:</p>
<ol>
<li>We add the <code>@ObservedObject</code> property wrapper, to let our app know, what we need to observe for any changes in the viewModel property.</li>
<li>We give our List the array of movies that we are going to fetch together with Combine. This will later be the part that automatically updates the list, when the data is added to the movies-array.</li>
<li>a+b: We add the movie's title and its original title to a Text-object.</li>
</ol>
<p><em>💡 If you changed the name of the ContentView, you have to go to your <code>SceneDelegate</code>-file and change the name there as well</em></p>
<p>Not much code needed to set up our super-simple listview - if we were to do this with UIKit and a UITableView, we would have had a lot more code. So let's take a second to appreciate SwiftUI 👏.</p>
<p><strong>Create the models for the data we are about to fetch: <code>MovieResponse</code> and <code>Movie</code></strong></p>
<p>Add the following to the two newly created files:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> Foundation

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">MovieResponse</span>: <span class="hljs-title class_">Codable</span> {
    <span class="hljs-keyword">let</span> movies: [<span class="hljs-type">Movie</span>]

    <span class="hljs-keyword">enum</span> <span class="hljs-title class_">CodingKeys</span>: <span class="hljs-title class_">String</span>, <span class="hljs-title class_">CodingKey</span> {
        <span class="hljs-keyword">case</span> movies <span class="hljs-operator">=</span> <span class="hljs-string">"results"</span>
    }
}
</code></pre>
<p>and for the Movie-model:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> Foundation

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">Movie</span>: <span class="hljs-title class_">Codable</span>, <span class="hljs-title class_">Identifiable</span> {
    <span class="hljs-keyword">var</span> id <span class="hljs-operator">=</span> <span class="hljs-type">UUID</span>()
    <span class="hljs-keyword">let</span> movieId: <span class="hljs-type">Int</span>
    <span class="hljs-keyword">let</span> originalTitle: <span class="hljs-type">String</span>
    <span class="hljs-keyword">let</span> title: <span class="hljs-type">String</span>

    <span class="hljs-keyword">enum</span> <span class="hljs-title class_">CodingKeys</span>: <span class="hljs-title class_">String</span>, <span class="hljs-title class_">CodingKey</span> {
        <span class="hljs-keyword">case</span> movieId <span class="hljs-operator">=</span> <span class="hljs-string">"id"</span>
        <span class="hljs-keyword">case</span> originalTitle <span class="hljs-operator">=</span> <span class="hljs-string">"original_title"</span>
        <span class="hljs-keyword">case</span> title
    }
}
</code></pre>
<p>Now that we got our models in place, let's get to the fun part.</p>
<h2>🕺 ..Finally! It's time to fetch some data!</h2>
<p>We are going to build a very general <code>APIClient</code>, which we will call from our <code>MovieViewModel</code> that we instantiated in the very top of our <code>MoviesView</code> that we just did.</p>
<p><strong>Create a new file named <code>MovieViewModel</code></strong>, and add the following to it:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> Foundation
<span class="hljs-keyword">import</span> Combine

<span class="hljs-keyword">class</span> <span class="hljs-title class_">MovieViewModel</span>: <span class="hljs-title class_">ObservableObject</span> {

    <span class="hljs-meta">@Published</span> <span class="hljs-keyword">var</span> movies: [<span class="hljs-type">Movie</span>] <span class="hljs-operator">=</span> [] <span class="hljs-comment">// 1</span>
    <span class="hljs-keyword">var</span> cancellationToken: <span class="hljs-type">AnyCancellable</span>? <span class="hljs-comment">// 2</span>

    <span class="hljs-keyword">init</span>() {
        getMovies() <span class="hljs-comment">// 3</span>
    }

}

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">MovieViewModel</span> {

    <span class="hljs-comment">// Subscriber implementation</span>
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">getMovies</span>() {
        cancellationToken <span class="hljs-operator">=</span> <span class="hljs-type">MovieDB</span>.request(.trendingMoviesWeekly) <span class="hljs-comment">// 4</span>
            .mapError({ (error) -> <span class="hljs-type">Error</span> <span class="hljs-keyword">in</span> <span class="hljs-comment">// 5</span>
                <span class="hljs-built_in">print</span>(error)
                <span class="hljs-keyword">return</span> error
            })
            .sink(receiveCompletion: { <span class="hljs-keyword">_</span> <span class="hljs-keyword">in</span> }, <span class="hljs-comment">// 6</span>
                  receiveValue: {
                    <span class="hljs-keyword">self</span>.movies <span class="hljs-operator">=</span> <span class="hljs-variable">$0</span>.movies <span class="hljs-comment">// 7</span>
            })
    }

}
</code></pre>
<p>Explanation:</p>
<ol>
<li>The <code>@Published</code> property wrapper lets Swift know to keep an eye on any changes of this variable. If anything changes, the body in all views where this variable is used, will update.</li>
<li>Subscriber implementations can use this type to provide a “cancellation token” that makes it possible for a caller to cancel a publisher. Be aware that your network calls won't work if you're not assigning your call to a variable of this type.</li>
<li>We are fetching the data as soon as the ViewModel is created, since there's no lifecycle in SwiftUI like we're used to from UIKit.</li>
<li>Here we start the actual request for "trending movies weekly"</li>
<li>Here we can handle the errors, if any</li>
<li>Here the actual subscriber is created. As mentioned earlier, the sink-subscriber comes with a closure, that lets us handle the received value when it's ready from the publisher.</li>
<li>We assign the received data to the movies-property - this will trigger the action mentioned in step 1</li>
</ol>
<p>Alright, so far so good - we still need to make the actual API call. For this, we need a general <code>APIClient</code>.
So, <strong>create a new file named <code>APIClient</code></strong> and add the following code:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> Foundation
<span class="hljs-keyword">import</span> Combine

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">APIClient</span> {

    <span class="hljs-keyword">struct</span> <span class="hljs-title class_">Response</span>&#x3C;<span class="hljs-title class_">T</span>> { <span class="hljs-comment">// 1</span>
        <span class="hljs-keyword">let</span> value: <span class="hljs-type">T</span>
        <span class="hljs-keyword">let</span> response: <span class="hljs-type">URLResponse</span>
    }

    <span class="hljs-keyword">func</span> <span class="hljs-title function_">run</span>&#x3C;<span class="hljs-type">T</span>: <span class="hljs-type">Decodable</span>>(<span class="hljs-keyword">_</span> <span class="hljs-params">request</span>: <span class="hljs-type">URLRequest</span>) -> <span class="hljs-type">AnyPublisher</span>&#x3C;<span class="hljs-type">Response</span>&#x3C;<span class="hljs-type">T</span>>, <span class="hljs-type">Error</span>> { <span class="hljs-comment">// 2</span>
        <span class="hljs-keyword">return</span> <span class="hljs-type">URLSession</span>.shared
            .dataTaskPublisher(for: request) <span class="hljs-comment">// 3</span>
            .tryMap { result -> <span class="hljs-type">Response</span>&#x3C;<span class="hljs-type">T</span>> <span class="hljs-keyword">in</span>
                <span class="hljs-keyword">let</span> value <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">JSONDecoder</span>().decode(<span class="hljs-type">T</span>.<span class="hljs-keyword">self</span>, from: result.data) <span class="hljs-comment">// 4</span>
                <span class="hljs-keyword">return</span> <span class="hljs-type">Response</span>(value: value, response: result.response) <span class="hljs-comment">// 5</span>
            }
            .receive(on: <span class="hljs-type">DispatchQueue</span>.main) <span class="hljs-comment">// 6</span>
            .eraseToAnyPublisher() <span class="hljs-comment">// 7</span>
    }
}
</code></pre>
<p>Explanation:</p>
<ol>
<li>This is our generic response object. The value property will be the actual object, and the response property will be the URL response including status code etc.</li>
<li>This is our only entry point for network requests, no matter if it's <code>GET</code>, <code>POST</code> or whatever - it's all specified in the request parameter.</li>
<li>Here we are "turning the <code>URLSession</code> into a publisher"</li>
<li>Decode the result to the generic type we defined in the <code>APIClient</code> (in this case <code>MovieResponse</code>)</li>
<li>Our "homemade" Response object now contains the actual data + the URL response from which we can find status code etc.</li>
<li>Return the result on the main thread</li>
<li>We end with erasing the publisher's type, since it can be very long and "complicated", and then transform and return it as the return type we want <code>(AnyPublisher&#x3C;Response&#x3C;T>, Error>)</code></li>
</ol>
<p>Now we have the <code>APIClient</code> set up, and as you can see, it just takes an <code>URLRequest</code> as parameter, so let's make something that can build URLRequests for us, that matches our API.</p>
<p><strong>Create a new file and name it <code>MovieDBAPI</code></strong></p>
<p>Add the following code to the newly created file:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> Foundation
<span class="hljs-keyword">import</span> Combine

<span class="hljs-comment">// 1</span>
<span class="hljs-keyword">enum</span> <span class="hljs-title class_">MovieDB</span> {
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> apiClient <span class="hljs-operator">=</span> <span class="hljs-type">APIClient</span>()
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> baseUrl <span class="hljs-operator">=</span> <span class="hljs-type">URL</span>(string: <span class="hljs-string">"https://api.themoviedb.org/3/"</span>)<span class="hljs-operator">!</span>
}

<span class="hljs-comment">// 2</span>
<span class="hljs-keyword">enum</span> <span class="hljs-title class_">APIPath</span>: <span class="hljs-title class_">String</span> {
    <span class="hljs-keyword">case</span> trendingMoviesWeekly <span class="hljs-operator">=</span> <span class="hljs-string">"trending/movie/week"</span>
}

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">MovieDB</span> {

    <span class="hljs-keyword">static</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">request</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">path</span>: <span class="hljs-type">APIPath</span>) -> <span class="hljs-type">AnyPublisher</span>&#x3C;<span class="hljs-type">MovieResponse</span>, <span class="hljs-type">Error</span>> {
        <span class="hljs-comment">// 3</span>
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">var</span> components <span class="hljs-operator">=</span> <span class="hljs-type">URLComponents</span>(url: baseUrl.appendingPathComponent(path.rawValue), resolvingAgainstBaseURL: <span class="hljs-literal">true</span>)
            <span class="hljs-keyword">else</span> { <span class="hljs-built_in">fatalError</span>(<span class="hljs-string">"Couldn't create URLComponents"</span>) }
        components.queryItems <span class="hljs-operator">=</span> [<span class="hljs-type">URLQueryItem</span>(name: <span class="hljs-string">"api_key"</span>, value: <span class="hljs-string">"your_api_key_here"</span>)] <span class="hljs-comment">// 4</span>

        <span class="hljs-keyword">let</span> request <span class="hljs-operator">=</span> <span class="hljs-type">URLRequest</span>(url: components.url<span class="hljs-operator">!</span>)

        <span class="hljs-keyword">return</span> apiClient.run(request) <span class="hljs-comment">// 5</span>
            .map(\.value) <span class="hljs-comment">// 6</span>
            .eraseToAnyPublisher() <span class="hljs-comment">// 7</span>
    }
}
</code></pre>
<p>Explanation:</p>
<ol>
<li>Set up the basics needed for making the request</li>
<li>Set up the paths we want to be able to call from the API.</li>
<li>Here we create the URL request. The request is a <code>GET</code>-request by default, hence we don't need to specify that.</li>
<li>Add the api_key you created at <code>The Movie Database</code> here!</li>
<li>We run the newly created request through our API client</li>
<li>Map is our operator, that lets us set the type of output we want. <code>\.value</code> in this case, is our generic type defined as return value of this method (<code>MoviesData</code>), since the client returns a Response-object, which contains both a value and a response property, but for now, we're only interested in the value.</li>
<li>This call cleans up the return type from something like <code>Publishers.MapKeyPath&#x3C;AnyPublisher&#x3C;APIClient.Response&#x3C;MoviesData>, Error>, T></code> to the expected type: <code>AnyPublisher&#x3C;MoviesData, Error></code></li>
</ol>
<h2>🎉 That's it!</h2>
<p>Run the project, and you should be presented with a list of weekly trending movies.
Combine publishes the list as soon as it's ready, all reactive and smoothly.</p>
<p>The way we built the <code>APIClient</code>, allows us to easily add more paths to the <code>APIPath</code> enum in a nice and clean way.
For simplicity, we only made a very basic and simple <code>GET</code> request, but we could make the <code>func request(_ path: APIPath)</code> function build any other kinds of requests for us as well, e.g a <code>POST</code>-request. The <code>APIClient</code> just takes a <code>URLRequest</code> of any kind, and that's what we feed it.</p>
<p>I hope you made it work and can see the advantages of using Combine for networking. It's super powerful as soon as you get the grasp of it!</p>
<p><em>Article Photo by <a href="https://unsplash.com">Unsplash</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Avoiding tampering on Android]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/03/06/avoiding-tampering-on-android</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/03/06/avoiding-tampering-on-android</guid>
            <pubDate>Fri, 06 Mar 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>It's important to keep user data safe. Like any software, Android can be
target of attacks to access valuable data and even though no software
is perfectly safe, as developers we should always follow the <a href="https://developer.android.com/topic/security/best-practices">security best practices</a>
and do what is in our reach to keep data safe.</p>
<p>Here are some measures that can help you make your development safer.
We don't grant the perfect safety of the methods here, so if you're dealing
with sensitive data, we recommend that you take it at your own will and you
should still validate all the measures.</p>
<h2>Debugger Check</h2>
<p>Here we have two options: we can check if the app is set as debuggable
and verify if there's any debugger attached. This is a good check for the
release version where usually we obfuscate the app (<a href="https://developer.android.com/studio/build/shrink-code">you're doing it, right!?</a>)
and we shouldn't be able to see debug info like logs, for example.</p>
<p>To verify if the app has the debuggable flag active, you can do the following:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">val</span> isDebuggable = applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE != <span class="hljs-number">0</span>
</code></pre>
<p>And to verify if there's any debugger attached at the moment, you can use
this code:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">val</span> isDebuggerAttached = Debug.isDebuggerConnected() || Debug.waitingForDebugger()
</code></pre>
<p>If you're using Kotlin you can use the following to Extension to easy your
life to verify if there's some debugger running.</p>
<pre><code class="hljs language-kotlin"><span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">runSensitiveCalculation</span><span class="hljs-params">()</span></span> = guardDebugger {
    <span class="hljs-comment">// No debugger attached</span>
    <span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> Calculation</span>
}

<span class="hljs-comment">/**
 * Executes [function] only if no debugger is attached.
 */</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">guardDebugger</span><span class="hljs-params">(function: (() -> <span class="hljs-type">Unit</span>))</span></span> {
    <span class="hljs-keyword">val</span> isDebuggerAttached = Debug.isDebuggerConnected() || Debug.waitingForDebugger()
    <span class="hljs-keyword">if</span> (!isDebuggerAttached) {
        function.invoke()
    }
}
</code></pre>
<h2>File / App Tampering</h2>
<p>A malicious user can try to modify some parts of your app or an external
resources you may have to use, like some external file your need to download.
We can try to mitigate these tamperings verifying the file integrity and/or
the app signature as well as the install origin of the app to confirm that it
wasn't installed from a not trustable source.</p>
<h3>File Integrity</h3>
<p>A common and safe way to verify if some file is altered compared to the
original is to validate its checksum. If you have to download files and
have an original checksum to compare the result, you can use Android's
<code>MessageDigest</code> class to do so. Just try to avoid weaker algorithms like
<code>MD5</code> or <code>SHA-1</code> since those have been exploited and formally deprecated
by the National Institute of Standards and Technology (NIST).</p>
<p>The Following code shows how to get a <code>SHA-256</code> checksum of a File's <code>InputStream</code>:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">generateChecksum</span><span class="hljs-params">(inputStream: <span class="hljs-type">InputStream</span>, algorithm : <span class="hljs-type">String</span> = <span class="hljs-string">"SHA-256"</span>)</span></span>: String? = <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">val</span> digest: MessageDigest = MessageDigest.getInstance(algorithm)
    <span class="hljs-keyword">val</span> hash: ByteArray = digest.digest(inputStream.readBytes())
    hash.toHexString()
} <span class="hljs-keyword">catch</span> (exception: NoSuchAlgorithmException) {
    exception.printStackTrace()
    <span class="hljs-literal">null</span>
}

<span class="hljs-function"><span class="hljs-keyword">fun</span> ByteArray.<span class="hljs-title">toHexString</span><span class="hljs-params">()</span></span> : String{
    <span class="hljs-keyword">val</span> hexChars = <span class="hljs-string">"0123456789ABCDEF"</span>.toCharArray()
    <span class="hljs-keyword">val</span> result = StringBuffer()

    forEach {
        <span class="hljs-keyword">val</span> octet = it.toInt()
        <span class="hljs-keyword">val</span> firstIndex = (octet and <span class="hljs-number">0xF0</span>).ushr(<span class="hljs-number">4</span>)
        <span class="hljs-keyword">val</span> secondIndex = octet and <span class="hljs-number">0x0F</span>
        result.append(hexChars[firstIndex])
        result.append(hexChars[secondIndex])
    }

    <span class="hljs-keyword">return</span> result.toString()
}
</code></pre>
<h3>App signature</h3>
<p>Developers must sign applications with their private key before the app
can be installed on user devices. The resulting app signature will be
broken if the APK is altered in any way.</p>
<p>Use the following class to verify your app signature at runtime:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">object</span> AppSignatureValidator {

    <span class="hljs-keyword">enum</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Result</span> </span>{
        VALID,
        INVALID,
        UNKNOWN
    }

    <span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> Set value for expectedSignature</span>
    <span class="hljs-comment">//  - Run app with AppSignatureValidator.validate()</span>
    <span class="hljs-comment">//  - Check logs for 'EXPECTED_SIGNATURE' and set value</span>
    <span class="hljs-comment">//  - Remove line Log.d("EXPECTED_SIGNATURE",...</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">val</span> expectedSignature = <span class="hljs-string">""</span> <span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> SET!</span>

    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">validate</span><span class="hljs-params">(context: <span class="hljs-type">Context</span>)</span></span>: Result {
        context.getAppSignature()?.string()?.let { currentSignature ->

            Log.d(<span class="hljs-string">"EXPECTED_SIGNATURE"</span>, currentSignature) <span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> REMOVE!</span>

            <span class="hljs-keyword">return</span> <span class="hljs-keyword">if</span> (currentSignature == expectedSignature) {
                Result.VALID
            } <span class="hljs-keyword">else</span> {
                Result.INVALID
            }
        }
        <span class="hljs-keyword">return</span> Result.UNKNOWN
    }

    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> Context.<span class="hljs-title">getAppSignature</span><span class="hljs-params">()</span></span>: Signature? = <span class="hljs-keyword">if</span> (Build.VERSION.SDK_INT &#x3C; <span class="hljs-number">28</span>) {
        packageManager.getPackageInfo(
            packageName,
            PackageManager.GET_SIGNATURES
        ).signatures.firstOrNull()
    } <span class="hljs-keyword">else</span> {
        packageManager.getPackageInfo(
            packageName,
            PackageManager.GET_SIGNING_CERTIFICATES
        ).signingInfo.apkContentsSigners.firstOrNull()
    }

    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> Signature.<span class="hljs-title">string</span><span class="hljs-params">()</span></span>: String? = <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">val</span> signatureBytes = toByteArray()
        <span class="hljs-keyword">val</span> digest = MessageDigest.getInstance(<span class="hljs-string">"SHA"</span>)
        <span class="hljs-keyword">val</span> hash = digest.digest(signatureBytes)
        Base64.encodeToString(hash, Base64.NO_WRAP)
    } <span class="hljs-keyword">catch</span> (exception: Exception) {
        <span class="hljs-literal">null</span>
    }
}
</code></pre>
<p>Notice that it would be a good thing to obfuscate your <code>expectedSignature</code>
value.</p>
<h3>App install origin</h3>
<p>You can also verify the origin of the installation so you just let your
app run if installed from trustable sources::</p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">enum</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Installer</span></span>(<span class="hljs-keyword">val</span> id: String) {
    GOOGLE_PLAY_STORE(id = <span class="hljs-string">"com.android.vending"</span>),
    AMAZON_APP_STORE(id = <span class="hljs-string">"com.amazon.venezia"</span>)
}

<span class="hljs-function"><span class="hljs-keyword">fun</span> Context.<span class="hljs-title">verifyInstaller</span><span class="hljs-params">(installer: <span class="hljs-type">Installer</span>)</span></span>: <span class="hljs-built_in">Boolean</span> {
    <span class="hljs-keyword">return</span> packageManager.getInstallerPackageName(packageName).startsWith(installer.id)
}
</code></pre>
<h2>SafetyNet</h2>
<p>The <a href="https://developer.android.com/training/safetynet">SafetyNet</a> is a tool from Google to help developers to spot tampering
attempts and take actions to avoid it (like not letting the user to run
the app). This can help to detect users with a rooted device that could
try to intercept or modify sensible data in your app.</p>
<h3>Setting up</h3>
<p>To use SafetyNet, you need Google Project API Token. Here's a quick
overview of how you can get one:</p>
<ol>
<li>Go to <a href="https://console.developers.google.com/apis/library">https://console.developers.google.com/apis/library</a></li>
<li>Search for and select the Android Device Verification. The Android Device Verification API dashboard screen appears.</li>
<li>If the API isn't already enabled, click Enable.</li>
<li>If the Create credentials button appears, click on it to generate an API key. Otherwise, click the All API credentials drop-down list, then select the API key that's associated with your project that has enabled the Android Device Verification API.</li>
<li>In the sidebar on the left, click Credentials. Copy the API key that appears.</li>
</ol>
<p>It's recommended that the API key is not in plain text inside the apk, as
a compromised device can have access to this.</p>
<p>After doing this, you can add the dependency on your gradle:</p>
<pre><code class="hljs language-groovy">implementation <span class="hljs-string">'com.google.android.gms:play-services-safetynet:17.0.0'</span>
</code></pre>
<h3>Verifying Google Services Availability</h3>
<p>First, you should verify if the user has a compatible Google Play Service
version running on the device. You can verify this doing the following:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">if</span>(GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context)== ConnectionResult.SUCCESS) {
  <span class="hljs-comment">// The SafetyNet Attestation API is available.</span>
} <span class="hljs-keyword">else</span> {
  <span class="hljs-comment">// Prompt user to update Google Play services.</span>
}
</code></pre>
<h3>Nonce</h3>
<p>Nonce is a term for a unique number that should't be replicated. It's
recommended that you generate this on your server in a safe manner to
avoid replications of requests using the same nonce. As per Google's
recommendation:</p>
<blockquote>
<p>As a best practice, derive part of the nonce from the data being sent to your servers. For example, concatenate the hash of the username with the request timestamp to form the nonce.</p>
</blockquote>
<p>If you want to locally make a test with a nonce, you can do so with the
following:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-comment">// Nonce generation example:</span>
<span class="hljs-keyword">val</span> nonce = ByteArray(<span class="hljs-number">32</span>)
SecureRandom().nextBytes(byteArray)
</code></pre>
<h3>Requesting a SafetyNet attestation</h3>
<p>With your nonce generated, you can use the following code snippet to
receive an <a href="https://developer.android.com/training/safetynet/attestation#kotlin">attestation</a> request response:</p>
<pre><code class="hljs language-kotlin">SafetyNet.getClient(<span class="hljs-keyword">this</span>).attest(nonce, API_KEY)
    .addOnSuccessListener(<span class="hljs-keyword">this</span>) {
        <span class="hljs-comment">// Indicates communication with the service was successful.</span>
        <span class="hljs-comment">// Use response.getJwsResult() to get the result data.</span>
    }
    .addOnFailureListener(<span class="hljs-keyword">this</span>) { e ->
        <span class="hljs-comment">// An error occurred while communicating with the service.</span>
        <span class="hljs-keyword">if</span> (e <span class="hljs-keyword">is</span> ApiException) {
            <span class="hljs-comment">// An error with the Google Play services API contains some</span>
            <span class="hljs-comment">// additional details.</span>
            <span class="hljs-keyword">val</span> apiException = e <span class="hljs-keyword">as</span> ApiException

            <span class="hljs-comment">// You can retrieve the status code using the</span>
            <span class="hljs-comment">// apiException.statusCode property.</span>
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">// A different, unknown type of error occurred.</span>
            Log.d(FragmentActivity.TAG, <span class="hljs-string">"Error: "</span> + e.message)
        }
    }
</code></pre>
<h3>Verifying the response</h3>
<p>With the request response in hands, it's time to validate the response
to confirm if the device has been tampered with. It's important to notice
that the ideal solution is for the server to validate the response and
apply restriction if the device is tampered (like invalidating the token)
as a malicious user can try to reverse engineer the app and remove the
validation from your code making the SafetyNet request useless.</p>
<p>If you want to trigger some action as well in the app, you can parse
the response on the device as well.</p>
<p>This is the payload response object:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">data</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SafetyNetResponse</span></span>(
    <span class="hljs-keyword">val</span> timestampMs: <span class="hljs-built_in">Long</span>,
    <span class="hljs-keyword">val</span> nonce: String,
    <span class="hljs-keyword">val</span> apkDigestSha256: String,
    <span class="hljs-keyword">val</span> apkPackageName: String,
    <span class="hljs-keyword">val</span> apkCertificateDigestSha256: List&#x3C;String>,
    <span class="hljs-keyword">val</span> ctsProfileMatch: <span class="hljs-built_in">Boolean</span> = <span class="hljs-literal">false</span>,
    <span class="hljs-keyword">val</span> basicIntegrity: <span class="hljs-built_in">Boolean</span> = <span class="hljs-literal">false</span>,
    <span class="hljs-keyword">val</span> advice: List&#x3C;String>? = <span class="hljs-literal">null</span>
)
</code></pre>
<p>You can parse it on the <code>addOnSuccessListener()</code> like this:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">parseJsonWebSignature</span><span class="hljs-params">(jwsResult: <span class="hljs-type">String</span>?)</span></span>: SafetyNetResponse? {
    jwsResult ?: <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>
    <span class="hljs-keyword">val</span> parts = jwsResult.split(<span class="hljs-string">"."</span>)
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">if</span> (parts.size == <span class="hljs-number">3</span>) {
        <span class="hljs-comment">//we're only really interested in the body/payload</span>
        <span class="hljs-keyword">val</span> decodedPayload = String(Base64.decode(jwtParts[<span class="hljs-number">1</span>], Base64.DEFAULT))
        Gson().fromJson&#x3C;SafeResponse>(decodedPayload)
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-literal">null</span>
    }
}
</code></pre>
<p>This is a description of what each of the items on the SafetyNetResponse
means:</p>
<ul>
<li><strong>timestampMs</strong>: Milliseconds past the UNIX epoch when the JWS response message was generated by Google's servers.</li>
<li><strong>nonce</strong>: The single-use token that the calling app passes to the API.</li>
<li><strong>apkPackageName</strong>: The calling app's package name.</li>
<li><strong>apkCertificateDigestSha256</strong>: Base-64 encoded representation(s) of the SHA-256 hash of the calling app's signing certificate(s)</li>
<li><strong>ctsProfileMatch</strong>: A stricter verdict of device integrity. If the value of ctsProfileMatch is true, then the profile of the device running your app matches the profile of a device that has passed Android compatibility testing.</li>
<li><strong>basicIntegrity</strong>: A more lenient verdict of device integrity. If only the value of basicIntegrity is true, then the device running your app likely wasn't tampered with. However, the device hasn't necessarily passed Android compatibility testing.</li>
<li><strong>error</strong>: Encoded error information relevant to the current API request.</li>
<li><strong>advice</strong>: A suggestion for how to get a device back into a good state.</li>
</ul>
<p>Here is a good further reading: <a href="https://android-developers.googleblog.com/2017/11/10-things-you-might-be-doing-wrong-when.html">10 things you might be doing wrong when using the SafetyNet Attestation API</a></p>
<h2>There's no such thing as a perfect app</h2>
<p>After applying all these tips to your app you are improving the general
security of your application and making it hard for malicious users to
modify and distribute your app. An important thing to keep in mind is that
security can always be improved and the tips here are not to be the
perfect solution. Technology evolves and safe methods today can be unsafe
tomorrow. So put in mind that you always have to try to keep one step ahead
of malicious users and be aware of methods that are not safe anymore so your
app doesn't become exposed.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Animated Transitions in ViewController - Part 1]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/02/27/View-Controller-Transitions</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/02/27/View-Controller-Transitions</guid>
            <pubDate>Thu, 27 Feb 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>The hunger for magnificent design, animations and transitions is huge and nowadays designers are pushing developers to create beautiful views with lots of animations and smooth transitions.</p>
<p>Based on this, I will present some basic concepts of transitions and animations. This is the first in a series of three blogposts.</p>
<p>In this article, we will create a custom transition with <a href="https://developer.apple.com/documentation/uikit/animation_and_haptics/view_controller_transitions">View Controller Transitions</a> and understand how it works based on UIKit. This is a powerful framework and you can use it to create smooth transitions and animate the elements of your view controller.</p>
<p>And to satisfy this hunger, nothing better than a Pizza App, right? Or maybe a salad if you don't like pizza...</p>
<p>![Salad](/assets/img/articles/2020-02-27-View-Controller-Transitions/EatPizza.webp</p>
<p>By the end of this article you will be able to:</p>
<ul>
<li>Create your custom transition</li>
<li>Animate view elements</li>
<li>Understand the transition delegates methods</li>
</ul>
<p>The final result should look something like this 😉</p>
<p>![Pizza Transition](/assets/img/articles/2020-02-27-View-Controller-Transitions/PizzaPart1.webp</p>
<p>You can also clone the final project from <a href="https://github.com/nodes-ios/transitions-animations-part1">here</a>.</p>
<h2>Let's get started!</h2>
<p>In this example, I'm using a simple ViewController with a UICollectionView. When an item is selected, a second ViewController is presented.</p>
<p>As our <code>PizzaViewController</code> is embedded in a UINavigationController, our ViewController must conform to the protocol <code>UINavigationControllerDelegate</code>. In this case, we are assigning the delegate to self and we will implement this protocol in a second.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">override</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">viewDidLoad</span>() {
    <span class="hljs-keyword">super</span>.viewDidLoad()

    navigationController<span class="hljs-operator">?</span>.delegate <span class="hljs-operator">=</span> <span class="hljs-keyword">self</span>

    viewModel.delegate <span class="hljs-operator">=</span> <span class="hljs-keyword">self</span>
    viewModel.getPizzas()
}
</code></pre>
<p>To trigger our animation every time you click in one of the cells, we store the IndexPath in a variable in the ViewModel for future reference, and for presenting the <code>PizzaDetailViewController</code></p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">extension</span> <span class="hljs-title class_">PizzaViewController</span>: <span class="hljs-title class_">UICollectionViewDelegate</span> {
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">collectionView</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">collectionView</span>: <span class="hljs-type">UICollectionView</span>, <span class="hljs-params">didSelectItemAt</span> <span class="hljs-params">indexPath</span>: <span class="hljs-type">IndexPath</span>) {

        viewModel.indexPath <span class="hljs-operator">=</span> indexPath
        <span class="hljs-keyword">let</span> pizza <span class="hljs-operator">=</span> viewModel.pizzas[indexPath.row]

        <span class="hljs-keyword">let</span> detailVC <span class="hljs-operator">=</span> <span class="hljs-type">PizzaDetailViewController</span>.instantiate()
        detailVC.pizza <span class="hljs-operator">=</span> pizza

        navigationController<span class="hljs-operator">?</span>.modalPresentationStyle <span class="hljs-operator">=</span> .custom
        navigationController<span class="hljs-operator">?</span>.pushViewController(detailVC, animated: <span class="hljs-literal">true</span>)
    }
}
</code></pre>
<p>So far there's nothing special other than assigning the NavigationController delegate to our ViewController.</p>
<h3>Implementing the delegate</h3>
<p>This is where things starts getting interesting 😀</p>
<p>We can have different animations, depending on whether you are presenting or dismissing a ViewController. To find out which one you should use, you can check the value of a property called <code>operation</code> with type <code>UINavigationController.Operation</code>. This property will tell us whether the user is presenting or dismissing a view.</p>
<p>In the first part of this example, we will implement only the presentation animation.</p>
<p>Your code should look like this:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">extension</span> <span class="hljs-title class_">PizzaViewController</span>: <span class="hljs-title class_">UINavigationControllerDelegate</span> {
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">navigationController</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">navigationController</span>: <span class="hljs-type">UINavigationController</span>,
                              <span class="hljs-params">animationControllerFor</span> <span class="hljs-params">operation</span>: <span class="hljs-type">UINavigationController</span>.<span class="hljs-type">Operation</span>,
                              <span class="hljs-params">from</span> <span class="hljs-params">fromVC</span>: <span class="hljs-type">UIViewController</span>,
                              <span class="hljs-params">to</span> <span class="hljs-params">toVC</span>: <span class="hljs-type">UIViewController</span>) -> <span class="hljs-type">UIViewControllerAnimatedTransitioning</span>? {
        <span class="hljs-keyword">switch</span> operation {
        <span class="hljs-keyword">case</span> .push:
            <span class="hljs-keyword">return</span> <span class="hljs-type">AnimationManager</span>(animationDuration: <span class="hljs-number">1.5</span>, animationType: .present)
        <span class="hljs-keyword">case</span> .pop:
            <span class="hljs-keyword">return</span> <span class="hljs-type">AnimationManager</span>(animationDuration: <span class="hljs-number">1.5</span>, animationType: .dismiss)
        <span class="hljs-keyword">default</span>:
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
        }
    }
}
</code></pre>
<p>Notice here that we are returning an object of type <code>UIViewControllerAnimatedTransitioning</code> which is our <code>AnimationManager</code>class. We'll cover that later. Hold on!</p>
<p>To make our life easier, both of our ViewControllers implement the protocol <code>PizzaTransitionable</code>. This protocol has two properties and we will use them in our animations. As we are dealing with a CollectionView and IndexPath, it's handy to have this kind of helper.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">protocol</span> <span class="hljs-title class_">PizzaTransitionable</span> {
    <span class="hljs-keyword">var</span> backgroundView: <span class="hljs-type">UIView</span> { <span class="hljs-keyword">get</span> }
    <span class="hljs-keyword">var</span> pizzaImage: <span class="hljs-type">UIImageView</span> { <span class="hljs-keyword">get</span> }
}
</code></pre>
<h5>Implementing the protocol on PizzaViewController</h5>
<pre><code class="hljs language-swift"><span class="hljs-keyword">extension</span> <span class="hljs-title class_">PizzaViewController</span>: <span class="hljs-title class_">PizzaTransitionable</span> {
    <span class="hljs-keyword">var</span> backgroundView: <span class="hljs-type">UIView</span> {
        <span class="hljs-keyword">guard</span>
            <span class="hljs-keyword">let</span> indexPath <span class="hljs-operator">=</span> viewModel.indexPath,
            <span class="hljs-keyword">let</span> pizzaCell <span class="hljs-operator">=</span> collectionView.cellForItem(at: indexPath) <span class="hljs-keyword">as?</span> <span class="hljs-type">PizzaViewCell</span>
        <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-type">UIView</span>()
        }
        <span class="hljs-keyword">return</span> pizzaCell.containerView
    }

    <span class="hljs-keyword">var</span> pizzaImage: <span class="hljs-type">UIImageView</span> {
        <span class="hljs-keyword">guard</span>
            <span class="hljs-keyword">let</span> indexPath <span class="hljs-operator">=</span> viewModel.indexPath,
            <span class="hljs-keyword">let</span> pizzaCell <span class="hljs-operator">=</span> collectionView.cellForItem(at: indexPath) <span class="hljs-keyword">as?</span> <span class="hljs-type">PizzaViewCell</span>
        <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-type">UIImageView</span>()
        }
        <span class="hljs-keyword">return</span> pizzaCell.pizzaImage
    }
}
</code></pre>
<h5>Implementing the protocol on PizzaDetailViewController</h5>
<pre><code class="hljs language-swift"><span class="hljs-keyword">extension</span> <span class="hljs-title class_">PizzaDetailViewController</span>: <span class="hljs-title class_">PizzaTransitionable</span> {
    <span class="hljs-keyword">var</span> backgroundView: <span class="hljs-type">UIView</span> {
        <span class="hljs-keyword">return</span> detailBackgroundView
    }

    <span class="hljs-keyword">var</span> pizzaImage: <span class="hljs-type">UIImageView</span> {
        <span class="hljs-keyword">return</span> detailPizzaImage
    }
}
</code></pre>
<p>Right now we have everything ready to get our hands dirty.</p>
<p>![Excellent](/assets/img/articles/2020-02-27-View-Controller-Transitions/Excellent.webp</p>
<h2>Animating the transition 😍</h2>
<p>Now it's time to see what's inside of our <code>AnimationManager</code> class.</p>
<p>Here we have two properties <code>animationDuration</code> and <code>animationType</code>. Probably you already know what they mean, right?</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">AnimationManager</span>: <span class="hljs-title class_">NSObject</span> {

    <span class="hljs-comment">// MARK: - Variables</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> animationDuration: <span class="hljs-type">Double</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> animationType: <span class="hljs-type">AnimationType</span>

    <span class="hljs-comment">// MARK: - Init</span>
    <span class="hljs-keyword">init</span>(<span class="hljs-params">animationDuration</span>: <span class="hljs-type">Double</span>, <span class="hljs-params">animationType</span>: <span class="hljs-type">AnimationType</span>) {
        <span class="hljs-keyword">self</span>.animationDuration <span class="hljs-operator">=</span> animationDuration
        <span class="hljs-keyword">self</span>.animationType <span class="hljs-operator">=</span> animationType
    }
}
</code></pre>
<p>The <code>AnimationType</code> is a custom enum and it has only 2 cases. We use it to know when we need to animate the presenting or dismissing of a view.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">enum</span> <span class="hljs-title class_">AnimationType</span> {
    <span class="hljs-keyword">case</span> present
    <span class="hljs-keyword">case</span> dismiss
}
</code></pre>
<p>We also need to make our AnimationManager conform to the <code>UIViewControllerAnimatedTransitioning</code> protocol.</p>
<p>The view controller will call the animator object from the transitioning delegate and will use it to perform the animation as you can see at the end of this snippet code.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">extension</span> <span class="hljs-title class_">AnimationManager</span>: <span class="hljs-title class_">UIViewControllerAnimatedTransitioning</span> {
    <span class="hljs-comment">// Return the animation duration defined when we instantiated the view.</span>
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">transitionDuration</span>(<span class="hljs-params">using</span> <span class="hljs-params">transitionContext</span>: <span class="hljs-type">UIViewControllerContextTransitioning</span>?) -> <span class="hljs-type">TimeInterval</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-type">TimeInterval</span>(exactly: animationDuration) <span class="hljs-operator">??</span> <span class="hljs-number">0</span>
    }

    <span class="hljs-comment">// Retrieve the ViewControllers and call the animation method</span>
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">animateTransition</span>(<span class="hljs-params">using</span> <span class="hljs-params">transitionContext</span>: <span class="hljs-type">UIViewControllerContextTransitioning</span>) {
        <span class="hljs-keyword">guard</span>
            <span class="hljs-keyword">let</span> toViewController <span class="hljs-operator">=</span> transitionContext.viewController(forKey: .to),
            <span class="hljs-keyword">let</span> fromViewController <span class="hljs-operator">=</span> transitionContext.viewController(forKey: .from)
        <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">// We only complete transition with success if the transition was executed.</span>
            transitionContext.completeTransition(<span class="hljs-literal">false</span>)
            <span class="hljs-keyword">return</span>
        }

        <span class="hljs-comment">// According to the animation type we call the method to animate the presenting or dismissing.</span>
        <span class="hljs-keyword">switch</span> animationType {
        <span class="hljs-keyword">case</span> .present:
            presentAnimation(
                transitionContext: transitionContext,
                fromView: fromViewController,
                toView: toViewController
            )
        <span class="hljs-keyword">case</span> .dismiss:
            dismissAnimation(
                transitionContext: transitionContext,
                fromView: fromViewController,
                toView: toViewController
            )
        }
    }
}
</code></pre>
<h4>Presenting the animation</h4>
<p>For a clean and organized codebase, the logic is split into different chunks of code using extensions. That way the code is more readable and nicer as you can see in the example project! 🤓</p>
<p>But here it is shown separately:</p>
<p>For this animation, the duration is split in two parts. The first one should run in 1/3 of the duration and the second part the other 2/3 of the time.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Assigning the context to a variable.</span>
<span class="hljs-keyword">let</span> containerView <span class="hljs-operator">=</span> transitionContext.containerView

<span class="hljs-comment">// We split the whole animation in 2 different parts.</span>
<span class="hljs-comment">// The fist part should run in 1/3 of the time and the second part 2/3 of the time.</span>
<span class="hljs-keyword">let</span> firstPartDuration <span class="hljs-operator">=</span> (animationDuration <span class="hljs-operator">/</span> <span class="hljs-number">3</span>)
<span class="hljs-keyword">let</span> secondPartDuration <span class="hljs-operator">=</span> (animationDuration <span class="hljs-operator">/</span> <span class="hljs-number">3</span>) <span class="hljs-operator">*</span> <span class="hljs-number">2</span>
</code></pre>
<p>Since delegate provides us a ViewController object, we need to cast it into the view type to access its objects.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Cast the ViewControllers to their original type</span>
<span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> fromPizzaVC <span class="hljs-operator">=</span> fromView <span class="hljs-keyword">as?</span> <span class="hljs-type">PizzaViewController</span> <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> }
<span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> toPizzaDetailVC <span class="hljs-operator">=</span> toView <span class="hljs-keyword">as?</span> <span class="hljs-type">PizzaDetailViewController</span> <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> }
</code></pre>
<p>Instead of animating the real objects of the view, we mirror them by creating new objects assigning the same position as the original ones. That way we can hide the ViewController and play with the "fake" objects.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Mirroring the objects that we will animate.</span>
<span class="hljs-keyword">let</span> backgroundView <span class="hljs-operator">=</span> <span class="hljs-type">UIView</span>()
<span class="hljs-keyword">let</span> pizzaSnapshot <span class="hljs-operator">=</span> <span class="hljs-type">UIImageView</span>()

<span class="hljs-comment">// Storing the frame positions.</span>
<span class="hljs-keyword">let</span> backgroundFrame <span class="hljs-operator">=</span> containerView.convert(
    fromPizzaVC.backgroundView.frame,
    from: fromPizzaVC.backgroundView.superview
)

<span class="hljs-keyword">let</span> pizzaSnapshotFrame <span class="hljs-operator">=</span> containerView.convert(
    fromPizzaVC.pizzaImage.frame,
    from: fromPizzaVC.pizzaImage.superview
)

<span class="hljs-comment">// Setting up new objects with original attributes from the presented view controller.</span>
backgroundView.frame <span class="hljs-operator">=</span> backgroundFrame
backgroundView.backgroundColor <span class="hljs-operator">=</span> fromPizzaVC.backgroundView.backgroundColor
backgroundView.layer.cornerRadius <span class="hljs-operator">=</span> fromPizzaVC.backgroundView.layer.cornerRadius

pizzaSnapshot.frame <span class="hljs-operator">=</span> pizzaSnapshotFrame
pizzaSnapshot.image <span class="hljs-operator">=</span> fromPizzaVC.pizzaImage.image
pizzaSnapshot.contentMode <span class="hljs-operator">=</span> .scaleAspectFit
</code></pre>
<p>Right after creating our "fake" objects, we need to add it to the containerView, then we can hide/show the objects from the original ViewControllers as you wish.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Adding the subviews to the container view.</span>
containerView.addSubview(fromPizzaVC.view)
containerView.addSubview(toPizzaDetailVC.view)
containerView.addSubview(backgroundView)
containerView.addSubview(pizzaSnapshot)

<span class="hljs-comment">// Hidding/Showing objects to not/be displayed while animating the transition.</span>
fromPizzaVC.view.isHidden <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>
fromPizzaVC.collectionView.isHidden <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>
toPizzaDetailVC.view.isHidden <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>
</code></pre>
<p>Since the animations are running in a chain, we animate the objects using <code>UIViewPropertyAnimator</code>. That way, we have more control over our animations and the code is more readable.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Background view final position.</span>
<span class="hljs-keyword">let</span> frameAnim <span class="hljs-operator">=</span> <span class="hljs-type">CGRect</span>(x: <span class="hljs-number">0</span>, y: <span class="hljs-number">0</span>, width: <span class="hljs-type">UIScreen</span>.main.bounds.width, height: <span class="hljs-type">UIScreen</span>.main.bounds.height)

<span class="hljs-comment">// Animate the view objects using PropertyAnimator</span>
<span class="hljs-keyword">let</span> animator1 <span class="hljs-operator">=</span> {
    <span class="hljs-type">UIViewPropertyAnimator</span>(duration: firstPartDuration, dampingRatio: <span class="hljs-number">1</span>) {
        backgroundView.transform <span class="hljs-operator">=</span> <span class="hljs-type">CGAffineTransform</span>(scaleX: <span class="hljs-number">0.9</span>, y: <span class="hljs-number">0.9</span>)
    }
}()

<span class="hljs-keyword">let</span> animator2 <span class="hljs-operator">=</span> {
    <span class="hljs-type">UIViewPropertyAnimator</span>(duration: secondPartDuration, curve: .easeInOut) {
        backgroundView.frame <span class="hljs-operator">=</span> frameAnim
        pizzaSnapshot.transform <span class="hljs-operator">=</span> <span class="hljs-type">CGAffineTransform</span>(rotationAngle: .pi)
        pizzaSnapshot.frame <span class="hljs-operator">=</span> containerView.convert(
            toPizzaDetailVC.pizzaImage.frame,
            from: toPizzaDetailVC.pizzaImage.superview
        )
    }
}()
</code></pre>
<p>And the final step is to define the sequence and start the animation. 🤗</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Prepare the animations sequence</span>
animator1.addCompletion { <span class="hljs-keyword">_</span> <span class="hljs-keyword">in</span>
    animator2.startAnimation()
}

animator2.addCompletion { <span class="hljs-keyword">_</span> <span class="hljs-keyword">in</span>
    fromPizzaVC.collectionView.isHidden <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>
    toPizzaDetailVC.view.isHidden <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>

    backgroundView.removeFromSuperview()
    pizzaSnapshot.removeFromSuperview()

    transitionContext.completeTransition(<span class="hljs-literal">true</span>)
}

<span class="hljs-comment">// Run animations</span>
animator1.startAnimation()
</code></pre>
<h4>Dismissing the animation</h4>
<p>As you know, we are not going to implement the dismiss method in this article. But stay tuned because we'll prepare some interesting animations in the future. For now, let's just go back to the <code>PizzaViewController</code> without doing anything.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">private</span> <span class="hljs-keyword">extension</span> <span class="hljs-title class_">AnimationManager</span> {
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">dismissAnimation</span>(<span class="hljs-params">transitionContext</span>: <span class="hljs-type">UIViewControllerContextTransitioning</span>,
                          <span class="hljs-params">fromView</span>: <span class="hljs-type">UIViewController</span>,
                          <span class="hljs-params">toView</span>: <span class="hljs-type">UIViewController</span>) {

        <span class="hljs-keyword">let</span> containerView <span class="hljs-operator">=</span> transitionContext.containerView

        containerView.addSubview(toView.view)
        containerView.addSubview(fromView.view)

        <span class="hljs-comment">// We'll not animate anything. This will be covered in the next part of the article.</span>
        transitionContext.completeTransition(<span class="hljs-literal">true</span>)
    }
}
</code></pre>
<h2>Where to Go From Here?</h2>
<p>Now that you've learned some of the basics animations when transitioning your ViewControllers you can try to improve and make some incredible transitions! The possibilities are endless!!</p>
<h2>Resources</h2>
<ul>
<li><a href="https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/CustomizingtheTransitionAnimations.html">Customizing the Transition Animations</a></li>
</ul>
<p>Photo by Rodolfo Clix from Pexels</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Tool to generate ARM templates]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/01/20/tool-to-generate-ARM-templates</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/01/20/tool-to-generate-ARM-templates</guid>
            <pubDate>Mon, 20 Jan 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/template-syntax">ARM templates</a> are a powerfull tool and from a DevOps perspective can deal with a lot of the hassle in creating new environments and ensure that the environments don't differ. However writing ARM templates can be a tedious task as there aren't tools for generating templates and you often have to do an actual deploy to get relevant error messages about invalid values and missing dependencies. One method of creating ARM templates is to export templates from the <a href="https://portal.azure.com">Azure portal</a>, however the templates generated this way are often bloated and you still need to do a lot of manual work to make parameters and variables for the settings that differ between environments.</p>
<p>We have developed a tool for generating ARM templates which can be seen <a href="https://armtemplategenerator.z16.web.core.windows.net/">here</a>. The tool is still under development. Currently it's only possible to create storage accounts and blob containers, but it shows the potential of such a tool.</p>
<h3>Structure of an ARM template</h3>
<p>An ARM template consists of four parts:</p>
<ol>
<li>Parameters</li>
<li>Variables</li>
<li>Resources</li>
<li>Outputs</li>
</ol>
<p>We will go into a short description of each part excluding outputs.</p>
<h4>Parameters</h4>
<p>Parameters are quite simple in their nature, but this is the point where you are able to differentiate the environments. You are able to define numbers, strings, objects etc. as a parameter, which can be used in variables and resources. Parameters are isolated, meaning they cannot reference any other parts of the ARM template.</p>
<h4>Variables</h4>
<p>Variables are a bit more complicated than parameters, but still their functionality is limited. You can define strings, arrays, and objects as a variable and you can use other variables and parameters as part of your variable. This can e.g. be used to combine two parameters:</p>
<pre><code class="hljs language-less">"<span class="hljs-selector-tag">variable</span>": "<span class="hljs-selector-attr">[concat(parameters(<span class="hljs-string">'name'</span>), <span class="hljs-string">'-'</span>, parameters(<span class="hljs-string">'environment'</span>))]</span>"
</code></pre>
<h4>Resources</h4>
<p>Resources are the heart of the ARM template. Each resource corresponds to a resource in the Azure environment. The resources can utilise parameters and variables as part of their specification.</p>
<p>Resources only have a few shared properties between them, so knowing how e.g. a database server is defined doesn't necessarily help you with defining another type of resource. Also the limitations of a property is not always intuitive and it can be bothersome to look up what's possible. This is the main focus of the application to mitigate these differences in a simple way.</p>
<h3>Goals of the tool</h3>
<p>There is one main focus of the tool: Making it easy to create a functioning ARM template. The goal of the tool is not be able to support all use cases and especially not the large scale ones.
To achieve this goal, there are a few subgoals to complete:</p>
<ul>
<li>Create or reference dependencies automatically</li>
<li>Create parameters automatically including limit to valid values</li>
<li>Be able to use parameters, variables, and functions</li>
</ul>
<p>The tool looks like this:</p>
<p><img src="/assets/img/articles/2020-01-20-tool-to-generate-ARM-templates/1.webp" alt=""></p>
<p>The different parts are visible in a friendly way on the left, the working window in the middle (it's here the forms for parameters, variables, resources, and outputs will be, where you'll do most of your work), and finally the generated template on the right which is updated automatically each time you press <code>save</code>.</p>
<h3>Technology used</h3>
<p>The idea of the project was to be as simple as possible and accessible, therefore we implemented it as a static web page using the following:</p>
<ul>
<li>React</li>
<li>TypeScript</li>
</ul>
<p>We're going to skip going into detail why these technologies are used as the program flow is in focus although a object oriented language in general makes it easier with the power of inheritence.</p>
<h3>Class structure</h3>
<p>The general idea of the application is to have a base class handle all the basic information of resources such as name and location and let the inheriting classes handle resource specific information.</p>
<p>In our case two different base classes are needed: one for handling the resource information itself and one for handling form logic on the web page <a href="https://github.com/nodes-dotnet/arm-template-generator-typescript/blob/master/src/models/Resource.ts">Resource.ts</a> and <a href="https://github.com/nodes-dotnet/arm-template-generator-typescript/blob/master/src/components/WorkingWindow/Resources/ResourceTypeForm.tsx">ResourceTypeForm.tsx</a> respectively.</p>
<p>The form logic classes are responsible for keeping track of the properties of the given resource and which parameters to create. For each resource property, it's possible to define a parameter name, if you want to generate a parameter for it. It will automatically create a parameter of the correct type for the property and limit the options to what's allowed including setting those limitations on the created parameter.</p>
<h3>Automatic parameter creation</h3>
<p>For parameter creation we went with a function in <a href="https://github.com/nodes-dotnet/arm-template-generator-typescript/blob/master/src/components/WorkingWindow/Resources/ResourceTypeForm.tsx">ResourceTypeForm.tsx</a> to check if a parameter should be created and do so adding it to the list of new parameters.</p>
<pre><code class="hljs language-TypeScript"><span class="hljs-keyword">protected</span> <span class="hljs-title function_">createParameter</span>(<span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>, <span class="hljs-attr">defaultValue</span>: <span class="hljs-built_in">boolean</span> | <span class="hljs-built_in">number</span> | <span class="hljs-built_in">string</span>, <span class="hljs-attr">type</span>: <span class="hljs-built_in">string</span>, <span class="hljs-attr">allowedValues</span>: <span class="hljs-built_in">number</span>[] | <span class="hljs-built_in">string</span>[], <span class="hljs-attr">parameterList</span>: { [<span class="hljs-attr">index</span>: <span class="hljs-built_in">string</span>]: <span class="hljs-title class_">Parameter</span> }): <span class="hljs-built_in">void</span> {
    <span class="hljs-keyword">if</span>(!name) {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    }

    <span class="hljs-keyword">let</span> parameter = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Parameter</span>();
    <span class="hljs-keyword">if</span>(defaultValue !== <span class="hljs-literal">null</span> &#x26;&#x26; defaultValue !== <span class="hljs-literal">undefined</span> &#x26;&#x26; defaultValue !== <span class="hljs-string">""</span>) {
        parameter.<span class="hljs-property">defaultValue</span> = defaultValue;
    }
    <span class="hljs-keyword">if</span>(allowedValues &#x26;&#x26; allowedValues.<span class="hljs-property">length</span> > <span class="hljs-number">0</span>) {
        parameter.<span class="hljs-property">allowedValues</span> = allowedValues
    }
    parameter.<span class="hljs-property">type</span> = <span class="hljs-keyword">type</span>;

    parameterList[name] = parameter;
}
</code></pre>
<p>When the function is called, the parameters for the function are quite self explanatory where they come from except for the allowed values. The allowed values are only applied if it's not an empty list. The allowed values are defined in each resource's respective class and fetched from there, when creating a parameter as can be seen here from the class <a href="https://github.com/nodes-dotnet/arm-template-generator-typescript/blob/master/src/components/WorkingWindow/Resources/StorageAccountForm.tsx">StorageAccountForm.tsx</a>:</p>
<pre><code class="hljs language-TypeScript"><span class="hljs-variable language_">this</span>.<span class="hljs-title function_">createParameter</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">state</span>.<span class="hljs-property">kindParameterName</span>, <span class="hljs-variable language_">this</span>.<span class="hljs-property">state</span>.<span class="hljs-property">kind</span>, <span class="hljs-string">"string"</span>, <span class="hljs-title class_">StorageAccount</span>.<span class="hljs-property">allowedKinds</span>, parametersToCreate);
</code></pre>
<p>If we look in <a href="https://github.com/nodes-dotnet/arm-template-generator-typescript/blob/master/src/models/Resources/StorageAccount.ts">StorageAccount.ts</a>, we can see that <code>allowedKinds</code> is a static property which defines all allowed values for a storage account type:</p>
<pre><code class="hljs language-TypeScript"><span class="hljs-keyword">static</span> <span class="hljs-attr">allowedKinds</span>:<span class="hljs-built_in">string</span>[] = [<span class="hljs-string">"Storage"</span>, <span class="hljs-string">"StorageV2"</span>, <span class="hljs-string">"BlobStorage"</span>, <span class="hljs-string">"FileStorage"</span>, <span class="hljs-string">"BlockBlobStorage"</span>];
</code></pre>
<p>This approach allows us to let the models keep track of what's possible and will in turn mean less maintenance when Microsoft adds other options. This static property is also used to populate the options list when setting the property in the web form.</p>
<h3>Automatic dependency creation</h3>
<p>The automatic dependency creation is really helpful if you just want a standard setup, or you're not sure what a resource actually depends on. When creating a resource, every type of dependent resource will be in the bottom of the form, where you can either select an existing resource (if any are available), or create a new one with the basic setup. All you need to specify is the name of the resource.</p>
<p>When creating a new resource, if that type of resource is dependant on another resource type, the form will recursively ask for dependencies until you either have selected existing resources or the resource types are not dependent on any other resource types e.g. when entering a blob container, the blob container requires a blob container service which in turn requires a storage account.</p>
<p>To achieve this functionality, every resource type implements a static function defined here:</p>
<pre><code class="hljs language-TypeScript"><span class="hljs-keyword">static</span> <span class="hljs-title function_">getDefault</span>(<span class="hljs-attr">_name</span>: <span class="hljs-built_in">string</span>, <span class="hljs-attr">_resourceDependency</span>: <span class="hljs-title class_">ResourceDependency</span>): <span class="hljs-title class_">Resource</span>[]
</code></pre>
<p>All that's required is the name you want to assign to the resource and <code>ResourceDependency</code>. The resource dependency is a model to maintain the dependency graph. If you want to look into the implementation, you can find it <a href="https://github.com/nodes-dotnet/arm-template-generator-typescript/blob/master/src/models/Resources/ResourceDependency.ts">here</a>. An example from <a href="https://github.com/nodes-dotnet/arm-template-generator-typescript/blob/master/src/models/Resources/StorageAccountBlobContainer.ts">StorageAccountBlobContainer.ts</a>, where it automatically creates a default blob service if you have chosen to create or new, otherwise it fetches the selected service, sets the dependent resources and adds the container to list of created resources which in turn tells the form logic to add the resources returned from the <code>getDefault</code> function.</p>
<pre><code class="hljs language-TypeScript"><span class="hljs-keyword">static</span> <span class="hljs-title function_">getDefault</span>(<span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>, <span class="hljs-attr">dependencyModel</span>: <span class="hljs-title class_">ResourceDependency</span>): <span class="hljs-title class_">Resource</span>[] {
    <span class="hljs-keyword">let</span> <span class="hljs-attr">resources</span>: <span class="hljs-title class_">Resource</span>[] = [];
    <span class="hljs-keyword">let</span> <span class="hljs-attr">blobService</span>: <span class="hljs-title class_">StorageAccountBlobService</span>;

    <span class="hljs-title class_">Object</span>.<span class="hljs-title function_">keys</span>(dependencyModel.<span class="hljs-property">newResources</span>).<span class="hljs-title function_">forEach</span>(<span class="hljs-function"><span class="hljs-params">type</span> =></span> {
        <span class="hljs-keyword">const</span> <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span> = dependencyModel.<span class="hljs-property">newResources</span>[<span class="hljs-keyword">type</span>];
        <span class="hljs-keyword">if</span>(<span class="hljs-keyword">type</span> === <span class="hljs-title class_">StorageAccountBlobService</span>.<span class="hljs-property">resourceType</span>) {
            resources.<span class="hljs-property">push</span>.<span class="hljs-title function_">apply</span>(resources, <span class="hljs-title class_">StorageAccountBlobService</span>.<span class="hljs-title function_">getDefault</span>(name, dependencyModel.<span class="hljs-property">required</span>.<span class="hljs-title function_">find</span>(<span class="hljs-function"><span class="hljs-params">r</span> =></span> r.<span class="hljs-property">type</span> === <span class="hljs-title class_">StorageAccountBlobService</span>.<span class="hljs-property">resourceType</span>)));

            blobService = resources.<span class="hljs-title function_">find</span>(<span class="hljs-function"><span class="hljs-params">r</span> =></span> r.<span class="hljs-property">type</span> === <span class="hljs-title class_">StorageAccountBlobService</span>.<span class="hljs-property">resourceType</span>) <span class="hljs-keyword">as</span> <span class="hljs-title class_">StorageAccountBlobService</span>;
        }
    });

    <span class="hljs-title class_">Object</span>.<span class="hljs-title function_">keys</span>(dependencyModel.<span class="hljs-property">existingResources</span>).<span class="hljs-title function_">forEach</span>(<span class="hljs-function"><span class="hljs-params">type</span> =></span> {
        <span class="hljs-keyword">let</span> resource = dependencyModel.<span class="hljs-property">existingResources</span>[<span class="hljs-keyword">type</span>];
        <span class="hljs-keyword">if</span>(resource.<span class="hljs-property">type</span> === <span class="hljs-title class_">StorageAccountBlobService</span>.<span class="hljs-property">resourceType</span>) {
            blobService = resource <span class="hljs-keyword">as</span> <span class="hljs-title class_">StorageAccountBlobService</span>;
        }
    });

    <span class="hljs-keyword">let</span> service = <span class="hljs-keyword">new</span> <span class="hljs-title class_">StorageAccountBlobContainer</span>();
    service.<span class="hljs-property">requiredResources</span> = blobService;
    service.<span class="hljs-property">setName</span> = name;
    service.<span class="hljs-property">dependsOn</span> = [blobService.<span class="hljs-title function_">getResourceId</span>()];

    resources.<span class="hljs-title function_">push</span>(service);

    <span class="hljs-keyword">return</span> resources;
}
</code></pre>
<p>With this logic, it is possible to add a storage container including the blob service and storage account by only entering the name of each resource and the public access level of the container.</p>
<p><img src="/assets/img/articles/2020-01-20-tool-to-generate-ARM-templates/2.webp" alt=""></p>
<p>By pressing save the following ARM template is created:</p>
<pre><code class="hljs language-JSON"><span class="hljs-punctuation">{</span>
  <span class="hljs-attr">"$schema"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"contentVersion"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"1.0.0.0"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"parameters"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
    <span class="hljs-attr">"StorageContainerPublicAccess"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">"defaultValue"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Blob"</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">"allowedValues"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span>
        <span class="hljs-string">"None"</span><span class="hljs-punctuation">,</span>
        <span class="hljs-string">"Container"</span><span class="hljs-punctuation">,</span>
        <span class="hljs-string">"Blob"</span>
      <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"string"</span>
    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"StorageContainerName"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">"defaultValue"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"StorageContainer"</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"string"</span>
    <span class="hljs-punctuation">}</span>
  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"variables"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"resources"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span>
    <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">"apiVersion"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2019-04-01"</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Microsoft.Storage/storageAccounts"</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"storageaccount"</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">"location"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"[resourceGroup().location]"</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">"kind"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"StorageV2"</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">"properties"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
        <span class="hljs-attr">"accessTier"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Cool"</span><span class="hljs-punctuation">,</span>
        <span class="hljs-attr">"supportsHttpsTrafficOnly"</span><span class="hljs-punctuation">:</span> <span class="hljs-keyword">true</span><span class="hljs-punctuation">,</span>
        <span class="hljs-attr">"encryption"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
          <span class="hljs-attr">"keySource"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Microsoft.Storage"</span><span class="hljs-punctuation">,</span>
          <span class="hljs-attr">"services"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
            <span class="hljs-attr">"blob"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
              <span class="hljs-attr">"enabled"</span><span class="hljs-punctuation">:</span> <span class="hljs-keyword">true</span>
            <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
            <span class="hljs-attr">"file"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
              <span class="hljs-attr">"enabled"</span><span class="hljs-punctuation">:</span> <span class="hljs-keyword">true</span>
            <span class="hljs-punctuation">}</span>
          <span class="hljs-punctuation">}</span>
        <span class="hljs-punctuation">}</span>
      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">"sku"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
        <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Standard_LRS"</span><span class="hljs-punctuation">,</span>
        <span class="hljs-attr">"tier"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Standard"</span>
      <span class="hljs-punctuation">}</span>
    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
    <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">"apiVersion"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2019-04-01"</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Microsoft.Storage/storageAccounts/blobServices"</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">"dependsOn"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span>
        <span class="hljs-string">"[resourceId('Microsoft.Storage/storageAccounts', 'storageaccount')]"</span>
      <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"storageaccount/default"</span>
    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
    <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">"apiVersion"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2019-04-01"</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Microsoft.Storage/storageAccounts/blobServices/containers"</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"storageaccount/default/[parameters('StorageContainerName')]"</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">"properties"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
        <span class="hljs-attr">"publicAccess"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Blob"</span>
      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">"dependsOn"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span>
        <span class="hljs-string">"[resourceId('Microsoft.Storage/storageAccounts/blobServices', 'storageaccount', 'default')]"</span>
      <span class="hljs-punctuation">]</span>
    <span class="hljs-punctuation">}</span>
  <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"outputs"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<h3>Restrictions</h3>
<p>Even though the tool creates valid templates, it doesn't check that the names are available, only that the syntax is valid. As the tool is meant as a light-weight helper tool, this is not planned to be implemented.</p>
<h3>Next steps</h3>
<p>This tool shows that it's possible to make a tool, which can leviate a lot of the hassles of writing an ARM template, although utilising the full power of the ARM template with a tool like this would require a lot of work and may not be worth the development time.</p>
<p>The next step of the tool is to support more resource types and refactor the shared logic between resources to make it more readable and more simple.</p>
<h3>Resources</h3>
<ul>
<li>The code is available on Github <a href="https://github.com/nodes-dotnet/arm-template-generator-typescript">here</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Compositional Layout Basics]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/01/10/Compositional-Layout-Part1</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/01/10/Compositional-Layout-Part1</guid>
            <pubDate>Fri, 10 Jan 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Even though this year at WWDC Apple introduced the new SwiftUI framework, which will redefine the way we create our app UI's from iOS 13, there were as well presented some advances in <code>UIKit</code>, precisely, in the way we can build an <code>UICollectionView</code> through the newly added Compositional Layout. With this new type of layout, we can now easily create a custom <code>UICollectionView</code> that supports different layouts for each section and eliminates the need for subclassing <code>UICollectionViewLayout</code> for achieving similar behaviour.</p>
<p>This series will focus on the way we can take advantage of the Compositional Layout when implementing an <code>UICollectionView</code>.</p>
<h4>Notes:</h4>
<ul>
<li>Even though we will focus on implementing a Compositional Layout in an iOS application, you can use most of this code to achieve similar results as well on macOS or tvOS.</li>
</ul>
<h4>Prerequisites:</h4>
<p>You will need to use Xcode 11 for this tutorial and our starter project. As well download our <a href="https://github.com/nodes-ios/Compositional-Layout">start project</a> to follow along.</p>
<h2>Create a Simple Compositional Layout</h2>
<p>We will start this tutorial with the creation of a simple Compositional Layout, a list, that we will subsequently modify into a more advance layout.</p>
<p>So let's get started!</p>
<p>In our <code>UIViewController</code> add the following code in the <code>makeLayout</code> function:</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// 1</span>
<span class="hljs-keyword">let</span> itemSize <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSize</span>(widthDimension: .fractionalWidth(<span class="hljs-number">1</span>),
                                      heightDimension: .absolute(<span class="hljs-number">50</span>))

<span class="hljs-comment">// 2</span>
<span class="hljs-keyword">let</span> item <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutItem</span>(layoutSize: itemSize)

<span class="hljs-comment">// 3</span>
<span class="hljs-keyword">let</span> group <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutGroup</span>.horizontal(layoutSize: itemSize,
                                               subitems: [item])

<span class="hljs-comment">// 4</span>
<span class="hljs-keyword">let</span> section <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSection</span>(group: group)
section.interGroupSpacing <span class="hljs-operator">=</span> <span class="hljs-number">5</span>
section.contentInsets <span class="hljs-operator">=</span> <span class="hljs-type">NSDirectionalEdgeInsets</span>(top: <span class="hljs-number">0</span>,
                                                        leading: <span class="hljs-number">5</span>,
                                                        bottom: <span class="hljs-number">5</span>,
                                                        trailing: <span class="hljs-number">5</span>)

<span class="hljs-keyword">let</span> layout <span class="hljs-operator">=</span> <span class="hljs-type">UICollectionViewCompositionalLayout</span>(section: section)
<span class="hljs-keyword">return</span> layout
</code></pre>
<p>1 - To create a Compositional Layout we will first need to declare an instance of <code>NSCollectionLayoutSize</code>. This will represent in our case the size of the <code>UICollectionViewCell</code>.</p>
<p>We can initialise this by specifying the width/height via an <code>NSCollectionLayoutDimension</code> instance in several ways:</p>
<ul>
<li>absolute: by providing an absolute value we set the width/height of the cell to a fixed value</li>
<li>estimated: by providing an estimated value we set the width/height of the cell to an estimative value, which will depend on the content's instrict size</li>
<li>fractionalWidth: by providing a fractionalWidth we set the width/height of the cell to a value proportional with the parent's width</li>
<li>fractionalHeight: by providing a fractionalHeight we set the width/height of the cell to a value proportional with the parent's height</li>
</ul>
<p>2 - After creating a size, we need to create an <code>NSCollectionLayoutItem</code> to which we will assign it. The item together with its size represent the way our cell will be displayed in the layout.</p>
<p>3 - Now that we have our items aspect defined, we will need to group them. To do this we need to create an instance of <code>NSCollectionLayoutGroup</code>. Here we can define several types of groups:</p>
<ul>
<li>horizontal: will have a horizontal layout</li>
<li>vertical: will have a vertical layout</li>
<li>custom: allows you to create a group based on your needs</li>
</ul>
<p>When creating a group we will need to pass the size as well in the initialiser, together with the items for the group. For the moment we will assign the group the same size as provided for the item object.</p>
<p>4 - With all the components created, we can wrap the <code>NSCollectionLayoutGroup</code> in a section object (<code>NSCollectionLayoutSection</code>) which we will use to create our <code>UICollectionViewCompositionalLayout</code> instance.</p>
<p>Note: If you want to create a Compositional Layout on macOS, you will need to create an instance of <code>NSCollectionViewCompositionalLayout</code>.</p>
<p>Run your code and you should see the following layout:</p>
<p><img src="/assets/img/articles/2020-01-10-Compositional-Layout-Part1/1.webp" alt=""></p>
<h2>Create a Simple Compositional Layout with Different Section Layouts</h2>
<p>Now that we know how the Compositional Layout structure and the meaning of the newly introduced classes, we can put all of them together and create a custom layout with different types of layout for each section.</p>
<p>For this example we will build a dynamic layout with a list type section and a grid type section.</p>
<p>We will start by declaring <code>enum SectionLayoutKind: Int</code>, in our <code>UIViewController</code>, which will be in charge of specifying the layout type and as well the number of columns for each layout type.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">enum</span> <span class="hljs-title class_">SectionLayoutKind</span>: <span class="hljs-title class_">Int</span> {
    <span class="hljs-keyword">case</span> grid
    <span class="hljs-keyword">case</span> list

    <span class="hljs-keyword">func</span> <span class="hljs-title function_">columnCount</span>(<span class="hljs-params">for</span> <span class="hljs-params">width</span>: <span class="hljs-type">CGFloat</span>) -> <span class="hljs-type">Int</span> {
    	<span class="hljs-comment">// have column count defined by screen size to make use of whole screen spacing</span>
        <span class="hljs-keyword">let</span> wideScreen <span class="hljs-operator">=</span> width <span class="hljs-operator">>=</span> <span class="hljs-number">1000</span>

        <span class="hljs-keyword">switch</span> <span class="hljs-keyword">self</span> {
        <span class="hljs-keyword">case</span> .grid:
            <span class="hljs-keyword">return</span> wideScreen <span class="hljs-operator">?</span> <span class="hljs-number">10</span> : <span class="hljs-number">5</span>

        <span class="hljs-keyword">case</span> .list:
            <span class="hljs-keyword">return</span> wideScreen <span class="hljs-operator">?</span> <span class="hljs-number">2</span> : <span class="hljs-number">1</span>
        }
    }
}
</code></pre>
<p>Go ahead and replace the contents of <code>makeLayout</code> with the following code block:</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// 1</span>
<span class="hljs-keyword">let</span> layout <span class="hljs-operator">=</span> <span class="hljs-type">UICollectionViewCompositionalLayout</span> { (sectionIndex: <span class="hljs-type">Int</span>, layoutEnv: <span class="hljs-type">NSCollectionLayoutEnvironment</span>) -> <span class="hljs-type">NSCollectionLayoutSection</span>? <span class="hljs-keyword">in</span>
		<span class="hljs-comment">// 2</span>
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> sectionLayoutKind <span class="hljs-operator">=</span> <span class="hljs-type">SectionLayoutKind</span>(rawValue: sectionIndex) <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span> }

        <span class="hljs-keyword">let</span> itemSize: <span class="hljs-type">NSCollectionLayoutSize</span>
        <span class="hljs-keyword">let</span> groupSize: <span class="hljs-type">NSCollectionLayoutSize</span>

        <span class="hljs-keyword">switch</span> sectionLayoutKind {
        <span class="hljs-keyword">case</span> .grid:
            itemSize <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSize</span>(widthDimension: .fractionalWidth(<span class="hljs-number">0.25</span>),
                                              heightDimension: .fractionalHeight(<span class="hljs-number">1</span>))

            groupSize <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSize</span>(widthDimension: .fractionalWidth(<span class="hljs-number">1</span>),
                                               heightDimension: .fractionalWidth(<span class="hljs-number">0.25</span>))
        <span class="hljs-keyword">case</span> .list:
            itemSize <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSize</span>(widthDimension: .fractionalWidth(<span class="hljs-number">1</span>),
                                              heightDimension: .absolute(<span class="hljs-number">50</span>))

            groupSize <span class="hljs-operator">=</span> itemSize

        <span class="hljs-keyword">default</span>:
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
        }

        <span class="hljs-keyword">let</span> item <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutItem</span>(layoutSize: itemSize)

		<span class="hljs-comment">// 3</span>
        <span class="hljs-keyword">let</span> group <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutGroup</span>.horizontal(layoutSize: groupSize,
                                                       subitem: item,
                                                       count: sectionLayoutKind.columnCount(for: layoutEnv.container.effectiveContentSize.width))
        group.interItemSpacing <span class="hljs-operator">=</span> .fixed(<span class="hljs-number">10</span>)

        <span class="hljs-keyword">let</span> section <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSection</span>(group: group)
        section.interGroupSpacing <span class="hljs-operator">=</span> <span class="hljs-number">5</span>
        section.contentInsets <span class="hljs-operator">=</span> <span class="hljs-type">NSDirectionalEdgeInsets</span>(top: <span class="hljs-number">0</span>,
                                                        leading: <span class="hljs-number">5</span>,
                                                        bottom: <span class="hljs-number">5</span>,
                                                        trailing: <span class="hljs-number">5</span>)

        <span class="hljs-keyword">return</span> section
    }

<span class="hljs-keyword">return</span> layout
</code></pre>
<p>1 - To create a Compositional Layout with multiple types of layout, we need to switch the order presented in the above section and start by initialising the layout first.</p>
<p>2 - In the provided initialisation code block, based on the <code>sectionIndex</code>, we initialise an instance of <code>SectionLayoutKind</code>. This will help determine the <code>itemSize</code> and <code>groupSize</code> for the expected type of layout.</p>
<p>3 - Since we want to be able to provide spacing and insets for our layout, but we don't want to handle all the math ourselves, we can call the <code>NSCollectionLayoutGroup</code> initialiser which takes <code>count</code> as a parameter. In our case, this will represent the column count which is set in the <code>SectionLayoutKind</code> <code>func columnCount(for width: CGFloat) -> Int</code>. This will automatically calculate and adjust the size of the item and the group to fit our need by overriding the appropriate dimensions values provided in the <code>NSCollectionLayoutSize</code> initialiser.</p>
<p>Run your code and you should see the following layout:</p>
<p><img src="/assets/img/articles/2020-01-10-Compositional-Layout-Part1/2.webp" alt=""></p>
<h2>Final notes</h2>
<p>Compositional Layouts redefined the way we can build custom <code>UICollectionView</code>'s by allowing us to implement complex designs with ease.</p>
<p>To learn more about Compositional Layout's advanced features, be sure to check out <a href="/en2020-01-10-Compositional-Layout-Part2">Part 2</a> and <a href="/en2020-01-10-Compositional-Layout-Part3">Part 3</a> of these tutorial series.</p>
<p>Hope to see you next time!</p>
<h4>References:</h4>
<ul>
<li><a href="https://developer.apple.com/videos/play/wwdc2019/215/">WWDC 2019 - Advances in Collection View Layout</a></li>
<li><a href="https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/compositional_layout_objects">Compositional Layout Objects Docs</a></li>
<li><a href="/en2020-01-10-Compositional-Layout-Part2">Part 2</a></li>
<li><a href="/en2020-01-10-Compositional-Layout-Part3">Part 3</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Supplementary Items and Decorations]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/01/10/Compositional-Layout-Part2</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/01/10/Compositional-Layout-Part2</guid>
            <pubDate>Fri, 10 Jan 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><a href="/en2020-01-10-Compositional-Layout-Part1">The previous part of this series</a> covered the ground base for implementing a Compositional Layout.</p>
<p>Now that we are comfortable with the base implementation, this part of the series will focus on the Supplementary Items and Decorations that we can use in a Compositional Layout and the way to implement them.</p>
<h4>Prerequisites:</h4>
<p>This post contains specifics to describe Compositional Layout implementation. Be sure to download our <a href="https://github.com/nodes-ios/Compositional-Layout">starter project</a> to follow along.</p>
<h2>Suplementary Items</h2>
<p>Supplementary Items implementation was redefined, now allowing us to anchor them to an item or a group, thus <code>NSCollectionLayoutSupplementaryItem</code>'s became more versatile and their implementation became simpler.
<code>NSCollectionLayoutSupplementaryItem</code>'s position is now defined relative to its host geometry by making use of <code>NSCollectionLayoutAnchor</code>'s.</p>
<h3>Creating a Badge</h3>
<p>Since a <code>NSCollectionLayoutSupplementaryItem</code> became more versatile and we can achor it to an item, creation of badges for each item in the collection view becomes possible.</p>
<p>To showcase the implementation of badges, we will reuse the grid layout we have created in <a href="/en2020-01-10-Compositional-Layout-Part1">part 1</a> of this series, but first, we will need to create the badge item. To do so we are going to create a new function <code>func makeBadge() -> NSCollectionLayoutSupplementaryItem</code> that will create for us the supplementary item we will add to our item.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">makeBadge</span>() -> <span class="hljs-type">NSCollectionLayoutSupplementaryItem</span> {
        <span class="hljs-comment">// 1</span>
        <span class="hljs-keyword">let</span> anchor <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutAnchor</span>(edges: [.top,
                                                      .trailing],
                                              fractionalOffset: <span class="hljs-type">CGPoint</span>(x: <span class="hljs-number">0.5</span>, y: <span class="hljs-operator">-</span><span class="hljs-number">0.5</span>))
        <span class="hljs-comment">// 2</span>
        <span class="hljs-keyword">let</span> size <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSize</span>(widthDimension: .absolute(<span class="hljs-number">15</span>),
                                          heightDimension: .absolute(<span class="hljs-number">15</span>))
        <span class="hljs-comment">// 3</span>
        <span class="hljs-keyword">let</span> badge <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSupplementaryItem</span>(layoutSize: size,
                                                        elementKind: <span class="hljs-string">"badge"</span>,
                                                        containerAnchor: anchor)
        <span class="hljs-keyword">return</span> badge
    }
</code></pre>
<p>1 - Here we define an <code>NSCollectionLayoutAnchor</code> for our badge. By specifying <code>[.top, .trailing]</code> we position our badge in the top right corner of the parent, while the <code>fractionalOffset</code> positions our badge's center in the the corner. A <code>fractionalOffset</code> of <code>.zero</code> would possition the whole badge inside the parent.</p>
<p>2 - As encountered before, we need to create a size for our item, so here we specify that we our badge should have a fixed value for width and height.</p>
<p>3 - We initialise our <code>NSCollectionLayoutSupplementaryItem</code> by assigning it the size, the anchor and an <code>elementKind</code>, based upon which we will identify our badge in <code>collectionView:viewForSupplementaryElementOfKind:atIndexPath:</code>.</p>
<p>Now that we have our badge ready, we just need to assign it to our items. To do so just replace <code>NSCollectionLayoutItem</code> initialiser in our <code>func makeLayout</code> with the following:</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// 1</span>
<span class="hljs-keyword">let</span> item <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutItem</span>(layoutSize: itemSize, supplementaryItems: [makeBadge()])
</code></pre>
<p>1 - Here we create the item for our layout and assign it the <code>NSCollectionLayoutSupplementaryItem</code> badge.</p>
<p>Run your code and you should see the following result:</p>
<p><img src="/assets/img/articles/2020-01-10-Compositional-Layout-Part2/1.webp" alt=""></p>
<h3>Creating a Header</h3>
<p>Previously, in our collection views, we could have encountered difficulties when trying to create and position header views. For example in a <code>UICollectionViewFlowLayout</code> the header would follow the same direction as the layout, making it impossible to use a supplementary view, if we would have liked our header to be above the content in a horizontal scrollable collection view. This is no longer the case because of the newly introduced <code>NSCollectionLayoutAnchor</code>, which allows us to possition our header anywhere in the parent.</p>
<p>For our header we will create an <code>NSCollectionLayoutBoundarySupplementaryItem</code>, that, similarly to a badge, we can anchor the way it better fits our needs.</p>
<p>So let's go ahead and create a function that will make our header.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">makeHeader</span>() -> <span class="hljs-type">NSCollectionLayoutBoundarySupplementaryItem</span> {
        <span class="hljs-comment">// 1</span>
        <span class="hljs-keyword">let</span> size <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSize</span>(widthDimension: .fractionalWidth(<span class="hljs-number">1</span>),
                                          heightDimension: .estimated(<span class="hljs-number">50</span>))
        <span class="hljs-comment">// 2</span>
        <span class="hljs-keyword">let</span> header <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutBoundarySupplementaryItem</span>(layoutSize: size,
                                                                 elementKind: <span class="hljs-string">"header"</span>,
                                                                 alignment: .top)
        <span class="hljs-keyword">return</span> header
	}
</code></pre>
<p>1 - Assign a size for our header. This will have the parent's width and an estimated height of 50, that will allow for font scaling if needed.</p>
<p>2 - We initialise our <code>NSCollectionLayoutBoundarySupplementaryItem</code> by assigning it the size, the anchor and an <code>elementKind</code>, based upon which we will identify our badge in <code>collectionView:viewForSupplementaryElementOfKind:atIndexPath:</code>.</p>
<p>Now that we have our header, in our <code>func makeLayout</code> we need to pin it to our section. To do so add the following after the section initialisation code.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// 1</span>
section.boundarySupplementaryItems <span class="hljs-operator">=</span> [makeHeader()]
</code></pre>
<p>1 - We assign the header as one of our section's <code>boundarySupplementaryItems</code>.</p>
<p>Run your code and you should see the following result:</p>
<p><img src="/assets/img/articles/2020-01-10-Compositional-Layout-Part2/2.webp" alt=""></p>
<h2>Decoration Items</h2>
<p>A new design feature we started seeing a lot in iOS 13 is the new "Card Design". When implementing this type of design it could come in handy if we can set a specific background to a section. This is now possible because of the introduction of <code>NSCollectionLayoutDecorationItem</code>.</p>
<p>Let's start by creating a function that will make our background decoration item.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">makeBackgroundDecorationItem</span>() -> <span class="hljs-type">NSCollectionLayoutDecorationItem</span> {
        <span class="hljs-keyword">let</span> backgroundItem <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutDecorationItem</span>.background(elementKind: <span class="hljs-string">"background"</span>)
        <span class="hljs-keyword">return</span> backgroundItem
    }
</code></pre>
<p>Now to be able to assign our <code>NSCollectionLayoutDecorationItem</code> to our layout we will need to add the following code in our <code>func makeLayout()</code>.</p>
<pre><code class="hljs language-swift"><span class="hljs-operator">...</span>
	<span class="hljs-comment">// 1</span>
    section.decorationItems <span class="hljs-operator">=</span> [makeBackground()]
<span class="hljs-operator">...</span>
	<span class="hljs-comment">// 2</span>
    layout.register(<span class="hljs-type">BackgroundDecorationView</span>.<span class="hljs-keyword">self</span>,
                    forDecorationViewOfKind: <span class="hljs-string">"background"</span>)
<span class="hljs-operator">...</span>
</code></pre>
<p>1 - Assign our decoration item to the section.</p>
<p>2 - Register the <code>UICollectionReusableView</code> we will use for our background item with the layout. This will be dequed automatically.</p>
<p>Run your code and you should see the following result:</p>
<p><img src="/assets/img/articles/2020-01-10-Compositional-Layout-Part2/3.webp" alt=""></p>
<h2>Final notes</h2>
<p>Compositional Layouts redefined the way we can build and design our layout by making it easier to implement suplemenraty items and introducing the concept of decoration items.</p>
<p>To learn more about Compositional Layout's advanced features be sure to check out <a href="/en2020-01-10-Compositional-Layout-Part3">part 3</a> of these tutorial series.</p>
<p>Hope to see you next time!</p>
<h4>References:</h4>
<ul>
<li><a href="https://developer.apple.com/videos/play/wwdc2019/215/">WWDC 2019 - Advances in Collection View Layout</a></li>
<li><a href="https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/compositional_layout_objects">Compositional Layout Objects Docs</a></li>
<li><a href="/en2020-01-10-Compositional-Layout-Part1">Part 1</a></li>
<li><a href="/en2020-01-10-Compositional-Layout-Part3">Part 3</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building the App Store using Compositional Layout]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2020/01/10/Compositional-Layout-Part3</link>
            <guid>https://engineering.monstar-lab.com/en/post/2020/01/10/Compositional-Layout-Part3</guid>
            <pubDate>Fri, 10 Jan 2020 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>In the first 2 parts of this series we learned the basics for implementing a [Compositional Layout]({{ site.baseurl }}{% post_url 2020-01-10-Compositional-Layout-Part1 %}) and how to add <a href="/en2020-01-10-Compositional-Layout-Part2">Supplementary Items and Decorations</a>.</p>
<p>With this knowledge in hand we will now try and recreate the "Apps" screen of the Apple App Store, and see how we can use the first 2 parts to build a UI everyone is familiar with.</p>
<h4>Prerequisites:</h4>
<p>Download the <a href="https://github.com/nodes-ios/Compositional-Layout-AppStore/tree/starter">Starter project</a> from GitHub to code along, you can also download the completed project from the <a href="https://github.com/nodes-ios/Compositional-Layout-AppStore/tree/master">master branch</a> on the same repo.</p>
<p>It is also recommended to have read <a href="/en2020-01-10-Compositional-Layout-Part1">part 1</a> and <a href="/en2020-01-10-Compositional-Layout-Part2">part 2</a>.</p>
<p>Let's have a look at the starter project.</p>
<h2>The Starter Project</h2>
<p>Here I will quickly go over what has been setup in the starter project and what you should change or do differently when working on an actual project.</p>
<h3>Models</h3>
<p>The models (<code>App</code>, <code>Category</code> and <code>Section</code>) in this project are pretty straight forward and don't need much explaination.</p>
<p>All of them include the information needed to display it on the screen. One note is that you probably want to change how images are handled, my suggestion is to load them from a url and have a caching system in place.</p>
<p>Then we have one enum, <code>SectionType</code> which defines what styles of sections can be shown in the <code>UICollectionView</code>. These are the styles we are going to make during this tutorial.</p>
<h3>Cells and Views</h3>
<p>I have setup a <code>SectionHeader</code> view and 4 <code>UICollectionViewCell</code>s that resemble the cells used in the App Store. I'm sure Apple has some nice adaptable cells to handle a few of these cases in 1 class but that is out of scope for this tutorial.
Of course if you prefer using Storyboard or Xib files go ahead and use that. It should not have any effect on the Layout we will cover in this tutorial.
I also added an extension on the <code>UICollectionReusableView</code> class to add the <code>identifier</code> property to the Supplementary views and the cells.</p>
<h3>AppsPresenter</h3>
<p>The AppsPresenter handles all the data and the ViewController will ask it for the data and info it needs to construct the <code>UICollectionView</code>.<br>
This is inspired by the VIPER/CLEAN architecture we use at Nodes but is an (over)simplified version of it. Feel free to use ViewModels, put it in the ViewController or whatever you are comfortable with.</p>
<p>You will need to go in the AppsPresenter to uncomment some of the data but you will not need to change anything else in this file.</p>
<p>The data model is definitely not optimal but hey it's a tutorial for the UI layout so let's ignore that for now. I will point to some other resources at the end of the post for more info on that.</p>
<h3>ViewController</h3>
<p>Then the last file is the <code>ViewController.swift</code> file where we will be working during the tutorial, I have already wired up the presenter and the UICollectionView so we can focus on the Layout part.</p>
<h2>Let's get started</h2>
<p>To make sure everything is setup correctly download the starter project and run the project. You should see an empty screen with the "Apps" title.
<img src="/assets/img/articles/2020-01-10-Compositional-Layout-Part3/start.webp" alt=""></p>
<h2>The featured section</h2>
<p>Let's get started with the first section in our App Store, the featured apps you usually find as the first section in the store. I already made the cells so let's have a look at the layout it self.</p>
<h3>UICollectionViewCompositionalLayout</h3>
<p>On line 29 of the <code>ViewController.swift</code> file you will see the collectionView being initialised with a collectionViewLayout that is created by the <code>makeLayout()</code> function, in this function we will tell the collectionView what layout to use for what section and set some other section level configurations.</p>
<p>Now we can finally start coding, remove the only line in the <code>makeLayout()</code> function so we have an empty function. Then add the following code (always write the code your self in these kinds of tutorials, you will understand the code much better that way).</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// 1</span>
<span class="hljs-keyword">let</span> layout <span class="hljs-operator">=</span> <span class="hljs-type">UICollectionViewCompositionalLayout</span> { (sectionIndex: <span class="hljs-type">Int</span>, layoutEnv: <span class="hljs-type">NSCollectionLayoutEnvironment</span>) -> <span class="hljs-type">NSCollectionLayoutSection</span>? <span class="hljs-keyword">in</span>
	 <span class="hljs-comment">// 2</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>.createSingleListSection()
}

<span class="hljs-comment">// 3</span>
<span class="hljs-keyword">let</span> config <span class="hljs-operator">=</span> <span class="hljs-type">UICollectionViewCompositionalLayoutConfiguration</span>()
config.interSectionSpacing <span class="hljs-operator">=</span> <span class="hljs-number">20</span>
layout.configuration <span class="hljs-operator">=</span> config

<span class="hljs-keyword">return</span> layout
</code></pre>
<p>1 - Here we are creating a new <code>UICollectionViewCompositionalLayout</code> using its initialiser. This initialiser needs a closure which provides us with the <code>sectionIndex</code> and the <code>layoutEnvironment</code>, this closure is called everytime the <code>layoutEnvironment</code> changes (e.g.: rotating from portrait to landscape).</p>
<p>2 - In this closure we check what section we are working with and return the appropriate layout. At this point in the tutorial we are only working with one section style so leave the closure like this for now, we will get back to this closure to add the other section styles but for now this is enough. In the next step we will define the <code>createSingleListSection()</code> method so don't worry about the error.</p>
<p>3 - Then we create a <code>UICollectionViewCompositionalLayoutConfiguration</code> and set its <code>interSectionSpacing</code>, this defines how much spacing the layout will provide between sections, then we assign it to the layout we created above.</p>
<p>Now that the collectionView knows what layout to use for its only section, we need to define that layout.</p>
<h3>Single List Section</h3>
<p>As you might have noticed we have not defined the <code>createSingleListSection()</code> function so lets get right to it.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// 1</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">createSingleListSection</span>() -> <span class="hljs-type">NSCollectionLayoutSection</span> {
	<span class="hljs-comment">// 2</span>
	<span class="hljs-keyword">let</span> itemSize <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSize</span>(widthDimension: .fractionalWidth(<span class="hljs-number">1</span>),
	                                      heightDimension: .fractionalHeight(<span class="hljs-number">1</span>))
	<span class="hljs-comment">// 3</span>
	<span class="hljs-keyword">let</span> layoutItem <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutItem</span>(layoutSize: itemSize)
	layoutItem.contentInsets <span class="hljs-operator">=</span> <span class="hljs-type">NSDirectionalEdgeInsets</span>(top: <span class="hljs-number">0</span>, leading: <span class="hljs-number">5</span>, bottom: <span class="hljs-number">0</span>, trailing: <span class="hljs-number">5</span>)

	<span class="hljs-comment">// 4</span>
	<span class="hljs-keyword">let</span> layoutGroupSize <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSize</span>(widthDimension: .fractionalWidth(<span class="hljs-number">0.95</span>),
	                                             heightDimension: .estimated(<span class="hljs-number">250</span>))
	<span class="hljs-comment">// 5</span>
	<span class="hljs-keyword">let</span> layoutGroup <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutGroup</span>.horizontal(layoutSize: layoutGroupSize,
	                                                     subitems: [layoutItem])

	<span class="hljs-comment">// 6</span>
	<span class="hljs-keyword">let</span> layoutSection <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSection</span>(group: layoutGroup)

	<span class="hljs-comment">// 7</span>
	layoutSection.orthogonalScrollingBehavior <span class="hljs-operator">=</span> .groupPagingCentered

	<span class="hljs-keyword">return</span> layoutSection
}
</code></pre>
<p>If you have read part 1 you should at least be a bit familiar with the classes and what we are defining here, but let's go over them none the less to be sure we know what's going on. (If you need a refresher have a look at <a href="/en2020-01-10-Compositional-Layout-Part1">part 1</a> of the series).</p>
<p>1 - We are defining the <code>createSingleListSection</code> function that will return a <code>NSCollectionLayoutSection</code>. We will create similar functions for the other section styles we want to use.</p>
<p>2 - Here we define the item size using the <code>NSCollectionLayoutSize</code> class. We use <code>.fractionalWidth(1)</code> and <code>.fractionalHeight(1)</code> for the width and the height as we want only one item to be shown at the time so 1 item should use 100% of the width and the height, for a list of options you can use here check <a href="/en2020-01-10-Compositional-Layout-Part1">part 1</a>.</p>
<p>3 - The next step is to use the <code>itemSize</code> we just defined to create the actual <code>NSCollectionLayoutItem</code> by passing in the itemSize to its initialiser. After that we add content insets to the leading and trailing edges to add some spacing between the items.</p>
<p>4 - Here we are defining the group size using the same class we used when defining the item size. This time we tell the LayoutSize that we want the group to take up 95% of the screen by passing in <code>0.95</code> in <code>fractionalWidth</code>. We do this to show a little bit of the previous and next items so the user is incentivised to scroll further, then we estimate that the height of the group will be 250, you can play around with this number to see what fits best, I quite like 250 so I'll leave it at that.</p>
<p>5 - Now that we have the group size, and the layout item we can create layout group. As you can see the initialiser requires an array of subitems, because we are only showing 1 type of item we just pass in the layoutItem we created. If you want to show differently sized items add more subitems, for this tutorial we will keep it at this.</p>
<p>6 - Then the same reasoning as step 5, we now have a layout group so we can make a layout section. To do that use <code>NSCollectionLayoutSection</code> initialiser and pass in the layoutGroup.<br>
If you run the app now (you will need to register the <code>Featured</code> cell so check the final step) you will see a vertical list of the featured apps, which is not what we want so let's see how step 7 solves this problem.</p>
<p>7 - To make the section scroll horizontally we will use 1 line of code, by setting the <code>orthogonalScrollingBehavior</code> to <code>.groupPagingCentered</code>.
OrthogonalScrolling means that this section should scroll at right angles to our collection view, meaning that if our collection view scrolls vertically this section will scroll horizontally.<br>
(Check out Paul Hudsons video on <code>UICollectionViewCompositionalLayout</code> (linked below) around minute 24 he explains the different behaviours you can use here).</p>
<p>Final step: in the <code>setupCollectionView()</code> function below the constraints add the following line to register our <code>FeaturedCell</code>.</p>
<pre><code class="hljs language-swift">collectionView.register(<span class="hljs-type">FeaturedCell</span>.<span class="hljs-keyword">self</span>,
                        forCellWithReuseIdentifier: <span class="hljs-type">FeaturedCell</span>.identifier)
</code></pre>
<p>Now run the app and see the featured section in all its glory!
<img src="/assets/img/articles/2020-01-10-Compositional-Layout-Part3/1.webp" alt=""></p>
<h2>This weeks favorites</h2>
<p>In this part we will add our second section style but before we do that we need to modify the <code>makeLayout()</code> function so it can handle multiple section styles.</p>
<h3>Multiple section styles</h3>
<p>In the closure where we currently only have the <code>return self.createSingleListSection()</code> remove that line and replace it with the code below.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// 1</span>
<span class="hljs-keyword">switch</span> <span class="hljs-keyword">self</span>.presenter.sectionType(for: sectionIndex) {
<span class="hljs-keyword">case</span> .singleList:   <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>.createSingleListSection()
<span class="hljs-comment">// 2</span>
<span class="hljs-keyword">case</span> .doubleList:   <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>.createDoubleListSection()
<span class="hljs-comment">// 3</span>
<span class="hljs-keyword">default</span>: <span class="hljs-built_in">fatalError</span>()
}
</code></pre>
<p>1 - In the presenter I added a function that returns what <code>SectionStyle</code> the given section index should use, with this value we can make a switch statement where we can return different layout sections based on that style.</p>
<p>2 - The line above this comment is basically the same as before so I'll leave it as that, but we also added our upcoming style called <code>DoubleList</code>, we will define the <code>self.createDoubleListSection()</code> function next.</p>
<p>3 - Because there are already other styles defined in the <code>SectionType</code> enum we need a default case to handle those for now. We will fill out the switch statement as we go.</p>
<h3>Section headers</h3>
<p>Before we continue with the double list section we need to add some code so we can add section headers. The featured section didn't need it but all the following section do need it so let's do it first.</p>
<p>Once again I have setup the class for our section headers, we just need to tell the <code>UICollectionView</code> how to show them, to do so add the following function to the <code>ViewController</code> class.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">createSectionHeader</span>() -> <span class="hljs-type">NSCollectionLayoutBoundarySupplementaryItem</span> {
    <span class="hljs-comment">// 1</span>
    <span class="hljs-keyword">let</span> layoutSectionHeaderSize <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSize</span>(widthDimension: .fractionalWidth(<span class="hljs-number">0.95</span>),
                                                         heightDimension: .estimated(<span class="hljs-number">80</span>))

    <span class="hljs-comment">// 2</span>
    <span class="hljs-keyword">let</span> layoutSectionHeader <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutBoundarySupplementaryItem</span>(layoutSize: layoutSectionHeaderSize,
                                                                          elementKind: <span class="hljs-type">UICollectionView</span>.elementKindSectionHeader,
                                                                          alignment: .top)
    <span class="hljs-keyword">return</span> layoutSectionHeader
}
</code></pre>
<p>1 - Here we see our friend <code>NSCollectionLayoutSize</code> again, not much has changed we are just telling it that we think the sections headers have a height of <code>80</code> and that it should use 95% of the given size.</p>
<p>2 - Now that we have the section header size we will create the <code>NSCollectionLayoutBoundarySupplementaryItem</code> by passing in:</p>
<ul>
<li>the section header size,</li>
<li>the kind of supplementaryitem it is, in this case it is a header.</li>
<li>and where to put it, in this case at the top of the section.</li>
</ul>
<p>And that is all that is needed for the section headers, now let's get to the double list section.</p>
<h3>Double List Section</h3>
<p>The <code>createDoubleListSection()</code> function is quite similar to the <code>createSingleListSection()</code> so I will go into less depth and just explain any differences.</p>
<p>Add the following function to the <code>ViewController</code> class.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">createDoubleListSection</span>() -> <span class="hljs-type">NSCollectionLayoutSection</span> {
    <span class="hljs-comment">// 1</span>
    <span class="hljs-keyword">let</span> itemSize <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSize</span>(widthDimension: .fractionalWidth(<span class="hljs-number">1</span>),
                                          heightDimension: .fractionalHeight(<span class="hljs-number">0.5</span>))

    <span class="hljs-keyword">let</span> layoutItem <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutItem</span>(layoutSize: itemSize)
    layoutItem.contentInsets <span class="hljs-operator">=</span> <span class="hljs-type">NSDirectionalEdgeInsets</span>(top: <span class="hljs-number">0</span>, leading: <span class="hljs-number">5</span>, bottom: <span class="hljs-number">0</span>, trailing: <span class="hljs-number">5</span>)

    <span class="hljs-keyword">let</span> layoutGroupSize <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSize</span>(widthDimension: .fractionalWidth(<span class="hljs-number">0.95</span>),
                                                 heightDimension: .estimated(<span class="hljs-number">165</span>))

    <span class="hljs-comment">// 2</span>
    <span class="hljs-keyword">let</span> layoutGroup <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutGroup</span>.vertical(layoutSize: layoutGroupSize,
                                                       subitem: layoutItem,
                                                       count: <span class="hljs-number">2</span>)
    <span class="hljs-comment">// 3</span>
    layoutGroup.interItemSpacing <span class="hljs-operator">=</span> .fixed(<span class="hljs-number">8</span>)

    <span class="hljs-keyword">let</span> layoutSection <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSection</span>(group: layoutGroup)
    layoutSection.orthogonalScrollingBehavior <span class="hljs-operator">=</span> .groupPagingCentered

    <span class="hljs-comment">// 4</span>
    <span class="hljs-keyword">let</span> layoutSectionHeader <span class="hljs-operator">=</span> createSectionHeader()
    layoutSection.boundarySupplementaryItems <span class="hljs-operator">=</span> [layoutSectionHeader]

    <span class="hljs-keyword">return</span> layoutSection
}
</code></pre>
<p>1 - The first difference is that we need to change the height for each item, as we want to show 2 items in each group we need to set it to 50% of the group size.</p>
<p>2 - Then when we create the layoutGroup we need to make some changes. We want the group to layout its item vertically so we need to use the <code>.vertical</code> initialiser. We pass in the size and the subitem like we did before, and as we always want 2 items per group we set the count to 2.</p>
<p>3 - Now that we have multiple items in a group we need to set the <code>interItemSpacing</code> on the layoutGroup. We know that we always want 8 spacing between the item so we use the <code>.fixed()</code> option.<br>
If you want the system to calculate how much spacing there is left and use that, use the <code>.flexible()</code> option and pass in an estimate.</p>
<p>4 - Now we come to a concept we saw in <a href="/en2020-01-10-Compositional-Layout-Part2">part 2</a>, and the reason why we implemented the <code>createSectionHeader()</code> function first.<br>
Here we have the <code>createSectionHeader()</code> function make a section header for us and assign it to the <code>boundarySupplementaryItems</code> property of the layoutSection.</p>
<p>Like last time we need to register the cell class the new Double list is going to use and the section header, so let's add the following lines in the <code>setupCollectionView()</code> function.</p>
<pre><code class="hljs language-swift">collectionView.register(<span class="hljs-type">SectionHeader</span>.<span class="hljs-keyword">self</span>,
                        forSupplementaryViewOfKind: <span class="hljs-type">UICollectionView</span>.elementKindSectionHeader,
                        withReuseIdentifier: <span class="hljs-type">SectionHeader</span>.identifier)
collectionView.register(<span class="hljs-type">MediumAppCell</span>.<span class="hljs-keyword">self</span>,
                        forCellWithReuseIdentifier: <span class="hljs-type">MediumAppCell</span>.identifier)
</code></pre>
<p>If you run the app now, you will just see the Featured section because we still have to add to our dataSource. Go to the <code>AppsPresenter.swift</code> file and uncomment everything from below the "Second section" comment to the "Third section" comment.</p>
<p>Run the app now and you will see our "This weeks favorites" section.
<img src="/assets/img/articles/2020-01-10-Compositional-Layout-Part3/2.webp" alt=""></p>
<h2>Learn something</h2>
<p>Now that we added the logic for handling multiple section styles and added the <code>createSectionHeader()</code> function, the other sections should be quite easy.</p>
<h3>Add a new style</h3>
<p>Extend the switch in the <code>makeLayout()</code> function so it looks like this.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">switch</span> <span class="hljs-keyword">self</span>.presenter.sectionType(for: sectionIndex) {
<span class="hljs-keyword">case</span> .singleList:   <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>.createSingleListSection()
<span class="hljs-keyword">case</span> .doubleList:   <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>.createDoubleListSection()
<span class="hljs-keyword">case</span> .tripleList:   <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>.createTripleListSection() <span class="hljs-comment">// new</span>
<span class="hljs-keyword">default</span>: <span class="hljs-built_in">fatalError</span>()
}
</code></pre>
<h3>Triple List Section</h3>
<p>Once again this function will be very similar to the single and double list variants so I will go over the differences between them.<br>
Add the following function to the <code>ViewController</code> class.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">createTripleListSection</span>() -> <span class="hljs-type">NSCollectionLayoutSection</span> {
	<span class="hljs-comment">// 1</span>
    <span class="hljs-keyword">let</span> itemSize <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSize</span>(widthDimension: .fractionalWidth(<span class="hljs-number">1</span>),
                                          heightDimension: .fractionalHeight(<span class="hljs-number">0.33</span>))
    <span class="hljs-keyword">let</span> layoutItem <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutItem</span>(layoutSize: itemSize)
    layoutItem.contentInsets <span class="hljs-operator">=</span> <span class="hljs-type">NSDirectionalEdgeInsets</span>(top: <span class="hljs-number">0</span>, leading: <span class="hljs-number">5</span>, bottom: <span class="hljs-number">0</span>, trailing: <span class="hljs-number">5</span>)

    <span class="hljs-keyword">let</span> layoutGroupSize <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSize</span>(widthDimension: .fractionalWidth(<span class="hljs-number">0.95</span>),
                                                 heightDimension: .estimated(<span class="hljs-number">165</span>))
    <span class="hljs-comment">// 2</span>
    <span class="hljs-keyword">let</span> layoutGroup <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutGroup</span>.vertical(layoutSize: layoutGroupSize,
                                                       subitem: layoutItem,
                                                       count: <span class="hljs-number">3</span>)
    layoutGroup.interItemSpacing <span class="hljs-operator">=</span> .fixed(<span class="hljs-number">8</span>)

    <span class="hljs-keyword">let</span> layoutSection <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSection</span>(group: layoutGroup)
    layoutSection.orthogonalScrollingBehavior <span class="hljs-operator">=</span> .groupPagingCentered

    <span class="hljs-keyword">let</span> layoutSectionHeader <span class="hljs-operator">=</span> createSectionHeader()
    layoutSection.boundarySupplementaryItems <span class="hljs-operator">=</span> [layoutSectionHeader]

    <span class="hljs-keyword">return</span> layoutSection
}
</code></pre>
<p>As you can see this one is almost identical except for 2 places where some numbers have changed.</p>
<p>1 - The first one is the fraction of the height that changed, instead of half we only want to use a third of the height for each item so was pass in 33% or <code>0.33</code></p>
<p>2 - This one is very obvious, because we want 3 item in the group instead of 2, we change the count from 2 to 3.</p>
<p><em>(note: As I already mentioned a few times there is so little difference i made a more generic method where you give the groupCount and the estimated height and the rest is handled. That will be in the bonus section of this post)</em></p>
<p>Then the last 2 steps just like we did in the other sections.</p>
<ul>
<li>Register the <code>SmallAppCell</code></li>
</ul>
<pre><code class="hljs language-swift">collectionView.register(<span class="hljs-type">SmallAppCell</span>.<span class="hljs-keyword">self</span>,
                        forCellWithReuseIdentifier: <span class="hljs-type">SmallAppCell</span>.identifier)
</code></pre>
<ul>
<li>Go to the <code>AppsPresenter.swift</code> and uncomment from the "Third section" comment to the "Forth section" comment</li>
</ul>
<p>Run your app and you should see the "Learn something" section with 3 items per group.
<img src="/assets/img/articles/2020-01-10-Compositional-Layout-Part3/3.webp" alt=""></p>
<h2>Top Categories</h2>
<p>Here we are, the last section we will cover in this post. We will switch from apps to categories and make a simple list of the top categories of the last month.</p>
<h2>Another new style</h2>
<p>Extend the switch in the <code>makeLayout()</code> function so it looks like this.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">switch</span> <span class="hljs-keyword">self</span>.presenter.sectionType(for: sectionIndex) {
<span class="hljs-keyword">case</span> .singleList:   <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>.createSingleListSection()
<span class="hljs-keyword">case</span> .doubleList:   <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>.createDoubleListSection()
<span class="hljs-keyword">case</span> .tripleList:   <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>.createTripleListSection()
<span class="hljs-keyword">case</span> .categoryList: <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>.createCategoryListSection(for: <span class="hljs-keyword">self</span>.presenter.numberOfItems(for: sectionIndex)) <span class="hljs-comment">// new</span>
}
</code></pre>
<p>We removed the default case as we are handling all the defined styles.
The <code>createCategoryListSection</code> needs an amount of items so we ask the presenter how many need to be shown.</p>
<h2>Category List Section</h2>
<p>A few more changes in this <code>createCategoryListSection(for amount: Int)</code> than the previous ones, so let's have a look.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">createCategoryListSection</span>(<span class="hljs-params">for</span> <span class="hljs-params">amount</span>: <span class="hljs-type">Int</span>) -> <span class="hljs-type">NSCollectionLayoutSection</span> {
    <span class="hljs-comment">// 1</span>
    <span class="hljs-keyword">let</span> itemSize <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSize</span>(widthDimension: .fractionalWidth(<span class="hljs-number">1</span>),
                                          heightDimension: .fractionalHeight(<span class="hljs-type">CGFloat</span>(<span class="hljs-number">1</span><span class="hljs-operator">/</span>amount)))
    <span class="hljs-keyword">let</span> layoutItem <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutItem</span>(layoutSize: itemSize)

    <span class="hljs-comment">// 2</span>
    layoutItem.contentInsets <span class="hljs-operator">=</span> <span class="hljs-type">NSDirectionalEdgeInsets</span>(top: <span class="hljs-number">0</span>, leading: <span class="hljs-number">20</span>, bottom: <span class="hljs-number">0</span>, trailing: <span class="hljs-number">5</span>)

    <span class="hljs-comment">// 3</span>
    <span class="hljs-keyword">let</span> layoutGroupSize <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSize</span>(widthDimension: .fractionalWidth(<span class="hljs-number">0.95</span>),
                                                 heightDimension: .estimated(<span class="hljs-type">CGFloat</span>(<span class="hljs-number">40</span> <span class="hljs-operator">*</span> amount)))
    <span class="hljs-comment">// 4</span>
    <span class="hljs-keyword">let</span> layoutGroup <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutGroup</span>.vertical(layoutSize: layoutGroupSize,
                                                       subitem: layoutItem,
                                                       count: amount)
    layoutGroup.interItemSpacing <span class="hljs-operator">=</span> .fixed(<span class="hljs-number">8</span>)

    <span class="hljs-keyword">let</span> layoutSectionHeader <span class="hljs-operator">=</span> createSectionHeader()

    <span class="hljs-keyword">let</span> layoutSection <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSection</span>(group: layoutGroup)
    layoutSection.boundarySupplementaryItems <span class="hljs-operator">=</span> [layoutSectionHeader]

    <span class="hljs-keyword">return</span> layoutSection
}
</code></pre>
<p>1 - Here we calculate the item height because we want to decide how many items are shown without the section being to small or to large.</p>
<p>2 - Because this section will not scroll horizontal we will not use the <code>orthogonalScrollingBehavior</code> this means we need to compensate for the spacing that behaviour provided by using 20 instead of 5 as the leading content inset.</p>
<p>3 - Once again here we need to calculate the estimated height for the group as we are showing all cells in 1 group. We estimated that 1 item should have a height of 40 so we multiply 40 by the given amount of items.</p>
<p>4 - Lastly we need to set the amount as the count of the layout group, because this function can handle lists of different lenghts.</p>
<p>Then the last 2 steps just like we did in the other sections.</p>
<ul>
<li>Register the <code>SmallCategoryCell</code></li>
</ul>
<pre><code class="hljs language-swift">collectionView.register(<span class="hljs-type">SmallCategoryCell</span>.<span class="hljs-keyword">self</span>,
                         forCellWithReuseIdentifier: <span class="hljs-type">SmallCategoryCell</span>.identifier)
</code></pre>
<ul>
<li>Go to the <code>AppsPresenter.swift</code> and uncomment from the "Fourth section" comment to the end of the initialiser</li>
</ul>
<p>Run your app and you should see the "Top Categories" section with a list of 5 categories.<br>
(If you add or remove some categories in the <code>AppsPresenter</code> you will see how it automatically adjusts the height of the section.<br>
<img src="/assets/img/articles/2020-01-10-Compositional-Layout-Part3/4.webp" alt=""></p>
<h2>Conclusion</h2>
<p>And there we have it, a recreation of the Apple App Store "Apps" screen.
The <code>UICollectionViewCompositionalLayout</code> has made it so much easier to construct complex collection views like this one, and it allows more freedom in designing your apps layout.<br>
We at Nodes are looking forward to see what our designers can come up with now that they can get more creative with their list/grid designs.</p>
<p>Thank you for reading!</p>
<h2>Credits</h2>
<p>The main inspiration for this part was <strong>Paul Hudson</strong>'s <a href="https://www.youtube.com/watch?v=SR7DtcT61tA">video</a> on UICollectionViewCompositionalLayout.
I highly recommend watching this video as it uses the new <code>UICollectionViewDiffableDataSource</code> as the collection view data source and goes more in depth on certain topics.<br>
This project is smilar to his although I tried to make it a bit more beginner friendly and try to cover some other topics.</p>
<p>This last part was written by Bob De Kort, taking over from Andrei Hogea who wrote part 1 and 2 (See below for author information). Thanks to Andrei for starting the series and giving me a good base to work from.</p>
<h3>References</h3>
<ul>
<li><a href="https://developer.apple.com/videos/play/wwdc2019/215/">WWDC 2019 - Advances in Collection View Layout</a></li>
<li><a href="https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/compositional_layout_objects">Compositional Layout Objects Docs</a></li>
<li>Header photo by <a href="https://unsplash.com/@viniciusamano">Vinicius Amano</a></li>
<li><a href="/en2020-01-10-Compositional-Layout-Part1">Part 1</a></li>
<li><a href="/en2020-01-10-Compositional-Layout-Part2">Part 2</a></li>
</ul>
<h2>Bonus</h2>
<h2>Generic List Layout Function</h2>
<p>This works quite well although if you don't give enough space the layout in the cells will break. You could also pass in the item height and multiply that by the group count to get the estimated group height so that the cells don't break.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">createXListSection</span>(<span class="hljs-params">groupCount</span>: <span class="hljs-type">Int</span>, <span class="hljs-params">estimatedHeight</span>: <span class="hljs-type">CGFloat</span>) -> <span class="hljs-type">NSCollectionLayoutSection</span> {
    <span class="hljs-keyword">let</span> itemSize <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSize</span>(widthDimension: .fractionalWidth(<span class="hljs-number">1</span>),
                                          heightDimension: .fractionalHeight(<span class="hljs-type">CGFloat</span>(<span class="hljs-number">1</span><span class="hljs-operator">/</span>groupCount)))
    <span class="hljs-keyword">let</span> layoutItem <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutItem</span>(layoutSize: itemSize)

    layoutItem.contentInsets <span class="hljs-operator">=</span> <span class="hljs-type">NSDirectionalEdgeInsets</span>(top: <span class="hljs-number">0</span>, leading: <span class="hljs-number">5</span>, bottom: <span class="hljs-number">0</span>, trailing: <span class="hljs-number">5</span>)

    <span class="hljs-keyword">let</span> layoutGroupSize <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSize</span>(widthDimension: .fractionalWidth(<span class="hljs-number">0.95</span>),
                                                 heightDimension: .estimated(estimatedHeight))
    <span class="hljs-keyword">let</span> layoutGroup <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutGroup</span>.vertical(layoutSize: layoutGroupSize,
                                                       subitem: layoutItem,
                                                       count: groupCount)
    layoutGroup.interItemSpacing <span class="hljs-operator">=</span> .fixed(<span class="hljs-number">8</span>)

    <span class="hljs-keyword">let</span> layoutSection <span class="hljs-operator">=</span> <span class="hljs-type">NSCollectionLayoutSection</span>(group: layoutGroup)
    layoutSection.orthogonalScrollingBehavior <span class="hljs-operator">=</span> .groupPagingCentered

    <span class="hljs-keyword">let</span> layoutSectionHeader <span class="hljs-operator">=</span> createSectionHeader()
    layoutSection.boundarySupplementaryItems <span class="hljs-operator">=</span> [layoutSectionHeader]

    <span class="hljs-keyword">return</span> layoutSection
}
</code></pre>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to make an augmented reality decorating experience app with AR Quick Look]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/12/31/How-to-make-an-Augmented-Reality-decorating-experience-app-with-AR-Quick-Look</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/12/31/How-to-make-an-Augmented-Reality-decorating-experience-app-with-AR-Quick-Look</guid>
            <pubDate>Tue, 31 Dec 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><a href="https://developer.apple.com/augmented-reality/quick-look/">AR Quick Look</a> provides a very powerful augmented reality experience with user interactions like moving/scaling the object, people occlusion and sharing of the model supported “out of the box”.</p>
<p>Now, as easy as Apple makes it for us, from my experience, a big part of the work needed for an AR project goes into preparing the models. In case you have a 3D designer making the models for you - make sure you are very specific about the requirements of the models so there are as few adjustments to be done on your side as possible.</p>
<h2>Finding the models</h2>
<p>AR Quick Look supports 2 input formats: <code>.usdz</code> and <code>.reality</code>.
We can interact with multiple models in AR Quick Look only with nested USDZ files, so for this reason we will use the <code>.usdz</code> file format in this tutorial.
Find and download the models you want to use in your experience. Two great places to find free 3D models are:</p>
<h3><a href="https://sketchfab.com/feed">Sketchfab</a></h3>
<p>My go-to place for 3d models for my AR experimental apps.
Those are the models I will be using in this tutorial, they will require a bit of extra work to have them ready for the app, but I like this example as this happens a lot of times in real life.</p>
<ul>
<li><a href="https://sketchfab.com/3d-models/vintage-coffee-table-70s-03-freebie-96bedccbbc3d470f8f7d636b598c7bd2">Vintage Coffee Table by Gueg</a></li>
<li><a href="https://sketchfab.com/3d-models/flowerpot-0ead7a2453da47c3a39b550fb283314f">Flower Pot by r.hessens</a></li>
<li><a href="https://sketchfab.com/3d-models/vauxhall-floor-lamp-antique-black-56054-12d878110c4e4cf2942d510f57417c7d">Vintage Floor Lamp by Zuo Modern</a></li>
<li><a href="https://sketchfab.com/3d-models/plot-mirror-850221-651d1e9e4b074e75934223d4014afa06">Plot Mirror by Zuo Modern</a></li>
</ul>
<p><img src="/assets/img/articles/2019-12-31-How-to-make-an-Augmented-Reality-decorating-experience-app-with-AR-Quick-Look/Models.webp" alt=""></p>
<h3><a href="https://poly.google.com/">Poly Google</a></h3>
<p>Also great for finding models to play around with ARKit. You will be able to see <code>USDZ File</code> as an option in the Download menu of a model if available. Here are 3 examples you can use:</p>
<ul>
<li><a href="https://poly.google.com/view/28PGrcM0hcZ">Yellow low poly chair by Anonymous</a></li>
<li><a href="https://poly.google.com/view/3qzceNuu8F2">Red low poly chair by Anonymous</a></li>
<li><a href="https://poly.google.com/view/aSjQVsSHIHQ">Green low poly chair by Anonymous</a></li>
</ul>
<p><em>Tip: I have experienced it is way easier to find .gltf files and convert them to .usdz. You can checkout <a href="https://engineering.nodesagency.com/categories/ios/2019/10/07/Using-USDZ-for-a-better-AR-experience">this article</a> I wrote for examples on how to do this using the USDZ Tools made by Apple.</em></p>
<h2>Size adjustments on the models (optional)</h2>
<p>Sometimes the size is not good for the augmented reality experience (for example a chair should be 1 meter in height, not 100 meters) so you have to adjust it.</p>
<p><em>Go to next step if your models are already in the right size.</em></p>
<p>It is better to resize the models using 3D software, but I will make the assumption of the worst case scenario: we don't have those tools installed, skills to use them or even access to the original 3D model designer to ask them to do it. So I will show you how to fix this in Xcode:</p>
<ul>
<li>Open the model in Xcode</li>
<li>Select the parent node in the Scene graph tree (called <code>scene</code> in my example)</li>
<li>On the right side in the <code>Node Inspector</code> view, you will be able to see <code>editing space</code>, <code>scale</code> and <code>bounding box</code>. Change the editing space to <code>Local</code> and adjust the scale of the model. The Bounding box measurements are in <code>meters</code> so in my case I scaled it down to 0.001 (for all coordinates - x, y and z - so the model keeps the aspect ratio).
<img src="/assets/img/articles/2019-12-31-How-to-make-an-Augmented-Reality-decorating-experience-app-with-AR-Quick-Look/Resize1.webp" alt=""></li>
<li>After adjusting the scale you can switch back to the <code>World</code> editing space and you will be able to see the updated bounding box.
<img src="/assets/img/articles/2019-12-31-How-to-make-an-Augmented-Reality-decorating-experience-app-with-AR-Quick-Look/Resize2.webp" alt=""></li>
</ul>
<h2>Making the nested USDZ file</h2>
<p>To make the nested USDZ file that contains all of the furniture pieces I will use <code>usdzcreateassetlib</code> from the USDZ Tools. You can find a <a href="/en2019-10-07-Using-USDZ-for-a-better-AR-experience">tutorial on how to install it here</a>.</p>
<ul>
<li>
<p>Put all .usdz files in a folder called <code>models</code></p>
</li>
<li>
<p>Navigate into the folder:</p>
</li>
</ul>
<pre><code class="hljs language-shell">cd models
</code></pre>
<ul>
<li>Run the <code>usdzcreateassetlib</code> command, the first parameter will be the name of the nested model that is created for you, in this case <code>furniture.usdz</code>, followed by the names of all the models you want to include:</li>
</ul>
<pre><code class="hljs language-shell">usdzcreateassetlib furniture.usdz coffee_table.usdz flowerpot.usdz lamp.usdz mirror.usdz
</code></pre>
<h2>Testing out the nested model</h2>
<p>If you want to easily do a check of the model before adding it to your app you can use the Notes app if you have it synchronised between your MacBook and iPhone. Drag and drop the model on your MacBook Notes app in a new note and open the file directly from the iPhone. Simple as that!</p>
<h2>Making the app</h2>
<ul>
<li>
<p>Now that we have the model ready we can start by creating a new <code>Single View App</code> project. For the User Interface mode I will be using Storyboards
<img src="/assets/img/articles/2019-12-31-How-to-make-an-Augmented-Reality-decorating-experience-app-with-AR-Quick-Look/Create-App.webp" alt=""></p>
</li>
<li>
<p>Open the <code>Main.storyboard</code> and add a Button that you can use to start the decorating experience. Feel free to add some extra UI niceness if you wish
<img src="/assets/img/articles/2019-12-31-How-to-make-an-Augmented-Reality-decorating-experience-app-with-AR-Quick-Look/Add-Button.webp" alt=""></p>
</li>
<li>
<p>Connect the button to the ViewController as an <code>IBAction</code></p>
</li>
<li>
<p>Copy the nested USDZ file to your project. Make sure it is added to the target as well
<img src="/assets/img/articles/2019-12-31-How-to-make-an-Augmented-Reality-decorating-experience-app-with-AR-Quick-Look/Copy-USDZ.webp" alt=""></p>
</li>
<li>
<p>In the ViewController, we will first import <code>QuickLook</code> and <code>ARKit</code></p>
</li>
</ul>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> QuickLook
<span class="hljs-keyword">import</span> ARKit
</code></pre>
<ul>
<li>To enable the experience, we need to conform to <a href="https://developer.apple.com/documentation/quicklook/qlpreviewcontrollerdatasource"><code>QLPreviewControllerDataSource</code></a> and implement the following required functions. You can see in the <code>previewController</code> function that we create an URL with the path of the nested USDZ file and return that as a QLPreviewItem</li>
</ul>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">numberOfPreviewItems</span>(<span class="hljs-params">in</span> <span class="hljs-params">controller</span>: <span class="hljs-type">QLPreviewController</span>) -> <span class="hljs-type">Int</span> { <span class="hljs-keyword">return</span> <span class="hljs-number">1</span> }

<span class="hljs-keyword">func</span> <span class="hljs-title function_">previewController</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">controller</span>: <span class="hljs-type">QLPreviewController</span>, <span class="hljs-params">previewItemAt</span> <span class="hljs-params">index</span>: <span class="hljs-type">Int</span>) -> <span class="hljs-type">QLPreviewItem</span> {
    <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> path <span class="hljs-operator">=</span> <span class="hljs-type">Bundle</span>.main.path(forResource: <span class="hljs-string">"furniture"</span>, ofType: <span class="hljs-string">"usdz"</span>) <span class="hljs-keyword">else</span> { <span class="hljs-built_in">fatalError</span>(<span class="hljs-string">"Couldn't find the supported input file."</span>) }
    <span class="hljs-keyword">let</span> url <span class="hljs-operator">=</span> <span class="hljs-type">URL</span>(fileURLWithPath: path)
    <span class="hljs-keyword">return</span> url <span class="hljs-keyword">as</span> <span class="hljs-type">QLPreviewItem</span>
}
</code></pre>
<ul>
<li>In the <code>IBAction</code> we just declared we will add a few lines of code that will start the <code>QLPreviewController</code> with the setup we made above</li>
</ul>
<pre><code class="hljs language-swift"><span class="hljs-keyword">@IBAction</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">startDecoratingButtonPressed</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">sender</span>: <span class="hljs-keyword">Any</span>) {
       <span class="hljs-keyword">let</span> previewController <span class="hljs-operator">=</span> <span class="hljs-type">QLPreviewController</span>()
       previewController.dataSource <span class="hljs-operator">=</span> <span class="hljs-keyword">self</span>
       present(previewController, animated: <span class="hljs-literal">true</span>, completion: <span class="hljs-literal">nil</span>)
   }
</code></pre>
<ul>
<li>Here is the full code, so nice and clean</li>
</ul>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> UIKit
<span class="hljs-keyword">import</span> QuickLook
<span class="hljs-keyword">import</span> ARKit

<span class="hljs-keyword">class</span> <span class="hljs-title class_">ViewController</span>: <span class="hljs-title class_">UIViewController</span>, <span class="hljs-title class_">QLPreviewControllerDataSource</span> {
    <span class="hljs-keyword">override</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">viewDidLoad</span>() {
        <span class="hljs-keyword">super</span>.viewDidLoad()
    }

    <span class="hljs-keyword">@IBAction</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">startDecoratingButtonPressed</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">sender</span>: <span class="hljs-keyword">Any</span>) {
        <span class="hljs-keyword">let</span> previewController <span class="hljs-operator">=</span> <span class="hljs-type">QLPreviewController</span>()
        previewController.dataSource <span class="hljs-operator">=</span> <span class="hljs-keyword">self</span>
        present(previewController, animated: <span class="hljs-literal">true</span>, completion: <span class="hljs-literal">nil</span>)
    }

    <span class="hljs-keyword">func</span> <span class="hljs-title function_">numberOfPreviewItems</span>(<span class="hljs-params">in</span> <span class="hljs-params">controller</span>: <span class="hljs-type">QLPreviewController</span>) -> <span class="hljs-type">Int</span> { <span class="hljs-keyword">return</span> <span class="hljs-number">1</span> }

    <span class="hljs-keyword">func</span> <span class="hljs-title function_">previewController</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">controller</span>: <span class="hljs-type">QLPreviewController</span>, <span class="hljs-params">previewItemAt</span> <span class="hljs-params">index</span>: <span class="hljs-type">Int</span>) -> <span class="hljs-type">QLPreviewItem</span> {
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> path <span class="hljs-operator">=</span> <span class="hljs-type">Bundle</span>.main.path(forResource: <span class="hljs-string">"furniture"</span>, ofType: <span class="hljs-string">"usdz"</span>) <span class="hljs-keyword">else</span> { <span class="hljs-built_in">fatalError</span>(<span class="hljs-string">"Couldn't find the supported input file."</span>) }
        <span class="hljs-keyword">let</span> url <span class="hljs-operator">=</span> <span class="hljs-type">URL</span>(fileURLWithPath: path)
        <span class="hljs-keyword">return</span> url <span class="hljs-keyword">as</span> <span class="hljs-type">QLPreviewItem</span>
    }

}
</code></pre>
<h2>Demo</h2>
<p><em>Tip: You can use two-finger swipe gesture on an object to levitate it. This is what we will use to place the flower pot on top of the table</em></p>
<p><img src="/assets/img/articles/2019-12-31-How-to-make-an-Augmented-Reality-decorating-experience-app-with-AR-Quick-Look/Demo.webp" alt=""></p>
<h2>Resources</h2>
<ul>
<li><a href="https://developer.apple.com/augmented-reality/quick-look/">AR Quick Look</a></li>
<li><a href="https://developer.apple.com/documentation/arkit/previewing_a_model_with_ar_quick_look">Previewing a model with AR Quick Look</a></li>
<li><a href="https://graphics.pixar.com/usd/docs/Usdz-File-Format-Specification.html">USDZ file format specification</a></li>
<li><a href="https://unsplash.com/photos/dLmlYKuoJBc">Image used in the app UI by Jean-Philippe Delberghe</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Android Dark Theme]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/12/30/Android-Dark-Theme</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/12/30/Android-Dark-Theme</guid>
            <pubDate>Mon, 30 Dec 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>With the arrival of Android 10, the latest trend in Android
development is giving the users the choice to select a dark version
of the app. Some say it can help save battery life and some
just prefer it because it is easier on the eyes when the user opens the app
in a dark room or at night for example. Whatever reason you prefer, here's a
quick walk through on how to implement it.</p>
<h2>Getting Started</h2>
<p>The quickest and easiest way to get started with the dark theme is forcing
it on the style. This will only work on Android 10, where the system will
override the app colors and try to make it dark. This can work nicely
for simple apps, but it might mess the app if it has a complex visual.</p>
<p>Note that doing this the dark mode will only work on Android 10 and it
won't have backward compatibility as the previous versions of Android
can't handle the dark mode natively.</p>
<h2>Forcing the Dark Theme</h2>
<p>Add this to your <code>Theme</code> in the <code>style.xml</code>:</p>
<pre><code class="hljs language-ini">&#x3C;item <span class="hljs-attr">name</span>=<span class="hljs-string">"android:forceDarkAllowed"</span> tools:targetApi=<span class="hljs-string">"q"</span>><span class="hljs-literal">true</span>&#x3C;/item>
</code></pre>
<h2>Examples</h2>
<p>Here are some examples where we let the system handle the dark mode:</p>
<p><img src="/assets/img/articles/2019-12-30-Android-Dark-Theme/avon1.webp" alt="">
<img src="/assets/img/articles/2019-12-30-Android-Dark-Theme/avon2.webp" alt=""></p>
<h2>Possible problems</h2>
<p>You can see in the previous screenshots that it worked quite well. But in some cases it's
not ideal, like the following example where the options are showing but you
can barely see the card background for each button:</p>
<p><img src="/assets/img/articles/2019-12-30-Android-Dark-Theme/avon-help.webp" alt=""></p>
<p>When forcing the dark theme, you can also specify in the xml for a
specific element to not be styled as dark using the following tag:</p>
<pre><code class="hljs language-ini">&#x3C;View
    ...
    android:<span class="hljs-attr">forceDarkAllowed</span>=<span class="hljs-string">"false"</span>
    .../>
</code></pre>
<p>This is a example with the dark mode forced on the theme and adding the
ignore tag on the card element:</p>
<p><img src="/assets/img/articles/2019-12-30-Android-Dark-Theme/avon-ignore.webp" alt=""></p>
<p>You can see that everything became dark but the card keep the original
background and text colors.</p>
<h2>Supporting Dark Theme</h2>
<p>To properly support the dark theme you first need to add the DayNight style
as parent of your app theme:</p>
<pre><code class="hljs language-ini">&#x3C;style <span class="hljs-attr">name</span>=<span class="hljs-string">"AppTheme"</span> parent=<span class="hljs-string">"Theme.AppCompat.DayNight"</span>>
</code></pre>
<p>or the proper theme from <a href="https://github.com/material-components/material-components-android">MaterialComponents'</a> :</p>
<pre><code class="hljs language-ini">&#x3C;style <span class="hljs-attr">name</span>=<span class="hljs-string">"AppTheme"</span> parent=<span class="hljs-string">"Theme.MaterialComponents.DayNight"</span>>
</code></pre>
<p>After this simple change you can start handling the dark mode state on the app.</p>
<p>I've created this simple class to ease the task of switching themes, you
can check it <a href="https://github.com/nodes-android/kotlin-template/blob/92e3406e3e70bef0e1e7cbdbb4fb3527e34c19f1/presentation/src/main/java/dk/nodes/template/presentation/util/ThemeHelper.kt">here</a>.</p>
<p>So using it to change the theme is as easy as: <code>ThemeHelper.applyTheme(Theme.DARK)</code>.</p>
<p>Don't forget to store the user preference of the theme, as the system doesn't store it,
you have to manually handle the states. The best place to apply the user
theme preference is in the <code>Application</code> class of the app, so when the views
are rendered the system already uses the proper theme resources.</p>
<h2>Handling Dark Theme</h2>
<p>If you want your app theme to follow the system settings, you have to
be aware if there is a configuration change and properly handle the
screens in your app so it doesn't lose data when it gets recreated.</p>
<p>For handling the activity recreation you need to add a config change
param in the <code>AndroidManifest.xml</code> for you activity, like this:</p>
<pre><code class="hljs language-ini">&#x3C;activity
    android:<span class="hljs-attr">name</span>=<span class="hljs-string">".MyActivity"</span>
    android:<span class="hljs-attr">configChanges</span>=<span class="hljs-string">"uiMode"</span> />
</code></pre>
<p>After that you can override the <code>onConfigurationChanged()</code> method in the
activity to handle and save the state when the user or the system switch
the system-wide theme.</p>
<p>Take care that, when supporting a dark theme, you shouldn't hardcode colors,
especially for background as it can look out of place in one of the themes.
Instead, you can set colors for the light theme (default folder) and
override the colors that don't look nice for the dark theme creating a
<code>colors.xml</code> in the night folder. Like this:</p>
<h5><strong>../src/main/res/values/colors.xml</strong></h5>
<pre><code class="hljs language-ini">&#x3C;resources>
    &#x3C;color <span class="hljs-attr">name</span>=<span class="hljs-string">"colorPrimary"</span>><span class="hljs-comment">#3F51B5&#x3C;/color></span>
    &#x3C;color <span class="hljs-attr">name</span>=<span class="hljs-string">"colorPrimaryDark"</span>><span class="hljs-comment">#303F9F&#x3C;/color></span>
    &#x3C;color <span class="hljs-attr">name</span>=<span class="hljs-string">"colorFabIcon"</span>><span class="hljs-comment">#FFFFFF&#x3C;/color></span>
&#x3C;/resources>
</code></pre>
<h5><strong>../src/main/res/values-night/colors.xml</strong></h5>
<pre><code class="hljs language-ini">&#x3C;resources>
    &#x3C;color <span class="hljs-attr">name</span>=<span class="hljs-string">"colorPrimary"</span>><span class="hljs-comment">#B53F3F&#x3C;/color></span>
    &#x3C;color <span class="hljs-attr">name</span>=<span class="hljs-string">"colorPrimaryDark"</span>><span class="hljs-comment">#9F3030&#x3C;/color></span>
    &#x3C;color <span class="hljs-attr">name</span>=<span class="hljs-string">"colorFabIcon"</span>><span class="hljs-comment">#000000&#x3C;/color></span>
&#x3C;/resources>
</code></pre>
<p>And here is how it looks like:</p>
<p><img src="/assets/img/articles/2019-12-30-Android-Dark-Theme/kotlin-template.webp" alt=""></p>
<p>With the Material Components library the system already provides some useful
resources to help you apply the dark theme to elements, like <code>android:colorBackground</code>
that automatically changes based on the app theme you apply.</p>
<p>You can read more about it <a href="https://material.io/develop/android/theming/color/">here</a>.</p>
<h2>Dark All the Things</h2>
<p>Implementing dark mode is not as hard as it looks. With some small
state and color handling it is possible to create an app that looks good on
both themes. Some people still prefer the light mode, so it's always nice to have the
option to switch and let the users chose way they prefer.</p>
<h2>Further Reading</h2>
<p><a href="https://developer.android.com/guide/topics/ui/look-and-feel/darktheme">Android Developers - Dark theme</a></p>
<p><a href="https://material.io/develop/android/theming/dark/">Dark Theme - Material Components</a></p>
<p><a href="https://github.com/material-components/material-components-android">Material Components GitHub</a></p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/w33-zg-dNL4">Rami Al-zayat</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[iOS 2019 Retrospective]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/12/16/ios-2019-retrospective</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/12/16/ios-2019-retrospective</guid>
            <pubDate>Mon, 16 Dec 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Apple recently posted their <a href="https://developer.apple.com/news/?id=11292019a">annual announcement</a> that App Store is shutting down over the holidays. At the same time Stack Overflow has announced that <a href="https://stackoverflow.blog/2019/12/09/tis-the-season-for-hats-join-us-for-winter-bash-2019/">Winter Bash 2019</a> is here, giving you a chance to add a funny hat to your Stack Overflow profile because why not?</p>
<p>All of that can only mean one thing: 2019 is coming to an end and what a year for the Swift community!</p>
<p>And so, as the snow falls quietly over Denmark (actually...its 5 degrees and raining outside!) and we are all in hygge-mode, roasting our chestnuts on an open fire (we're not really! That heat came from running <code>carthage bootstrap --platform iOS</code>!). We - the iOS developers here at Nodes - thought it would be fun to have a lighthearted look back at some of the events that took place in the iOS community throughout the year of 2019.</p>
<p>So...Please join us for a walk down memory lane.</p>
<h2>Winter/Spring</h2>
<p>Winter and early spring is usually the quiet period in Apple developer land. The number of beta versions of iOS, macOS etc. is low, compared to the fall frenzy at least.</p>
<p>This year was a bit atypical as Swift 5 was released in March.</p>
<h3>Swift 5</h3>
<p>Swift 5 gave us, amongst many other things:</p>
<ul>
<li>a standard <code>Result</code> type, so we didn't have to add that to each and every project ourselves.</li>
<li>raw strings which allows you to write: <code>#"Look at this! I can finally put things in "airquotes" without those ugly backspaces"#</code></li>
</ul>
<p>For a refresher of what was included in Swift 5, have a look at this <a href="https://www.hackingwithswift.com/articles/126/whats-new-in-swift-5-0">fine post</a> from Paul Hudson</p>
<h3>Revoking of Enterprise Certificates</h3>
<p>Apart from that, there was the "<a href="https://techcrunch.com/2019/01/29/facebook-project-atlas/">Enterprise Certificate-gate</a>" (no year without a gate right?) where Apple revoked Facebooks Enterprise certificate after discovering that Facebook was using their Enterprise certificate to install "spyware" disguised as a research app on users devices (with the users consent though). Many <a href="https://www.theverge.com/2019/1/31/18206027/apple-facebook-research-app-enterprise-certificate-google">articles</a> and tweets were written but now, almost a year later we had almost forgot about this, until we started researching for this post.</p>
<h3>Realm acquired by MongoDB</h3>
<p>Another tidbit from the spring of 2019 was that MongoDB announced that they had acquired <a href="https://realm.io/blog/mongodb-to-acquire-realm-the-future-is-bright/">Realm</a>. We have used Realm in more than one project - plus these stories of acquisition are always interesting to follow from the sideline.</p>
<p>Since the initial post, things have been awfully quiet until October where the future <a href="https://realm.io/blog/sharing-the-mongodb-realm-roadmap/">roadmap</a> was announced.</p>
<p>It will be interesting to follow the continued adventures of Realm in 2020.</p>
<h3>Marzipan</h3>
<p>However, the thing we spent the most of our time discussing in the spring of 2019 in Apple land was Marzipan.</p>
<p>Marzipan was the codename Apple gave their new tool/framework which allowed us to run iOS apps on macOS and it was demonstrated at WWDC 2018 so we knew it was coming, but not the scope of it.</p>
<p>This led to countless blogposts, tweets, speculations and discussions in the community (a polite word for bitter arguments) about whether Marzipan was the future of macOS apps?</p>
<p>These are just <em>a few</em> of the <em>many</em> posts that were written in the spring related to Marzipan</p>
<ul>
<li><a href="https://blog.curtisherbert.com/pacing-ourselves-in-the-marzipan-marathon/">https://blog.curtisherbert.com/pacing-ourselves-in-the-marzipan-marathon/</a></li>
<li><a href="https://9to5mac.com/2019/02/20/marzipan-mac/">https://9to5mac.com/2019/02/20/marzipan-mac/</a></li>
<li><a href="https://www.theverge.com/2019/5/29/18643448/apple-wwdc-preview-mac-ipad-apps-marzipan">https://www.theverge.com/2019/5/29/18643448/apple-wwdc-preview-mac-ipad-apps-marzipan</a></li>
</ul>
<p>And so, as we spent our time discussing Marzipan and taking advantage of the new <code>Result</code> the seasons changed outside our windows, winter became spring and Apple announced the dates and poster for this years WWDC event, leaving us to wonder what all those symbols meant.</p>
<p><img src="/assets/img/articles/2019-12-16-ios-2019-retrospective/wwdc19.webp" alt="WWDC 2019 Poster">
<em>Source <a href="https://developer.apple.com">Apple</a></em></p>
<p>Little did we know at the time that we were actually about to get our minds blown at this years WWDC!</p>
<h2>Summer - WWDC</h2>
<p>Summer to an Apple developer means one thing: WWDC!</p>
<p>If you are not one of the fortunate few who get to spend your hard earned money going to the conference, you still get a lot of value from WWDC with all the conference videos you have to watch, the new betas you have to play with and all the new frameworks you have to get acquainted with.</p>
<p>It is at the same time - to quote a famous book opening - the best of times and the worst of times. Everything is new, shiny and exiting and at the same time everything is new, broken and confusing.</p>
<p>This year, leading into WWDC, we of course knew about Marzipan and we had heard rumurs about <a href="https://www.cultofmac.com/627088/what-to-expect-from-wwdc-2019/">Dark Mode</a> but apart from that, Apple had managed to mostly keep the leaks under control so we didn't really know what to expect.</p>
<p>Boy were we in for a surprise!</p>
<h3>WWDC</h3>
<p>Lets just get it out of the way now. WWDC 2019 was one of the best WWDCs for an Apple developer in many years! 2014 was crazy with the introduction of Swift of course, but 2019 is up there.</p>
<p>Ironically there weren't that many new features in neither iOS 13, macOS Catalina, tvOS or watchOS for that matter. We got our Dark Mode and iTunes was split into separate apps, but apart from that it was the reveal of the new <em>insane</em> <a href="https://www.apple.com/mac-pro/">MacPro</a> (insane also covers the price ;)) that initially got the most applause and excitement.</p>
<p>The <a href="https://www.youtube.com/watch?v=psL_5RIBqnY">keynote</a> followed a familiar path for the first couple of hours. Nearing the end it was revealed that project Marzipan was to be known as <a href="https://developer.apple.com/mac-catalyst/">Catalyst</a> and we started thinking "well...that was that, lets start downloading the betas"</p>
<p>But then Craig Federighi came back to stage in a "One more thing" kind of moment (timecode: 2.06.33 in the keynote if you want to rewatch) to introduce <a href="https://developer.apple.com/xcode/swiftui/">Swift UI</a> which left most of us buzzing with equal parts of joy, excitement, confusion and bewilderment.</p>
<p>Tons and tons of questions and new content to look into!</p>
<p>All of a sudden that WWDC 2019 logo with the exploding head made perfect sense.</p>
<p>Sitting here now in December of 2019 with the knowledge we have now about Swift UI and its current state, it is easy to be cynical and snarky but back in early June it really felt like something fundamental had shifted from under our feet. Brent Simmons sums it up really really well in this great <a href="https://inessential.com/2019/06/07/the_next_era_ends_the_swift_era_begins">blog post</a> where he writes:</p>
<blockquote>
<p>I’m surely not the only person to think, all week long, that this WWDC marks the end of Apple’s NeXT era and the beginning of the Swift era.</p>
</blockquote>
<blockquote>
<p>The NeXT era began, of course, when Apple acquired NeXT, with its Unix-based operating system, amazing developer tools and frameworks, and its CEO, Steve Jobs.</p>
</blockquote>
<p>...</p>
<blockquote>
<p>Before WWDC there was plenty of talk about Catalyst (which we had been calling Marzipan) which is a way of bringing UIKit (iOS) apps to the Mac. And this was indeed part of the news of the week.</p>
</blockquote>
<blockquote>
<p>But the future was never about UIKit being a better version of what we already had for Macs. In some respects UIKit is better, in other ways not. It’s just that there are so many more iOS apps than Mac apps.</p>
</blockquote>
<blockquote>
<p>Though I don’t discount Catalyst’s usefulness — we will get lots of apps new to the Mac — the real news this week was about SwiftUI and the Combine framework. This, finally, is a new way of writing apps, and it’s based on Swift and not on Objective-C. It’s very much not from NeXT.</p>
</blockquote>
<p>Think about it! With SwiftUI we are starting the journey away from frameworks designed in the early 90es! Thats how slow these fundamental technologies, these tectonic plates if you will, move from time to time, but now things have been set in motion and we are starting the transition to a new architecture.</p>
<p>As the dust settled and we got to play with the betas, watch the presentations and read the documentation of course other hidden gems started to appear. Here are just a few honorable mentions:</p>
<ul>
<li>a new <a href="https://medium.com/flawless-app-stories/ios-13-uisegmentedcontrol-3-important-changes-d3a94fdd6763">design</a> on the <code>UISegmentedControl</code></li>
<li><code>UITableViewDiffableDataSource</code> <a href="https://wwdcbysundell.com/2019/diffable-data-sources-first-look/">Described here</a>, and WWDC talk is <a href="https://developer.apple.com/videos/play/wwdc2019/220/">here</a></li>
<li>Compositional layouts in <code>UICollectionView</code>, WWDC talk <a href="https://developer.apple.com/videos/play/wwdc2019/215/">here</a></li>
<li><a href="https://developer.apple.com/documentation/combine">Combine</a>, Apple's attempt of an reactive framework which works great with SwiftUI but also works wonders on its own.</li>
</ul>
<h3>Post WWDC</h3>
<p>After WWDC the summer was spent trying to keep up with the torrent of blog posts, tweets and videos that hit us.</p>
<p>The engineers here at Nodes even contributed to this torrent with some - if we may say so ourselves - mighty fine blog posts about new features such as:</p>
<ul>
<li><a href="/en2019-06-21-Add-A-Swift-Package-to-Your-iOS-Application">Swift Package Manager - part 1</a></li>
<li><a href="/en2019-06-21-Create-Your-Own-Swift-Package">Swift Package Manager - part 2</a></li>
<li><a href="/en2019-06-26-ARKit-3-brings-more-power-to-Apples-Augmented-Reality">ARKit 3</a></li>
<li><a href="/en2019-07-03-Dark-Mode">Dark Mode</a></li>
</ul>
<p>Of course the community also had the usual discussions about the quality of the betas and how things deteriorated quickly over the pace of those betas.</p>
<p>Only! This year the bugs seemed a bit more serious (<a href="https://mjtsai.com/blog/2019/07/11/icloud-data-loss-with-macos-10-15-and-ios-13-betas/">iCloud content</a> being lost for instance) and we got awfully close to the GM versions before things started to look fairly stable.</p>
<p>Also, as SwiftUI was exposed to "real users" and use cases things started changing fast. Classes and functions got renamed or deprecated and tutorials went out of sync with the real world at a frantic pace. For a brush up, have a look at <a href="https://medium.com/better-programming/swiftui-will-it-be-ready-with-only-a-month-to-go-128cc9187028">this blog post</a> from early August for instance.</p>
<p>Apart from that, we started looking forward to the GM of Xcode so we could start reclaiming precious disk space by not having to have both the beta and the normal Xcode installed.</p>
<p>...little did we know! (cue: dramatic music! Narrator: "<em>After the break! Join us to find out how little the iOS developers at Nodes did know</em>")</p>
<h2>Fall</h2>
<p>Fall to an Apple developer means new versions of iOS and macOS and also new iPhones, something you anticipate with equal parts excitement for the smell of new hardware and dread for how much rushed work you have to do to adapt your app to new hardware and new features.</p>
<p>Well, we got our <a href="https://www.apple.com/iphone/">iPhones</a> and they were pretty (albeit expensive).</p>
<h3>iOS 13</h3>
<p>What we also got this year was betas and updates</p>
<p>![Soo many betas](/assets/img/articles/2019-12-16-ios-2019-retrospective/betas.webp</p>
<p>iOS 13 was followed by 13.1 which was followed by 13.1.1, which was followed by 13.1.2 which was...OK lets cut to the chase. As of early December the current iOS version is 13.3 and there are still features and things which are not working as expected.</p>
<p>One example was the horror stories of HomePods being <a href="https://www.cultofmac.com/661814/bricked-homepod-ios-13-2/">bricked by installing iOS 13.2</a> which led to Apple pulling that update!</p>
<p>All this led to Apple overhauling their release and test process for iOS 14 - according to <a href="https://www.bloomberg.com/news/articles/2019-11-21/apple-ios-14-features-changes-testing-after-ios-13-bugs">this article</a> in Bloomberg which has these quoteworthy sections for instance:</p>
<blockquote>
<p>By August, realizing that the initial iOS 13.0 set to ship with new iPhones a few weeks later wouldn’t hit quality standards, Apple engineers decided to mostly abandon that work and focus on improving iOS 13.1, the first update. Apple privately considered iOS 13.1 the “actual public release” with a quality level matching iOS 12. The company expected only die-hard Apple fans to load iOS 13.0 onto their phones.</p>
</blockquote>
<blockquote>
<p>The timing of the iOS 13.1 update was moved up by a week to Sept. 24, compressing the time that iOS 13.0 was Apple’s flagship OS release. New iPhones are so tightly integrated with Apple software that it would have been technically impossible to launch the iPhone 11 with iOS 12, and since 13.1 wasn’t ready in time, Apple’s only choice was to ship with 13.0 and update everyone to 13.1 as quickly as it could.</p>
</blockquote>
<p>Ouch!</p>
<h3>SwiftUI</h3>
<p>We also spent the fall of 2019 discussing SwiftUI and its state, was it ready for production? Should you rewrite your entire app and so on.</p>
<p>Brent Simmons (who we mentioned just after WWDC, remember?) wrote <a href="https://inessential.com/2019/10/21/swiftui_is_still_the_future">this blog post</a> in October where he mentioned that they had rewritten SwiftUI code as classic UIKit code due to limitations.</p>
<p>It is still very early days for SwiftUI and there <em>will</em> be flaws and limitations (think back to Swift 1.0 for comparison!). We are however certain that Apple are working hard to improve SwiftUI and we can't wait to see what will be announced in 2020.</p>
<h3>Catalyst</h3>
<p>But what about Catalyst? The thing we talked so much about in the spring? Well...oddly enough, as SwiftUI was introduced it seemed all the hype around Catalyst faded out. Looking here in December 2019 you'll have a hard time finding many apps taking advantage of Catalyst.</p>
<h3>Conferences</h3>
<p>Fall also means conferences. This year developers from Nodes went to both iOS conferences like NSSpain, but also conferences related to Android, Vapor and JavaScript</p>
<p>Here are some of our reports, just to rub it in!</p>
<ul>
<li><a href="/en2019-11-07-NSSpain-2019">NSSpain</a></li>
<li><a href="/en2019-10-08-Droidcon-Berlin">DroidCon Berlin</a></li>
<li><a href="/en2019-11-06-Droidcon-London">DroidCon London</a></li>
<li><a href="/en2019-11-06-ServerSide-swift">ServerSide Swift</a></li>
<li><a href="/en2019-12-03-NordicJS-2019">Nordic js</a></li>
</ul>
<h3>Hacktoberfest</h3>
<p><img src="/assets/img/articles/2019-12-16-ios-2019-retrospective/Hacktoberfest_19.webp" alt="Hacktoberfest"></p>
<p>October = <a href="https://hacktoberfest.digitalocean.com">Hacktoberfest</a>, the annual celebration of Open Source and contribution. For the first time, Nodes chose to get involved in this event by organizing [events]({% post_url 2019-11-11-Nodes-Hacktoberfest-2019 %}) at various offices.</p>
<p>This lead to many great PRs to our <a href="http://github.com/nodes-ios">iOS repositories</a>. Thank you to all who contributed!</p>
<h2>End of the year</h2>
<p>Over these past few weeks Apple has released their new <a href="https://www.apple.com/macbook-pro-16/">MacBook Pro 16-inch</a> model, their new <a href="https://www.apple.com/airpods-pro/">Airpods Pro</a> and the new <a href="https://www.apple.com/mac-pro/">MacPro</a>, just in case you were wondering what all we want for Christmas is (no you don't get to sing Mariah!)</p>
<p>And so we conclude our tour back here in mid December after a whirlwind tour of the year 2019 as an iOS developer. A crazy year for Swift which gave us SwiftUI and Combine which - probably - will shape our work for many years to come.</p>
<p>At the same time not the best year for iOS with a bug ridden release in the fall.</p>
<p>Looking towards 2020 there are a couple of things to keep an eye on:</p>
<ul>
<li>what will happen with SwiftUI?</li>
<li>what will Apples stance on <a href="https://www.apple.com/privacy/">privacy</a> mean to their relationship with some of the other large tech companies?</li>
<li>what will happen with AppleTV+?</li>
</ul>
<p>We will know a great deal more at the end of 2020 :)</p>
<p>Thank you for reading along and from all of us here at Nodes to all of you on the good earth, we wish you happy holidays, may your bugs be easy to fix and may your builds compile fast.</p>
<p>See you in 2020.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/C0sW3yscQXc">NordWood Themes</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[An overview of watchOS complications]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/12/04/watchOS-complications</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/12/04/watchOS-complications</guid>
            <pubDate>Wed, 04 Dec 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Complications are bits of watchOS application that can be put on the user's watch face and offer quick information, simply by looking at his/her Apple Watch, without opening the accompanying watchOS/iOS app. The Apple Watch ships with some default complications: Calendar, Weather, Activity, Battery (it shows only the watch’s battery level), etc.</p>
<p>There are 5 complication families. This article will show how to make 7 types of complications, from each complication family. 2 of the shown complications are only available in watchOS 5, and only on the Apple Watch Series 4 and up.</p>
<p>Recently, I had the chance to play with watchOS complications. This led me on a learning path.</p>
<p><code>There is a goodie at the end of the article :)</code></p>
<p>Before starting, I would like to point out a series of links that turned out to be useful on the subject of watchOS complications:</p>
<ul>
<li><a href="https://developer.apple.com/documentation/clockkit/clkcomplicationfamily">CLKComplicationFamily</a></li>
<li><a href="https://developer.apple.com/design/human-interface-guidelines/watchos/app-architecture/complications/">HIG on watchOS Complications</a></li>
<li><a href="https://developer.apple.com/design/human-interface-guidelines/watchos/icons-and-images/complication-images/">HIG on watchOS Complication images</a></li>
</ul>
<p>The examples that follow will show how to achieve one flavour for each of the complication family.</p>
<p>We start off with:</p>
<h3><a href="https://developer.apple.com/documentation/clockkit/circular_small">Circular Small</a></h3>
<pre><code class="hljs language-swift">circularSmall.textProvider <span class="hljs-operator">=</span> <span class="hljs-type">CLKSimpleTextProvider</span>(text: <span class="hljs-string">""</span>)
circularSmall.fillFraction <span class="hljs-operator">=</span> <span class="hljs-type">Float</span>()
circularSmall.ringStyle <span class="hljs-operator">=</span> <span class="hljs-type">CLKComplicationRingStyle</span>.closed
</code></pre>
<p>The snippet creates a closing ring, with a string in the middle.</p>
<p><img src="/assets/img/articles/2019-12-04-watchOS-complications/1_Circular_Small.webp" alt="Example Image"></p>
<p>The <strong>fillFraction</strong> takes values between 0.0 and 1.0. The closer the value is to 1.0, the more closed the ring is.</p>
<p>This complication can be used in the <em>Color</em> watch face.</p>
<h3><a href="https://developer.apple.com/documentation/clockkit/extra_large">Extra Large</a></h3>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> extraLarge <span class="hljs-operator">=</span> <span class="hljs-type">CLKComplicationTemplateExtraLargeRingText</span>()
extraLarge.textProvider <span class="hljs-operator">=</span> <span class="hljs-type">CLKSimpleTextProvider</span>(text: <span class="hljs-string">""</span>)
extraLarge.fillFraction <span class="hljs-operator">=</span> <span class="hljs-type">Float</span>()
extraLarge.ringStyle <span class="hljs-operator">=</span> <span class="hljs-type">CLKComplicationRingStyle</span>.closed
</code></pre>
<p><img src="/assets/img/articles/2019-12-04-watchOS-complications/2_X-tra_Large.webp" alt="2 X-tra Large"></p>
<p>The snippet creates a closing ring, with a string in the middle.
The <strong>fillFraction</strong> takes values between 0.0 and 1.0. The closer the value is to 1.0, the more closed the ring is.</p>
<p>This complication can be used in the <em>X-Large</em> watch face</p>
<h3><a href="https://developer.apple.com/documentation/clockkit/graphic">Graphic Circular</a></h3>
<p>Graphic Circular is one of the new complications introduced in watchOS 5 and only available on the Apple Watch Series 4 and up.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> graphicCircular <span class="hljs-operator">=</span> <span class="hljs-type">CLKComplicationTemplateGraphicCircularOpenGaugeRangeText</span>()
graphicCircular.centerTextProvider <span class="hljs-operator">=</span> <span class="hljs-type">CLKSimpleTextProvider</span>(text: <span class="hljs-string">""</span>)
graphicCircular.leadingTextProvider <span class="hljs-operator">=</span> <span class="hljs-type">CLKSimpleTextProvider</span>(text: <span class="hljs-string">""</span>)
graphicCircular.trailingTextProvider <span class="hljs-operator">=</span> <span class="hljs-type">CLKSimpleTextProvider</span>(text: <span class="hljs-string">""</span>)
graphicCircular.gaugeProvider <span class="hljs-operator">=</span> <span class="hljs-type">CLKTimeIntervalGaugeProvider</span>(style: .ring, gaugeColors: [<span class="hljs-type">Array</span> of <span class="hljs-type">UIColors</span>], gaugeColorLocations: <span class="hljs-literal">nil</span>, start: startDate, end: endDate)
</code></pre>
<p><img src="/assets/img/articles/2019-12-04-watchOS-complications/3_Graphic_Circular.webp" alt="3 Graphic Circular"></p>
<p>The <em>center text</em> is the text that will be in the "middle" of the ring.
The <em>leading text</em> will be in the bottom left of the ring, while the <em>trailing text</em> will be in the bottom right.</p>
<p>The gauge can be either filled-style or ring-style. In the above example, the ring-style is chosen. Depending on the start date and the end date provided, it will have a small circle that will move along the gauge.</p>
<p>The <code>gaugeColors</code> parameters has to be given an array of colors. If you provide <code>gaugeColorLocations</code> with a <code>nil</code> value, watchOS will place the colors in an even out manner.</p>
<h3><a href="https://developer.apple.com/documentation/clockkit/graphic">Graphic Rectangular</a></h3>
<p>Graphic Rectangular is one of the new complications introduced in watchOS 5 and only available on the Apple Watch Series 4 and up.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> graphicRectangular <span class="hljs-operator">=</span> <span class="hljs-type">CLKComplicationTemplateGraphicRectangularTextGauge</span>()
graphicRectangular.headerTextProvider <span class="hljs-operator">=</span> <span class="hljs-type">CLKSimpleTextProvider</span>(text: <span class="hljs-string">""</span>)
graphicRectangular.body1TextProvider <span class="hljs-operator">=</span> <span class="hljs-type">CLKSimpleTextProvider</span>(text: <span class="hljs-string">""</span>)
graphicRectangular.gaugeProvider <span class="hljs-operator">=</span> <span class="hljs-type">CLKSimpleGaugeProvider</span>(style: .fill, gaugeColors: [<span class="hljs-type">Array</span> of <span class="hljs-type">UIColors</span>], gaugeColorLocations: <span class="hljs-literal">nil</span>, fillFraction: <span class="hljs-type">Float</span>())```
</code></pre>
<p><img src="/assets/img/articles/2019-12-04-watchOS-complications/4_Graphic_Rectangular.webp" alt="4 Graphic Rectangular"></p>
<h3><a href="https://developer.apple.com/documentation/clockkit/modular_large">Modular Lage</a></h3>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> modularLargeTemplate <span class="hljs-operator">=</span> <span class="hljs-type">CLKComplicationTemplateModularLargeStandardBody</span>()
modularLargeTemplate.headerTextProvider <span class="hljs-operator">=</span> <span class="hljs-type">CLKSimpleTextProvider</span>(text: <span class="hljs-string">""</span>)
modularLargeTemplate.body1TextProvider <span class="hljs-operator">=</span> <span class="hljs-type">CLKSimpleTextProvider</span>(text: <span class="hljs-string">""</span>)
modularLargeTemplate.body2TextProvider <span class="hljs-operator">=</span> <span class="hljs-type">CLKSimpleTextProvider</span>(text: “”)
</code></pre>
<p><img src="/assets/img/articles/2019-12-04-watchOS-complications/5_Modular_Large.webp" alt="5 Modular Large"></p>
<h3><a href="https://developer.apple.com/documentation/clockkit/modular_small">Modular small</a></h3>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> modularSmall <span class="hljs-operator">=</span> <span class="hljs-type">CLKComplicationTemplateModularSmallRingText</span>()
modularSmall.textProvider <span class="hljs-operator">=</span> <span class="hljs-type">CLKSimpleTextProvider</span>(text: <span class="hljs-string">""</span>)
modularSmall.fillFraction <span class="hljs-operator">=</span> <span class="hljs-type">Float</span>()
modularSmall.ringStyle <span class="hljs-operator">=</span> <span class="hljs-type">CLKComplicationRingStyle</span>.closed
</code></pre>
<p><img src="/assets/img/articles/2019-12-04-watchOS-complications/6_Modular_Small.webp" alt="6 Modular Small"></p>
<p>This complication is very similar to set up like the Circular Small one.</p>
<h3><a href="https://developer.apple.com/documentation/clockkit/utilitarian">Utilitarian Small</a></h3>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> utilitarianSmall <span class="hljs-operator">=</span> <span class="hljs-type">CLKComplicationTemplateUtilitarianSmallRingText</span>()
utilitarianSmall.textProvider <span class="hljs-operator">=</span> <span class="hljs-type">CLKSimpleTextProvider</span>(text:<span class="hljs-string">""</span>)
utilitarianSmall.fillFraction <span class="hljs-operator">=</span> <span class="hljs-type">Float</span>()
utilitarianSmall.ringStyle <span class="hljs-operator">=</span> <span class="hljs-type">CLKComplicationRingStyle</span>.closed
</code></pre>
<p><img src="/assets/img/articles/2019-12-04-watchOS-complications/7_Utilitarian_Small.webp" alt="7 Utilitarian Small"></p>
<p>This complication is very similar to set up like the Circular Small one.</p>
<hr>
<p>The examples show how to make a complication for only 7 out of the 11 types of complications.</p>
<p>Keep in mind that each complication has other possibilities to show the information — instead of a closed/open ring, one could only show text or only an image.</p>
<h3>At the end</h3>
<p>We gather all the complications available in one cheat sheet to make it easier to have an overview over the many complications that Apple has to offer.
You can find the name of the complication and links to Apple's documentation.
You can get <a href="/assets/img/articles/2019-12-04-watchOS-complications/watchOS_Cheat_Sheet.pdf">the cheat sheet here</a></p>
<p><em>Article Header Photo by <a href="https://unsplash.com/@lloyddirks?utm_source=unsplash&#x26;utm_medium=referral&#x26;utm_content=creditCopyText">Lloyd Dirks on Unsplash</a></em></p>
<p><em>All other pictures taken from <a href="https://developer.apple.com/documentation/clockkit">Apple's documentation</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Nordic.js 2019]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/12/03/NordicJS-2019</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/12/03/NordicJS-2019</guid>
            <pubDate>Tue, 03 Dec 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>The 9th version of the <a href="https://nordicjs.com/2019">Nordic.js</a> conference took place at Magasin 9 near the harbor of Stockholm.
The whole frontend team was invited, and three of us were able to attend (Rasmus, Themi and Jonas).
It was our first trip to a conference as a team, and it was nice to spend time with colleagues outside of the office, away from desks and keyboards.</p>
<p>The conference has, as the name indicates, focus on JavaScript. Time has changed and JavaScript isn’t just JavaScript anymore, so it is always interesting to see a schedule with so diverse and broad topics.</p>
<p>The location was very hip, Magasin 9, a huge harbor building, that they had transformed into a large area with lots of activities for the attendants such as saunas, bathtubs, VR, board games, Silent Disco, plenty of interactive experiences, a barber and even free tattoos! They really wanted the conference to be fun and playful, emphasized by the selection of two very colorful hosts.</p>
<p><img src="/assets/img/articles/2019-12-03-NordicJS-2019/nordic-overview.webp" alt="Nordic.js conference"></p>
<h2>Thursday</h2>
<p><em>First conference day out of two.</em></p>
<h3>David Khourshid - Mind-Reading with Adaptive and Intelligent User Interfaces</h3>
<p>David Khoursid is a software engineer for Microsoft and he did an exciting talk on Adaptive and Intelligent User Interfaces. He used javascript library Guess.js to demonstrate how to integrate elements of machine learning in order to build UI that predicts what the user is about to do.</p>
<p>Based on collecting data over time it’s possible to e.g shortcut actions for the user, and the goal for this could be to give the user a more streamlined and efficient experience with an app or website (or other types of interfaces).</p>
<p>He is advocating the concept of Model Driven Development that works with finite state machines, hierarchical states and state charts that maps out states and events, giving possibilities upon to predict the next state or event in the user flow. He showed how a library of his creation - XState - that can be used for controlling these flows. At the end of the talk we saw a simple code example on how to "train" a form with a feedback button to shortcut the flow and predict what the user is going to do, based on his former actions. He did an interesting and engaging talk - but it was also clear that it takes a lot of work and deep dive analysis to develop like this - to make your applications adaptive to the users behaviour and choices.</p>
<h3>Mark Volkmann - Svelte</h3>
<p>A new javascript framework is on the rise! <a href="https://svelte.dev/">Svelte</a> is the name of the latest approach to build web apps. It has a minified syntax, a Moustache like templating system and built in reactivity (no need to set up complicated state bindings). Each component has its own file that contains all necessary HTML, CSS, JavaScript and special Svelte syntax JavaScript. It all compiles to native JavaScript in bundles that are much smaller than other frameworks.</p>
<p>Seeing it demonstrated was a mixed experience. While it did seem fast and easy to write a component, it also looked a bit like a step backwards with the almost Moustache like templating concept. Sass and TypeScript is not yet supported out of the box, though it seems there’s some hack available for making those tools work.</p>
<p>We like new things and ideas, but even though there are definitely some interesting features, we all felt a little sceptic at Svelte. Mostly because it was difficult to see how this could be a wise choice for building medium to larger corporate size apps and websites with a higher complexity than your average JavaScript demonstration go to example, the To-Do-List!</p>
<p>At the same time Svelte does seem to gain some traction, so we will probably keep an eye on its development.</p>
<h3>Isabela Moreira - Localization</h3>
<p>As default we are using localization in our web and mobile apps, but you can always learn something new, so it was interesting to hear a talk about localization and what learnings the speaker had made.</p>
<p>The speaker, Isabela Moreira, made good points about what is important to keep in mind when building apps that needs to work in more languages across the global, for example bidirectional text, unicode, and string concatenation, so having hardcoded strings makes it very difficult to have a dynamic UI. A good point about string concatenation was that trying to do it manually is a bad idea, as language are very different. For example consider the string: "No new messages", here you could be tempted to make the translation string like: "No new {{type}}" and then pass in the type, but in other languages the adjective "new" might change based on the noun, so it is always recommended to translate as a single unit.</p>
<p>She showed us a way to setup the infrastructure in a React App using a localization library called <a href="https://github.com/formatjs/react-intl">react-intl</a> which will handle the localization and allows you to do more advanced formatting like pluralization, numerical values, currencies, and interpolation.</p>
<p>At Nodes we have been using <a href="https://react.i18next.com/">react-i18next</a> which is another popular localization tool.</p>
<h3>Godfrey Chan - Thinking in TypeScript</h3>
<p>At Nodes we recently took a decision to move into the world of TypeScript, so we were very happy to hear that there were two TypeScript focused presentations. Godfrey Chan gave a very good introduction to TypeScript, with multiple examples that showed how you get better feedback while writing code, but also how it improve documentation, readability and maintainability.</p>
<p>As an example, in JavaScript you are allowed to change a variable to any type you want:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">let</span> value = <span class="hljs-string">"hello"</span>;
value = <span class="hljs-number">2</span>;
value = <span class="hljs-literal">false</span>;
</code></pre>
<p>The editor and the browser will not complain about this, so it requires the developers to be super careful as you can’t rely on variables being a certain type. But in TypeScript, you have the opportunity to define that variable has to be a certain type and if it is changed to something else, then you will get an error message before it is compiled, hence prevent errors in the browser.</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">let</span> value = <span class="hljs-string">"hello"</span>;
value = <span class="hljs-number">2</span>; <span class="hljs-comment">// error: Type 'number' is not assignable to type 'string'</span>
</code></pre>
<p>In relation to documentation, Godfrey made a really good point about how defining types actually makes it easy to understand code that somebody else have written. Let’s look at an example:</p>
<pre><code class="hljs language-tsx"><span class="hljs-keyword">function</span> <span class="hljs-title function_">greetingsFor</span>(<span class="hljs-params">name: <span class="hljs-built_in">string</span></span>): <span class="hljs-built_in">string</span> {
  <span class="hljs-keyword">return</span> <span class="hljs-string">`hello, <span class="hljs-subst">${name}</span>!`</span>;
}
</code></pre>
<p>Here we can easily read from just the first line that the function expects a parameter <code>name</code> of the type <code>string</code>, and that the function will return a <code>string</code>. If that information was not there we would have to go into the function and actually see how the parameter was being used to make some assumption about what type it is, and what the function eventually will return. Potentially a lot of guesswork and crossing fingers, but with TypeScript you will immediately get feedback when calling the function as seen below:</p>
<pre><code class="hljs language-tsx"><span class="hljs-title function_">greetingsFor</span>(<span class="hljs-number">5</span>); <span class="hljs-comment">// error: Type 'number' is not assignable to type 'string'</span>
</code></pre>
<p>In conclusion a very good introduction to the basics of TypeScript.</p>
<h3>Robert Zhu - Full Stack Type Safety with React, GraphQL, and TypeScript</h3>
<p>Followed right after the introduction to TypeScript, was a more in depth talk by Robert Zhu about how one can build an advanced type-safe web app with React, GraphQL, and TypeScript. In his talk he walked us through an implementation of the classic ToDo list app, but taken to the next level by using GraphQL and TypeScript. A very interesting part was the library <a href="https://typegraphql.ml/">TypeGraphQL</a> that links TypeScript and GraphQL very nicely together as you only need to define the schema once and then you will get that autocomplete magic.</p>
<h2>Friday</h2>
<p><em>Second and last conference day.</em></p>
<h3>Rachel Andrew - Refactoring (the way we talk about) CSS</h3>
<p>Rachel Andrew is part of the CSS working group and talked about some of the future parts of CSS, that affects the way CSS is used for building layout systems. We learned that there’s gonna come some changes in the way we have to think about layouts. Where we normally think in vertical and horizontal dimensions or in the flex-box Axis dimensions, we will get a new concept of a Block-dimension and an Inline-dimension, that are independent of Axis, along with different writing modes, that can shake things up.</p>
<p>As an example, a normal block-element (being in a block formatting context) will have a default writing-mode of horizontal and will fill the screen in a horizontal axis (block axis). By changing the writing mode to vertical the box will change to inline formatting context and now fill the screen vertically.</p>
<p>These concepts supplemented with many new CSS properties aim to make it more intuitive as a developer to handle flow of content blocks, distribute space around items and handling appropriate heights and widths of container elements. In short the new specs will describe a more robust and versatile system to build layout with.
Browsers are not yet supporting many of the specs, but in her view, we should all start learning these specs today, and as a sidepoint stop teaching old specs to beginners and let them start fresh with the new specs.</p>
<h3>Piérre Reimertz - Gettin’ into the (Tensor)Flow</h3>
<p>Very funny session about machine learning and the use of TensorFlow, an open source machine learning platform. The speaker Piérre Reimertz was quite new to the subject but made a very entertaining speak with a 30 minutes hands-on demo.
With a ukulele in hand he trained his laptop to predict the coming tones based on the tones he was actually playing. It definitely inspired us to not worry too much when you want to try out a new technology. Just trying out new libraries and technologies can broaden your horizons, and maybe one day you will need it for a real project.</p>
<h3>Talia Nassi - Testing in Production</h3>
<p>Interesting speak from Talia Nassi, Test Engineer from WeWork. She talked about the fear of testing in production and why we should actually try it out. Her point was that Test Environments usually are not updated with newest content and configurations, which make testing hard to do and make them unreliable.</p>
<p>Her approach was to expose new features and updates only to a selected group of users, hidden by a flag, login or secret URLs. Talia shared an interesting tool called <a href="https://www.split.io/">Split.io</a> that allows you to do this in your app, so we will definitely check it out.</p>
<p>We all found it very interesting but also hard to maintain in the real world. Especially when you are a large team of developers with a lot of dependent components across the site. However, we have also experienced that different parts did not behave the same way in testing and production environments, so maybe it could be an interesting approach to test out on a project.</p>
<p><img src="/assets/img/articles/2019-12-03-NordicJS-2019/nordic-testing.webp" alt="Testing in Production"></p>
<h3>Katie Fenn - Memory: Don't Forget to Take Out the Garbage</h3>
<p>It is not very often that you stop up and peak into the memory tab in the browser to investigate how much memory your application is using as we have automatic garbage collection for memory management. But from time to time, we do get memory leaks, which can be caused by several things. Katie Fenn took us on a journey into the deep dark corners of memory management with the talk: "Memory: Don't Forget to Take Out the Garbage". Katie showed some of the common mistakes one might make and how to address the issue. A very good point was made on scoping variables within functions, because creating global variables will not be wiped by the garbage collector.</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">repeat</span>(<span class="hljs-params"></span>) {
  results = []; <span class="hljs-comment">// ← global</span>
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &#x3C; <span class="hljs-number">1000</span>; i++) {
    results.<span class="hljs-title function_">push</span>(i);
  }
}
</code></pre>
<p>Using a scoped variable instead would allow the memory to be reclaimed when the function is done executing:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">repeat</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">var</span> results = [];
  <span class="hljs-comment">// OR</span>
  <span class="hljs-keyword">let</span> counts = [];
</code></pre>
<p>Using <code>const</code> is also another way to tell the browser engine that it can do any optimizations it wants as the basic value will not be changed.</p>
<p>Another common pitfall is forgetting to unsubscribe event listeners or clearing intervals, so the browser never frees up the memory and keeps unnecessary data. Finally, Katie demonstrated how the powerful devtools in the browser can easily profile the web app and investigate the memory management.</p>
<h3>Vitaly Friedman - Designing and Building With Privacy In Mind</h3>
<p>It was very interesting to see that Vitaly Friedman was on the program of Nordic.js, he is the co-founder and editor-in-chief of Smashing Magazine, and has given many inspiring talks and workshops.</p>
<p>In this talk, Vitaly focused on exploring the importance of privacy, and how we should design and build applications with a different mindset that what we see now. We all see the annoying GDPR cookie banners, push notifications, video autoplays, ads, pop-ups, but does it really have to be that way.</p>
<p>Vitaly shared some interesting points on Terms and Conditions, that we are basically giving away data left and right when ever we are accepting cookies, but there is actually a service that can help us understand what our rights are: <a href="https://tosdr.org/">https://tosdr.org/</a>.</p>
<p>Instead of tricking users into accepting cookies and whatnot, then we should not only be implementing privacy by design, but also allowing the user to better understand what the data is used and allow users to use the core functionality without tracking.</p>
<p><img src="/assets/img/articles/2019-12-03-NordicJS-2019/nordic-vitaly.webp" alt="Vitaly Friedman"></p>
<h3>Code in the Dark</h3>
<p>Awesome event! Who can code an HTML page without previewing the result?</p>
<p>The rules are simple</p>
<ul>
<li>You have 15 minutes</li>
<li>Everyone uses the same Code in the Dark editor</li>
<li>In the beginning of the competition you will get a simple design and the required assets</li>
<li>No previewing of the result</li>
<li>The audience can follow your typing on large screens</li>
<li>When the whistle blows the audience vote for a winner, the best implementation wins</li>
</ul>
<p>Around 30 Frontend Developers participated for this event. First 2 semi final heats with 10 participants, then the final with 2 winners from each heat. To put the stress levels to a maximum, loud techno-music and smoke machines were blasting out, but the participants were fully focused and typing away.</p>
<p>In the final the participants had to implement the Slack logo – a nice challenge that can be solved in many ways. Finally, the winner was found, he did a good attempt on the Slack logo and he got an enormous trophy and lots of credit from a very excited audience.</p>
<p><img src="/assets/img/articles/2019-12-03-NordicJS-2019/nordic-code-dark.webp" alt="Code in the Dark"></p>
<h2>Awesome and fun conference!</h2>
<p>We would like to thank <a href="https://nordicjs.com/2019">Nordic.js</a> for arranging a really good conference! It has been an interesting couple of days with excellent talks, fun new libraries, and some learnings that we can definitely take with us home to improve our daily programming.</p>
<p>We did enjoy the diverse JavaScript conference with more general talks, but we would prefer longer talks as 30 minutes only allowed the speaker to give a brief talk instead of going more in depth and provide even more valuable information.</p>
<p>For next time, we will try to attend a more ReactJS specific conference to see if we can get even more applicable knowledge as well as joining a workshop where we can get hands-on experience with new libraries and technologies.</p>
<p><img src="/assets/img/articles/2019-12-03-NordicJS-2019/nordic-team.webp" alt="Nodes Team"></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[European Women In Technology 2019]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/12/02/european-women-in-tech-2019</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/12/02/european-women-in-tech-2019</guid>
            <pubDate>Mon, 02 Dec 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>European Women In Technology 2019</h2>
<p>European Women in Technology is the largest conference by women for women in the industry in Europe. It was two days in Amsterdam filled with talks, workshops, mentoring opportunities and networking. With 7 tracks and 6 workshops in parallel during most of the days garnished with minor pop-up events.<br>
While there's opportunities to get introduced to new and emerging technologies, the main focus of the conference is networking, how to navigate a man dominated industry as a woman, tips and tricks to elevate your career, and finally how to bring more diversity into our teams. Around 95 % of the attendees are women, but men are also welcome to join.<br>
<a href="https://www.europeanwomenintech.com/">You can read more about the conference here.</a></p>
<h2>Opening Keynote: Fireside chat on the tech landscape in Europe</h2>
<p>By Shaloo Garg (Managing Director, UN Women, Microsoft for Start-ups), Nicole Iseppi (Managing Director, Operations and Performance, Global Industrial Hub) and Ödgärd Andersson (Volvo).<br>
Some of the key points from the discussion was the 3D's of the next decade; <em>decentralization, decarbonization and digitalization</em>. In the words of Nicole Iseppi, "We need to build our companies so they are open to world and open to the new opportunities and technologies that the future will bring."<br>
Ödgärd brought forward the exciting evolution and innovation we see in China and questions if the human needs are universal, or if we will see a split in needs and desires in the future. She feared that Europe may lack back in the future due to our lack of mobility compared to the rest of the world.<br>
Shaloo talked a lot about some of the most evolving markets she's experienced in the last few years. Especially Africa and the Middle East are becoming innovative tech hubs, with many start-ups and a lot of talent waiting to take on the world. She also emphasised that ESG (Environmental, Social and corporate governance) is gaining focus everywhere and if companies aren't already focussing on it they will in the next few years.</p>
<h2>The two faces of tolerance</h2>
<p>By Evita Stoop (Chief Marketing Officer, IBM Benelux).<br>
How do we handle a lone red tulip among a field of yellow ones? Do we try to make it yellow, do we tolerate it's presence, or do we embrace and cherish it?<br>
Evita talked about the opposite meanings of tolerance and to tolerate. It is this paradox that highlights the difference between just creating a diverse team and truly embracing it.</p>
<h2>Workshop: An introduction to serverless, with examples featuring Serena Williams</h2>
<p>By Sara Gerion (Software engineer at DAZN).<br>
Sara guided us through the process of setting up our first serverless service on AWS using CloudFormation. She had prepared the yaml files to get started and then walked up through setting up and deploy a simple Lambda function using Node.js. Sara explained some of the benefits of serveless such as if you have functions that only need to run once in a while and that it can be cost efficient in certain cases.</p>
<h2>Workshop: Building a train type classifier from vibration data</h2>
<p>By Ole Tommy Vorren (Machine Learning Engineer at Konnux).<br>
Ole had prepared a neural network to identify train types via real life vibrational data. After walking us through the project we got our hands dirty tweaking the parameters in the model to get the most accurate model.</p>
<h2>Workshop: Introduction to continuous integration / continuous delivery</h2>
<p>By Giulia Di Rienzo (IT Architect at the European Central Bank).<br>
Giulia Di Rienzo explained the basics of CI/CD. She had prepared a project using GitLabs build in CI tool, where we had to first make a simple change to the project working as described in the principles in GitFlow. Second we had to update the settings for our CI tool to also measure the code coverage of the mock project, which wasn't impressive 😉</p>
<h2>Day 2 Opening Address</h2>
<p>By Helle Thorning Schmidt (CEO of Save The Children International and former Prime Minister of Denmark).<br>
Helle gave a strong talk on being a woman in a power. She emphasised that leadership requires courage and it requires that you are willing to step out of your comfort zone into the unknown. She shared some strong anecdotes from her time as both a politician and as CEO in a diverse company full of experts from all over the world. Her final message to us was to "become friends with our inner nervousness and harness it as a power".</p>
<h2>The bionic company - what does the company of the future look like?</h2>
<p>By Karalee Close (Managing Director and Partner at Boston Consulting Group).<br>
Karalee gave an engaging talk on the future of tech and described what BCG has defined as "the bionic company". DigitalBCG believes that the companies of the future needs to be able to harness the power of technology and humanity in a unison. They need to transform their organization from a "know-it-all" to a "learn-it-all" organization. She described, how the bionic company thinks and moves data in an agile fashion, where the infrastructure is becoming more modular. The modular structure allows the company to move quickly in alignment with new techniques. Her final message was that the future will require a greater focus on emotional intelligence and creatively as the technology will take over the more tedious task for us.</p>
<h2>The changing face of STEM: Breaking down stereotypes and exceeding expectations</h2>
<p>By Corinne Vigreux (Co-founder of TomTom) and Dr. Anne-Marie Imafidon (Co-Founder, CEO and Head Stemmate of STEMettes).
The main points from the discussion is that we need to take a look at our existing education system, because it caters to certain stereotypes around gender and topics, which we will need to break down order to feed the future demands for highly skilled technical people with a diverse background. Dr. Anne-Marie explained how the schools in England already introduce code and computation thinking when the children are five years old, but that it is extremely important that the infrastructure are prepared for it and that the teachers get the proper education first. They also pointed out the need for another way to evaluate the children so softer skills also get evaluated and the children learn to be confident enough that they dare be creative and make mistakes.</p>
<h2>Workshop: Five ways to boost your SCRUM</h2>
<p>By Evelien Roos (Agile Coach at Xebia).<br>
The workshop was a basic introduction to SCRUM. It was an active workshop where we had to discuss our experiences with SCRUM in our company.<br>
The four key messages from the workshop were:</p>
<ol>
<li>Find a good scrum master</li>
<li>Get leadership support</li>
<li>Remember the scrum values</li>
<li>Mix up the exercises in the meetings to keep people engaged</li>
</ol>
<h2>Expose cyber-attacks a step ahead with predictive intelligence and hunting</h2>
<p>By Rini Icent Gopinath (Senior Information Security Expert at ABN AMRO).<br>
Rini explained the lifecycle of a cyber attack and some of the things you can do to protect your network. Did you e.g. know that it takes an average of 206 days to detect a data breach?
She walked us through how she hunts for threats as part of her job as a security expert.</p>
<h2>Blockchain for beginners: Understanding the how and why</h2>
<p>By Sanne Visser (Blockchain Tester and Managing Consulting at Capgemini).<br>
Sanne explained how the blockchains work garnished with funny stories of how she and her husband got into it in the first place. She walked us through how the blockchain is a series of hashes and that the big issue, especially with bitcoin, is that each link has to be verified, which makes it a very slow process. She finished of the talk by giving six points that you could consider before going into blockchain:</p>
<ol>
<li>Do I need blockchain?</li>
<li>Do I trust my partners?</li>
<li>How will we govern it?</li>
<li>How will it be secured?</li>
<li>Can it scale up?</li>
<li>Is the ecosystem a problem?</li>
</ol>
<h2>Netflix Open Connect, delivering internet tv to the world</h2>
<p>By Nina Bargisen (Director at Open Connect Partner Engagement, Netflix).<br>
An interesting break down of Netflix's in house content distribution network, Open Connect. Nina described how Netflix handles their load balance across countries by distributing popular content on their network of conventional and flash servers. She illustrated how Netflix measures the activity on their servers to predict what people want to watch the following day and when in the day they should update the content on their servers.</p>
<h2>Final words</h2>
<p>European Women in Technology is a very different tech conference compared to any ones we have ever attended before. The focus are much more on the human side of the industry and it takes some time to get familiar with. But that doesn't change the fact that this conference was a very inspiring one. It was a great reminder to sit in a room with 1000's of other women from the tech industries and be reminded that we are not the only ones, and it was great to see, hear and network with so many accomplished women from our industry.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Php Conference - Barcelona 2019]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/11/12/Php_Conference_Barcelona</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/11/12/Php_Conference_Barcelona</guid>
            <pubDate>Tue, 12 Nov 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>On 12t-13th of November 2019, <a href="https://php.barcelona/">Barcelona hosted the annual Php Conference</a>. So many inspiring speakers (Rasmus Lerdorf - the creator of Php Language opened the event) with plenty of interesting topics. There is no justice trying to resume their talks but just some key notes could raise your curiosity and you can go deeper following their attached slides/videos. Enjoy!</p>
<h2>Php - what will bring in the future</h2>
<p>by <a href="https://twitter.com/rasmus">Rasmus Lerdorf</a></p>
<ul>
<li>V.7.4 (null coalescing assignment operator - "??="; typed properties)</li>
<li>V.8 (the main feature will be the introduction of JIT - Just in time - compilation)</li>
<li>Struggles (strong types can significantly slow down the app if they are abused - still not possible in arrays; Php by itself will probably never not be a proper player in the multithreading world)</li>
</ul>
<h2>Evolving from Helpers to Middleware</h2>
<p>by <a href="https://twitter.com/Ocramius">Marco Pivetta</a> -
<a href="https://www.youtube.com/watch?v=v1I57-_Rsv0">video</a></p>
<ul>
<li>Why/how we evolved from Globals, helpers - global functions - to MVC, proper validations, Testing, Dependency injection, ORMs, DDD, Middlewares</li>
</ul>
<h2>Microservices</h2>
<p>by <a href="https://twitter.com/ircmaxell">Anthony Ferrara</a> - <a href="https://www.softwaretalks.io/v/7268/microservices-gone-wrong-anthony-ferrara-php-uk-conference">video</a>
and
by <a href="https://twitter.com/manelselles">Manel Selles</a></p>
<ul>
<li>How they can go really wrong with a poor architecture - for ex. creating unnecessarily small microservices</li>
<li>The importance of having an automation team while developing microservices (for deployment, migration, backups, etc)</li>
<li>Test failure paths and end to end integration tests</li>
<li>Clear definition of business objectives as early as possible</li>
<li>Monitoring: telemetry, metrics</li>
<li>Importance of Elastic search</li>
</ul>
<h2>Serverless php app with Bref on top of AWS Lambda</h2>
<p>by <a href="https://twitter.com/matthieunapoli">Matthieu Napoli</a> - <a href="https://vimeo.com/345897780">video</a></p>
<ul>
<li>Even though AWS Lambda doesn’t support to run php code, Bref developed libraries that can facilitate this.</li>
<li>Advantages of having function as a service (Pay only for the code you run - cheap; Don’t have to bother about scalability; Great performance with the right associated memory size option - advisable over 128MB of memory)</li>
</ul>
<h2>JWT’s</h2>
<p>by <a href="https://twitter.com/sambego">Sam Bellen</a></p>
<ul>
<li>Cookies vs token based authentication</li>
<li>Types of tokens in authentication (Access - gives access to different server resources; ID - end user authentication; Refresh - used to generate additional access tokens)</li>
<li>JWT structure (Head, Payload, Signature)</li>
<li>OAuth protocol</li>
<li>Cachable php apps</li>
</ul>
<h2>Php performance</h2>
<p>by <a href="https://twitter.com/nikita_ppv">Nikita Popov</a> - <a href="https://www.slideshare.net/nikita_ppv/php-performance-trivia">slides</a></p>
<ul>
<li>Opcache vs ACPU</li>
<li>Arrays vs Objects memory usage</li>
<li>Garbage Collection</li>
<li>Strong types can improve but also slow down - depending on the case and use</li>
</ul>
<h2>GoLang - an alternative to Php</h2>
<p>by <a href="https://twitter.com/kasiazien">Kat Zień</a> - <a href="https://github.com/katzien/talks/blob/master/get-going-with-a-new-language/phpbarcelona-2019-11-12/slides.pdf">slides</a></p>
<ul>
<li>Concurrency and parallelism - much stronger and real in Go</li>
<li>Differences and similarities between Go and PHP</li>
<li>Cool tools that implicitly come along with Go (Formatting, Documenting, Testing, Running)</li>
</ul>
<h2>Advanced Web application architecture</h2>
<p>by <a href="https://matthiasnoback.nl/">Mathias Noback</a> -
<a href="https://www.slideshare.net/matthiasnoback/advanced-web-application-architecture-php-barcelona">slides</a></p>
<ul>
<li>Importance of layers</li>
<li>Ports and adaptors, etc</li>
</ul>
<h2>Webhooks</h2>
<p>by <a href="https://lornajane.net/">Lorna Mitchell</a> - <a href="https://noti.st/lornajane/IxSlX9#sfdxDNC">slides</a></p>
<ul>
<li>Github webhooks - live example of how great webhooks work</li>
<li>API model vs webhooks</li>
<li>Ngrok - great tool to test webhooks</li>
<li>Webhooks - Queues - Workers</li>
</ul>
<h2>ReactPhp &#x26; Swoole - ways to make performance in an async/event driven world</h2>
<p>by <a href="https://twitter.com/acasademont">Albert Casademont</a>
and by <a href="https://www.zimuel.it/">Enrico Zimuel</a> - <a href="https://www.youtube.com/watch?v=2AzQ7lDZpyU">video</a></p>
<h2>Zend engine stages</h2>
<p>by <a href="https://twitter.com/derickr">Derick Rethans</a> - <a href="https://derickrethans.nl/talks/jump-bcn19.pdf">slides</a></p>
<ul>
<li>Parse the scripts</li>
<li>Create logical representation</li>
<li>Create executable code</li>
<li>Convert AST into opcodes (bytecode) - similar to Assembler</li>
<li>Execute bytecode</li>
</ul>
<h2>Mutation testing</h2>
<p>by <a href="https://twitter.com/tfidry">Théo Fidry</a> - <a href="https://www.youtube.com/watch?v=dlVASJ-MbUE">video</a></p>
<ul>
<li>Infection tool</li>
<li>Slow but can be improved</li>
</ul>
<hr>
<p>There were two packed days, hosted in Gaudi's beautiful Barcelona, with so many passionate people, great vibes and experiences. It's great to stay connected to the last trends and see how quickly this world is spinning!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Nodes x Hacktober Fest 2019]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/11/11/Nodes-Hacktoberfest-2019</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/11/11/Nodes-Hacktoberfest-2019</guid>
            <pubDate>Mon, 11 Nov 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Getting started with Open Source projects can be daunting some times, but we hope that our local <a href="https://hacktoberfest.digitalocean.com/">Hacktober Fest</a> events inspired people to get more involved in this community. We organised 5 events across 5 different locations, all with their own personal vibes and here is a short recap of what happened.</p>
<h2>Aarhus</h2>
<p>The Nodes Aarhus office kickstarted the month with a cozy gathering at the ORBIT Lab space - a local creative and inspirational hub that offers visitors the most recent technologies in ICT to play with. A small dedicated group of people showed up, and we served pizza and cold drinks. People networked, talked about being developers and Open Source in general and after the introduction talk by Nicolai Harbø (iOS and Android Developer at Nodes) the conversation continued.
<img src="/assets/img/articles/2019-11-11-Nodes-Hacktoberfest-2019/Aarhus.webp" alt=""></p>
<h2>Dubai</h2>
<p>Many different backgrounds attended the local Hacktober Fest event in Dubai: teachers, students, freelancers and developers working at companies here in Dubai and outside of it. Roxana Jula (Associate Senior Mobile Developer at Nodes) gave a talk about Open Source and how to get involved. Pawel Wilk (Senior Backend Developer at Nodes) talked about his experiences of contributing to Open Source projects with a nice list of projects he helped out like Drupal 6 or CKEditor. Everybody loved the Open Mic idea, where guests could get on the “stage” and present cool projects they own or maintain. Gonçalo Montes Palma (Flutter Developer at Ianum) showed us a very cool project that notifies you about close-by Portuguese wildfires.
<img src="/assets/img/articles/2019-11-11-Nodes-Hacktoberfest-2019/Dubai.webp" alt=""></p>
<h2>Berlin</h2>
<p>We were very happy to host our Berlin Hacktober Fest event in the cosy lounge at rent24. Matko Smoljan (Senior Android Developer at Nodes) introduced the audience to what is Hacktober Fest, Open Source and free (as in freedom) software and how people can make their own contributions. Jonas from the Vapor core team talked about how you can develop a business around an open-source product, using Vapor itself as a great example of doing so. All of this was accompanied by amazing food prepared by Grazyna Lasek.
<img src="/assets/img/articles/2019-11-11-Nodes-Hacktoberfest-2019/Berlin.webp" alt=""></p>
<h2>Copenhagen</h2>
<p>Copenhagen office followed up with a nice event with mostly developers, but there were three middle-school guys and a teacher as well. Steffen Sommer (Technical Director and Solution Architect at Nodes) had the first presentation, talking about Open Source Software, Nodes and how OSS is used and contributed to by Nodes. The external speaker was Kenneth Geisshirt (Senior Engineer at MongoDB). Being in the software industry for a respectful amount of time, Kenneth shared his experience with OSS, how to make meaningful contributions and how to make the best of OSS. Before and in between the presentations, the guests got to know each other a little better and to exchange their experience with OSS, but also to find out more about Nodes.
<img src="/assets/img/articles/2019-11-11-Nodes-Hacktoberfest-2019/Copenhagen.webp" alt=""></p>
<h2>Prague</h2>
<p>The Prague Hacktober Fest was attended mainly for developers from different backgrounds: mobile, frontends and specially by backend developers. The most important part for sure was the networking, people really enjoyed exchanging experiences and talking about how they work while enjoying some pizza and beers. During the open mic, a 14 year old showed his own project <a href="http://uni.hys.cz/">Interclip</a>: a url shortener with an open API that generated a code instead of a url.
<img src="/assets/img/articles/2019-11-11-Nodes-Hacktoberfest-2019/Prague.webp" alt=""></p>
<hr>
<p><a href="https://opensource.guide/">Open source is changing the world – one pull request at a time!</a>
<a href="https://www.nodesagency.com/">#EngineeringAwesome</a></p>
<p>We hope everybody had a fun Hacktober Fest!
Special thanks to everybody who contributed by organising these events and writing this article!</p>
<p><strong>Nodes iOS Open Source Projects</strong></p>
<ul>
<li><a href="https://github.com/nodes-ios/Rye">Rye</a></li>
<li><a href="https://github.com/nodes-ios/Drawer">Drawer</a></li>
<li><a href="https://github.com/nodes-ios/KeyboardHelper">KeyboardHelper</a></li>
<li><a href="https://github.com/nodes-ios/Spinner">Spinner</a></li>
<li><a href="https://github.com/nodes-ios/NStackSDK">NStack</a></li>
<li><a href="https://github.com/nodes-ios/Serpent">Serpent</a></li>
</ul>
<p><strong>Nodes Android Open Source Projects</strong></p>
<ul>
<li><a href="https://github.com/nodes-android/nstack-kotlin">Nstack</a></li>
<li><a href="https://github.com/nodes-android/kotlin-template">Template</a></li>
<li><a href="https://github.com/nodes-android/filepicker">Filepicker</a></li>
<li><a href="https://github.com/nodes-android/locksmith">Locksmith</a></li>
<li><a href="https://github.com/nodes-android/form-validator">Form-Validator</a></li>
</ul>
<p><strong>Nodes Vapor Open Source Projects</strong></p>
<ul>
<li><a href="https://github.com/nodes-vapor/admin-panel">Admin Panel</a></li>
<li><a href="https://github.com/nodes-vapor/jwt-keychain">JWTKeyChain</a></li>
<li><a href="https://github.com/nodes-vapor/paginator">Paginator</a></li>
<li><a href="https://github.com/nodes-vapor/reset">Reset</a></li>
<li><a href="https://github.com/nodes-vapor/bugsnag">Bugsnag</a></li>
</ul>
<p><strong>Other open Source projects mentioned</strong></p>
<ul>
<li><a href="https://github.com/vapor/vapor">Vapor</a></li>
<li><a href="https://github.com/FogosPT/fogosmobile/">Fogosmobile</a></li>
<li><a href="https://github.com/vostpt/mobile-app/">VostPT App</a></li>
<li><a href="https://github.com/aperta-principium/Interclip">Interclip</a></li>
</ul>
<p><strong>Thanks for hosting us</strong></p>
<ul>
<li><a href="https://astrolabs.com/">Astrolabs</a></li>
<li><a href="https://orbitlab.au.dk/">OrbitLab</a></li>
<li><a href="https://www.rent24.com/">Rent24</a></li>
<li><a href="https://www.hubpraha.cz/en/k10/">Impact Hub Prague K10</a></li>
</ul>
<p><strong>Be the first to hear about our upcoming local events</strong></p>
<ul>
<li><a href="https://www.meetup.com/Nodes-Dubai">Dubai Meetup Group</a></li>
<li><a href="https://www.meetup.com/Nodes-Copenhagen">Copenhagen Meetup Group</a></li>
<li><a href="https://www.meetup.com/meetup-group-WFRQWdmf">Aarhus Meetup Group</a></li>
<li><a href="https://www.meetup.com/nodes-events">Berlin Meetup Group</a></li>
<li><a href="https://www.meetup.com/Nodes-Prague">Prague Meetup Group</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[SwiftGen]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/11/11/SwiftGen</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/11/11/SwiftGen</guid>
            <pubDate>Mon, 11 Nov 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>SwiftGen is a tool to auto-generate Swift code for resources of your projects, to make them type-safe. You can organize and manage your resources by using auto-generated swift code.</p>
<p>You can use SwiftGen for assets, colors, core data, fonts, interface builder files, JSON &#x26; YAML files, plists and localizable strings.</p>
<h2>Installation</h2>
<p>There are many ways of installing SwiftGen with project. I'll use CocoaPods.</p>
<ol>
<li>
<p>Open terminal, navigate to your project and run <strong>pod init</strong>.</p>
</li>
<li>
<p>Add <strong>pod 'SwiftGen'</strong> to your pod file.</p>
</li>
<li>
<p>Run <strong>pod install</strong>.</p>
</li>
<li>
<p>To invoke SwiftGen, go to Build Phases -> Add New Run Script Phase and add</p>
<pre><code class="hljs language-bash"><span class="hljs-variable">${PODS_ROOT}</span>/SwiftGen/bin/swiftgen[]()
</code></pre>
<p>Everytime we build, SwiftGen is going to run and update all resources.</p>
</li>
</ol>
<h2>Configuration file</h2>
<ul>
<li>Create <strong>swiftgen.yml</strong> configuration file.</li>
</ul>
<p><img src="/assets/img/articles/2019-11-11-SwiftGen/addSwiftGen.webp" alt=""></p>
<ul>
<li>
<p>You can write inputs and outputs for the resources for which you want to generate swift code. Each action (xcassets, colors, fonts) in configuration file will generate constants for that type of input file. Inside output option templateName, define the Stencil template to use <a href="https://github.com/SwiftGen/SwiftGen/tree/master/Documentation/templates">See more info here</a> to generate the output.</p>
<p>Let's see example for images, colors and fonts. Add the below lines of code in swiftgen.yml file.</p>
<pre><code class="hljs language-yaml"><span class="hljs-string">**Images**</span>

	<span class="hljs-attr">xcassets:</span>
	    <span class="hljs-attr">inputs:</span> <span class="hljs-string">Path/Assets.xcassets</span>
	    <span class="hljs-attr">outputs:</span>
	        <span class="hljs-bullet">-</span> <span class="hljs-attr">templateName:</span> <span class="hljs-string">swift4</span>
	          <span class="hljs-attr">output:</span> <span class="hljs-string">Path/SwiftGen/assets.swift</span>

<span class="hljs-string">**Colors**</span>

	<span class="hljs-attr">colors:</span>
	    <span class="hljs-attr">inputs:</span> <span class="hljs-string">Path/CustomAppColors.txt</span>
	    <span class="hljs-attr">outputs:</span>
	        <span class="hljs-bullet">-</span> <span class="hljs-attr">templateName:</span> <span class="hljs-string">swift4</span>
	          <span class="hljs-attr">output:</span> <span class="hljs-string">Path/SwiftGen/colors.swift</span>

<span class="hljs-string">**Fonts**</span>

	<span class="hljs-attr">fonts:</span>
	    <span class="hljs-attr">inputs:</span> <span class="hljs-string">Path/Fonts</span>
	    <span class="hljs-attr">outputs:</span>
	        <span class="hljs-bullet">-</span> <span class="hljs-attr">templateName:</span> <span class="hljs-string">swift4</span>
	          <span class="hljs-attr">output:</span> <span class="hljs-string">Path/SwiftGen/fonts.swift</span>
</code></pre>
</li>
</ul>
<h2>Usage</h2>
<ol>
<li>
<p>Add some resources to input files so we can test it.</p>
<p><strong>Images</strong></p>
<p>Add some Images to Assets.xcassets. For eg: I've added image named with header.png.</p>
<p><strong>Colors</strong></p>
<p>I've created CustomAppColors.txt file and added hex color</p>
<pre><code class="hljs language-bash">lightPurple         : <span class="hljs-comment">#EBEEFF</span>
</code></pre>
<p><strong>Fonts</strong></p>
<p>Create fonts folder and add font files <code>.ttf/.otf.</code>
For eg: I've added font</p>
<pre><code class="hljs">Roboto-Regular.ttf
</code></pre>
</li>
<li>
<p>Create new group and name it <strong>SwiftGen</strong>. The generated output files will go inside this folder.</p>
</li>
<li>
<p>After building project, Click SwiftGen Folder -> Show in Finder, drag and drop auto-genetrated assets.swift, colors.swift and fonts.swift files.</p>
<p><img src="/assets/img/articles/2019-11-11-SwiftGen/autoGeneratedFiles.webp" alt=""></p>
</li>
<li>
<p>You can use generated code for images, colors and fonts like</p>
<pre><code class="hljs language-swift"><span class="hljs-operator">**</span><span class="hljs-type">Images</span><span class="hljs-operator">**</span>

	<span class="hljs-keyword">let</span> myImage <span class="hljs-operator">=</span> <span class="hljs-type">Asset</span>.header.image
	or
	<span class="hljs-keyword">let</span> myImage <span class="hljs-operator">=</span>  <span class="hljs-type">UIImage</span>(named: <span class="hljs-type">Asset</span>.header.name)

<span class="hljs-operator">**</span><span class="hljs-type">Colors</span><span class="hljs-operator">**</span>

	<span class="hljs-keyword">let</span> myColor <span class="hljs-operator">=</span> <span class="hljs-type">ColorName</span>.lightPurple.color
	or
	<span class="hljs-keyword">let</span> myColor <span class="hljs-operator">=</span> <span class="hljs-type">UIColor</span>(named: .lightPurple)

<span class="hljs-operator">**</span><span class="hljs-type">Fonts</span><span class="hljs-operator">**</span>

	<span class="hljs-keyword">let</span> myFont <span class="hljs-operator">=</span> <span class="hljs-type">FontFamily</span>.<span class="hljs-type">Roboto</span>.regular.font(size: <span class="hljs-number">20.0</span>)
	or
	<span class="hljs-keyword">let</span> myFont <span class="hljs-operator">=</span> <span class="hljs-type">UIFont</span>(font: <span class="hljs-type">FontFamily</span>.<span class="hljs-type">Roboto</span>.regular, size: <span class="hljs-number">20.0</span>)
</code></pre>
</li>
</ol>
<h2>Advantages</h2>
<ul>
<li>
<p>Avoid typos thanks to type safety.</p>
</li>
<li>
<p>Free auto-completion.</p>
</li>
<li>
<p>Avoid the risk of using an non-existing asset name.</p>
</li>
<li>
<p>All this will be ensured by the compiler.</p>
</li>
</ul>
<h2>References</h2>
<p><a href="https://github.com/SwiftGen/SwiftGen">https://github.com/SwiftGen/SwiftGen</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[NSSpain 2019]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/11/07/NSSpain-2019</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/11/07/NSSpain-2019</guid>
            <pubDate>Thu, 07 Nov 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>NSSpain 2019</h2>
<p>NSSpain is one of the most well-known iOS conferences held in Europe. This year was its seventh year, and with over three hundred participants, the tickets got sold out even before a single speaker was announced.
Taking place in Logroňo, a beautiful city situated in the northern part of Spain, I had to take a local flight from Madrid to get there. After realizing most of the passengers in the small plane are young men all wearing Apple Watches, I knew I was on the right flight :).
The typical relaxed Spanish way of life had its stamp on the conference as well. The atmosphere was very calm, and the participants friendly and always happy to start a conversation. I made a couple of new friends at the airport, ordering a shared taxi, and continued to make more during the entire conference.
The talks were divided into two days and included a wide range of topics from the vast world of iOS. On one part of the spectrum, we could listen to some in-depth technical presentations. Some topics covered the code compilation in Xcode, building your own debugger or the way programming can be applied to solving mathematical logic.
The other part gave insight into more practical iOS programming skills like asynchronous code, self-sizing cells, accessibility, unit testing, or using Swift for scripting.
A big part of the presentations was devoted to the "softer" part of development as well. The topics covered building trust in your users, green development, or career transformation from a developer to a manager.
Let's take a look into some of the topics that caught our attention.</p>
<h2>Key takeaways from developing VLC for iOS</h2>
<p><em><a href="https://vimeo.com/362126060">https://vimeo.com/362126060</a></em>
This presentation was one of the less technical but delivered some valuable insights into the parts of the development process that are not always in the primary focus of a developer. The key points were as follows.</p>
<ol>
<li>
<p>Know your licenses
Firstly released in 2010, VLC player remained in the App Store only for three months before being put down due to a GNU GPL license violation. GPL (general public license) is a free software license. It states that any derivative work must be open-source and distributed under the same license terms. Since App Store adds its terms of use, it violates this rule. And you can not safely use any GPL licensed work in your apps.</p>
</li>
<li>
<p>Know the local laws
Another shutdown from the App Store came in 2014. The reason, Dolby patent violation. The VLC team was based in France, which did not recognize software patents. However, by submitting the app to the App Store, American laws were applied, and the app was taken down again. Something the VLC team did not take into consideration.</p>
</li>
</ol>
<p>One remark worth mentioning is that during the time VLC was down from the App Store, people would actually abuse its open source license. They would put their cloned versions on the App Store and sell them to customers for money.</p>
<ol start="3">
<li>
<p>Up the bus factor
A bus factor is a number equal to the number of team members who, if run over by a bus, would put the project in jeopardy. The lower it is, the bigger the risk. In the case of VLC, it took only one key developer to leave, and the project stopped for almost a year.</p>
</li>
<li>
<p>Write the documentation and tests
After not being appropriately maintained for a year, the VLC crashes count quadrupled. The new team who came for a rescue found it extremely difficult to find the causes since there was no documentation and tests written for the code.</p>
</li>
</ol>
<h2>Under the hood the debugger</h2>
<p><em><a href="https://vimeo.com/362136409">https://vimeo.com/362136409</a></em>
This might not have been the easiest topic at the conference but gave us a deeper understanding of how debuggers on iOS/macOS work.</p>
<p>LLDB is the debugger that powers all the Xcode debugger functionality under the hood. LLDB provides the ability for reading variables, shows the frame within the backtrace. With LLDB you can set a breakpoint just as you can in Xcode. Also it provides: pauls - <code>process interrupt</code> command, resume - <code>process continue</code>, step - <code>thread step-in</code> and <code>thread step-out</code>.</p>
<p>What happens when we use Xcode to debug our app?
The first step that Xcode does is to launch the app. The next thing is to attach the debugger to the launch process and then maybe is pausing or hitting a breakpoint.</p>
<p>When we launch the application, it gets the assignment of the virtual memory map. This is an address space where the process reads and writes to and from. This is where the operating system is going to store the binary, heap, stack, and kernel space. We can read and write all over this address space except the kernel space. We have syscalls to communicate between the kernel space and the userspace. In the kernel space, we have Mach tasks. They help the debugger's process communicate with our process.
The breakpoints are just special kind of exceptions, in the Mach Task we have Exception Port which is shared between the debugged application and the debugger. In that way, the exception breakpoint is handled by the debugger.</p>
<h2>Swift life out of iOS</h2>
<p><em><a href="https://vimeo.com/362188196">https://vimeo.com/362188196</a></em>
Probably all of us have to do repetitive things in our day to day work. Not all iOS developers feel very comfortable writing bash scripts to automate these tasks. Luckily, we can also automate this repetitive work with Swift. When you write a script in a language you are more comfortable with, you will do it faster. You can also use the tools you are familiar with, such as Xcode, Apple APIs, and your favorite 3rd party frameworks.
With the <code>Process()</code> API, we can execute terminal commands; this opens many possibilities. We can use it for Git. We can use it to run other scripts written in Bash, Python, or Ruby. We can use it to open applications, also for search. We can make our Swift scripts interactive as prompting for user input with <code>readLine()</code>. It is possible to create Pipes, if you assign an instance of <code>Pipe()</code> to the <code>Process()</code>'s <code>standardOutput</code>. You can use Swift directly as a script for this purpose. You have to put as the first line of your file <code>#!/usr/bin/swift</code>, or you can compile it, which is around 100 faster if you need speed. Another option is to use swift-sh. This is a small tool that makes the use of a scripting approach with 3rd party dependencies easy.</p>
<h2>We loved this conference!</h2>
<p>Even after the talks were over for the day, the program was not. The first day a couple of buses took us all for a local winery visit. We had a chance to see the largest Europe's wine barrel storage and, of course, taste some of the fine Spanish wine. The next day was the first day of a big wine festival, and the entire city filled with people celebrating and dancing. What can be better than enjoy such an event with your iOS friends? :)</p>
<p>And the conclusion and overall impressions? We loved this conference! The organization was flawless, the talks were in-depth and informative, yet not tedious, and the people were always up for inspiring conversations.</p>
<p>If you were not lucky enough to attend, you can watch the talks here on Vimeo <a href="https://vimeo.com/nsspain">https://vimeo.com/nsspain</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Droidcon London 2019]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/11/06/Droidcon-London</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/11/06/Droidcon-London</guid>
            <pubDate>Wed, 06 Nov 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>Droidcon London 2019</h2>
<p>We had the opportunity to attend the biggest Android conference in London. It was full of great talks and we will try to highlight here some of which we attended.</p>
<h2>Quick facts</h2>
<ol>
<li>2 entire days with talks</li>
<li>Workshops available</li>
<li>5 tracks at same time during the day</li>
<li>[<a href="http://uk.droidcon.com/">http://uk.droidcon.com/</a>]</li>
</ol>
<h2>Keynote: Coding like an Athlete</h2>
<p><em>by Christina Lee</em></p>
<p>Even though programmers are the "athletes" of a company, it's not usual to take into account the developer's physical and mental health as a way to improve the programming performance. The keynote showed how professional athletes take this into account and how every small improvements helps into the performance, as well as how the science supports those facts and, doing so, how programmers can benefit doing improvements in those areas besides just studying code related stuff.</p>
<h2>Workshop: Unit Test Your Views</h2>
<p><em>by Jorge Ortiz Fuentes</em></p>
<p>This workshop was divided in 2 parts, the first one was discussing what tests are and a bit about the architecture and the second one was doing the unit tests in a sample project. It was time to do some coding and learning a bit on how important the app architecture can contribute to easiness the tests in all layers. In this case we wrote unit tests to assure the view behavior was the expected when triggering functions in the ViewModel and observing the view response to the events.</p>
<h2>Do the Loco-MotionLayout: Building animations with MotionLayout</h2>
<p><em>by Michael Scamell</em></p>
<p>This talk showed all the power that the new Google's MotionLayout is: complex animations being done in a really easy way. The talk showed a step by step on how to create an animation using multiple elements and changing properties like alpha, perspective, shadows, etc. The <code>MotionLayout</code> is still in development but now with the Motion Editor on Android Studio 4.0 it probably will start to get discovered and more used.</p>
<h2>Image Loading With Fresco: 4 Years Later</h2>
<p><em>by Alexander Oprisnik</em></p>
<p>Fresco is the open source image loading library from Facebook that is one of the very well known libraries for Android. From 4 years until now multiple things improved in the library: new features, support to newer image formats even on older devices, modularization to make it lightweight giving the user the possibility to use just what he needs and many others. One of the main concerns from the library is performance and efficient memory management as it still support old android versions: Android 2.3 (Gingerbread) and later.</p>
<h2>Reverse-Engineering apps on the device - how far can we go?</h2>
<p><em>by Jeb Ware</em></p>
<p>This talk got us surprised. It was listed on Droidcon's schedule as a <code>beginner</code> talk, which was a bit weird as usually reverse-engineering can be something really hard and complex, but the talk showed how much information any app can get just by being installed in your phone. Android's system can give, without any runtime permission, the list of installed apps on the phone and, with some tricks and help from some libraries, you can get all the assets used in others app like images, string, layout file, etc.</p>
<h2>Lightning Talk: Backup and Restore; Where are we? Where do we want to go?</h2>
<p><em>by Al Sutton</em></p>
<p>This was a quick talk about what Google offers for free, so that developers can backup some of the data in the cloud so, when the app is reinstalled, the users configs or (almost) anything else the developer want are restored saving the user time and giving a better experience and removing the need to have our own host and handling all the storage for each user. They explained that with big files and in some complex cases a server is still needed but the Google way still is a viable option for many cases.</p>
<h2>Keynote: Building An Accessible Smart Guitar For The Deaf, Blind and Mute</h2>
<p><em>by Joe Birch</em></p>
<p>Joe Birch has done something really great here. He created a guitar which can be played by deaf, blind and mute.
In this talk he explains why he started this guitar project. He wants everyone to have the same experience with an accessible guitar.
He used different types of technologies to achieve his goal such as Actions on Google, Firebase, Dialogflow and Google Cloud.</p>
<h2>First look at Jetpack Compose</h2>
<p><em>by Łukasz Wasylkowski</em></p>
<p>Android UI toolkit is 10 years old now. It doesn't give us much flexibility and it doesn't satisfy today's needs.
Jetpack Compose is brand new UI toolkit for Android development. It has similarities with frameworks like React or Flutter.
It gives the ability to developers to design UI in reactive way. Another great thing about it is all of the Compose APIs are in Kotlin.
With Android Studio 4.0 now we can see the preview of the what we design in the Android Studio! Google is moving to this new UI toolkit.
In my opinion we developers can start to play with it. It is mature enough for most of the cases. So this talk is all about Jetpack Compose what can you do with it.
If you want to have an idea about Compose this talk will be good start.</p>
<h2>Idio~~ ma~~tic Kotlin</h2>
<p><em>by Márton Braun</em></p>
<p>We developers try to always write the best code we can write, try to implement best approaches we can implement. This talk is the exactly the opposite of that. :)
Sure, Marton mentions really cool language features but do we really need them? As he mentions in his talk no we don't and he doesn't suggest us to use them either.
It was interesting and enjoyable. If you want to have fun and abuse Kotlin language feature this is the talk you should watch.</p>
<h2>Flutter for the Discerning Android Developer</h2>
<p><em>by Simon Lightfoot</em></p>
<p>Flutter is the Google's new cross-platform solution for mobile, web and desktop. As a mobile developer it is the best one comparing the other solutions.
Simon Lightfoot explains how flutter works under the hood on Android and show it is not depending too much on the platforms, as it handles business logic, UI and drawing to the screen itself.
The only thing Flutter talking with platform for camera, Bluetooth and other hardware.</p>
<h2>Keynote - Android: For Users and Developers</h2>
<p><em>by Chet Haase and Romain Guy</em></p>
<p>This keynote is from the famous Googlers and they tell the audience the about the android evaluation in the latest years from two perspectives. One of them is from user, the other one is from developers.
They tried to explain what is changed and why it is changed. It was a very really good wrap up considering that in the past couple of years, a lot have changed in the Android development. For users the operating system evolved into something more secure.
For developer it is much more easier to develop mobile applications to Android.</p>
<h2>It's a great experience!</h2>
<p>The entire event was full of great talks. It was really nice to see such a big event with people from all around the world coming to learn about Android development. This blog post tries to show a little bit of what we attended in 2 days full of talks. It's a great experience and for sure we recommend to attend any Droidcon you can.</p>
<p><em>Article Photo by <a href="https://www.flickr.com/photos/skillsmatter/48957824092/in/album-72157711476555262/">Skills Matter</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ServerSide.swift 2019]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/11/06/ServerSide-swift</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/11/06/ServerSide-swift</guid>
            <pubDate>Wed, 06 Nov 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>What it's all about</h2>
<p>As stated on their website, ServerSide.swift is aiming to be a non-profit, framework-independent conference "solely run for the love of server-side Swift". This was the second year of the conference, this time organized in Copenhagen, Denmark from Oct 30 to Nov 1.</p>
<p><img src="/assets/img/articles/2019-11-06-ServerSide-swift/conference-hall.webp" alt="conference hall"></p>
<p>Since becoming open-source, Swift's qualities have been gaining more recognition in the backend space. The head count has increased by about 40% this year, totalling in 112 attendees. While a major part of the audience still seems to stem from the Apple ecosystem, there have been a few of us coming from other areas, e.g. PHP or the Android side of the mobile fence. In the coming years I really wish for the server-side Swift community to open up even further and hope this will bring many new benefits to the platform.</p>
<h2>The venue</h2>
<p>The conference was hosted in the BLOX building housing the Danish Architectural Center (DAC) and the BLOXHUB innovation network. The workshops were held on the top floor with a beautiful view of the harbor, and the stage for the talks was backed by a massive glass window with a view of the Christian IV's brewery and the Royal Library.</p>
<p>Needless to say the location choice was excellent! The conference pass also allowed visitors to freely explore the DAC exhibition with building models by the world-renowned design studio Bjarke Ingels Group.</p>
<p><img src="/assets/img/articles/2019-11-06-ServerSide-swift/dac-exhibition.webp" alt="conference hall"></p>
<h2>Workshop Day</h2>
<p>As I'm still fairly new to Vapor and Swift, I signed up for the intrudoctory <strong>"Getting Started with Vapor"</strong> workshop by <strong>Tanner Nelson</strong> and <strong>Jonas Schwartz</strong> from the Vapor core team. During sign-up I wasn't aware we'll already be working with the upcoming Vapor 4, so besides brushing up on the basics it was also quite useful to get acquainted with the new features we can expect using in production soon. The workshop quickly covered the basics of HTTP, REST and what Vapor is good at, followed by a hands-on session where we built a simple API and deployed it to Vapor Cloud 2 (soon reaching beta).</p>
<p><img src="/assets/img/articles/2019-11-06-ServerSide-swift/getting-started-with-vapor.webp" alt="conference hall"></p>
<p>After becoming a Vapor pro in a single morning, it was time to start <strong>"Building Microservices"</strong> with <strong>Ralph Küpper</strong>. After a short introduction to microservices, an e-commerce example project was used to present one way of how they could be implemented using a mixed Vapor 3 and 4 setup. There has also been discussion on how microservices can be mixed (e.g. using different languages and frameworks), scaled and made reusable. The workshop was concluded by setting up AWS and deploying the solution.</p>
<p>The laws of physics prevented me from attending the other two workshops. However, I've spoken to several people attending <strong>"Contributing to SwiftNIO and SSWG"</strong> by <strong>Cory Benfield</strong> and <strong>Johannes Weiss</strong> and they seemed pretty excited about where their code ended up. :)</p>
<p>The other afternoon slot was reserved for <strong>"Build a cloud-native app with Kitura"</strong> by <strong>Ian Partridge</strong> where the attendees built a safety notification system for disaster cases. Seems quite interesting!</p>
<h2>The talks</h2>
<p>Most of the talks over the next two days have been great and they left a lot of impressions to take home. It would be hard to cover all of them so I'll select some of my personal highlights. However, please keep in mind the list is subjective and even omits some of the other talks I enjoyed a lot.</p>
<h2>"Static site generation in Swift" by John Sundell</h2>
<p>I probably heard and forgot about static site generators multiple times by now. While I'm still pondering on if I should rebuild or shut down my immensely outdated personal website, it was very interesting to see John's approach and self-developed tools in a cheerful presentation style.</p>
<p>Short story: write your markdown, run some Swift and get that blog up-and-running with no backend whatsoever!</p>
<p>The whole solution will soon be open-sourced as three tools. <em>Ink</em> is a markdown formatter, <em>Plot</em> is an HTML DSL and <em>Publish</em> is the static site generator.</p>
<h2>"Breaking into tech" by Heidi Hermann</h2>
<p>Okay this one's a bit biased since Heidi's sitting right across the desk as I'm writing this. Does it mean her talk was any less good? Heck, no!</p>
<p>Her speech about us all being mentors and helping others enter the tech industry was so inspiring I'm even willing to forget she used my LEGO contraption as an example for the most depressive phase of a project, career or a relationship.</p>
<p>Let's make our interview processes fair and welcoming so all candidates can show us what they can really do.</p>
<h2>"Building State Machines in Swift" by Cory Benfield</h2>
<p>This brought up some nostalgia (and a few bitter memories) from my university days. The talk was anything but bitter, however. It served as a good refresher and inspired some thoughts on utilizing state machines more often in future projects.</p>
<p><img src="/assets/img/articles/2019-11-06-ServerSide-swift/talk-state-machines.webp" alt="conference hall"></p>
<h2>"Swift development on Linux" by Jonas Schwartz</h2>
<p>Not only does Jonas deserve a place among the highlights for his talk, but he also deserves a medal for bravery since he was probably the only person with a non-Apple laptop there. :)</p>
<p>As a Linux user wanting to jump more into backend development myself, discovering that one of the Vapor core team's members is using Linux full-time gave me a lot of confidence in picking Vapor as my next framework.</p>
<p>Jonas gave a great overview of the available tools and what you should expect by choosing Linux as your primary development platform.</p>
<p>Btw, he uses Arch.</p>
<h2>"How we Vapor-ised our Mac app" by Matias Piipari</h2>
<p>Matias explained how they migrated a Mac app for scientific writing to a server-based solution. It's always interesting to see how legacy code is tackled and transitioned to something new.</p>
<p>What makes it even more interesting is that this isn't a storry with a clear happy-end. Due to AppKit and some other remaining dependencies, the solution currently has to run on macOS, meaning a transition to Linux is still not entirely possible. Hence the team had to stay pragmatic and build servers from Macs instead.</p>
<h2>"Supercharging your Web APIs with gRPC" by Daniel Alm</h2>
<p>Maybe I've been living under a rock but I haven't heard of gRPC before. Daniel gave a great overview of the benefits backed by code examples. It's definitely something I'm going to look more into.</p>
<h2>"Building high-tech robots with Swift" by Gerwin de Haan, Mathieu Barnachon</h2>
<p>How unexpected - they had me at "robots". Gerwin and Mathieu explained how StyleShoots got started and how they built multiple machines for taking professional fashion photography without a professional fashion photographer.</p>
<h2>Conclusion</h2>
<p>The conference was educative, a lot of fun and very well organized. Kudos to the whole team for the good food, great location selection and interesting content!</p>
<p>As mentioned, there have been a lot more great talks than I've been able to cover. Also worth mentioning was the panel discussion moderated by <strong>Tim Condon</strong> with <strong>Kaitlin Mahar</strong>, <strong>Siemen Sikkema</strong>, <strong>Tanner Nelson</strong> and <strong>Ian Partridge</strong>, followed by a boat ride through the Copenhagen canals and tacos at the Condesa Bar.</p>
<p><img src="/assets/img/articles/2019-11-06-ServerSide-swift/boat-ride.webp" alt="conference hall"></p>
<hr>
<p>Special thanks to <a href="/authors/siemen-sikkema">Siemen Sikkema</a> for sharing his notes!</p>
<p>Image sources:</p>
<ul>
<li>Group photo (cover): <a href="https://twitter.com/MartinLasek?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor">Martin Lasek / ServerSide.swift</a></li>
<li>Conference hall: <a href="https://twitter.com/SwiftServerConf/status/1190172067691401222">ServerSide.swift Twitter</a></li>
<li>"Getting started with Vapor": <a href="https://twitter.com/SwiftServerConf/status/1189496185435951117/photo/2">ServerSide.swift Twitter</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[NestJS, Your Angular Window to Server-side Programming]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/11/04/NestJS-Your-Angular-Window-to-Server-side-Programming</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/11/04/NestJS-Your-Angular-Window-to-Server-side-Programming</guid>
            <pubDate>Mon, 04 Nov 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>The role of a software developer is getting more open-ended with time. They are more of a “Fire Fighter Developer” if not anything else. Developers need to be constantly vigilant, doing more than what they are asked for, especially when some teams need help, they are required to step in to save the day. We look into a Node.js framework using Angular lens in this article, which will serve as a perfect gateway for frontend developers to dive into the server-side programming with existing knowledge sets.</p>
<p>The framework is not so new given the fast adoption rate of the framework. NestJS is a platform that Angular community is proud to have, and NodeJS community was looking for in terms of highly opinionated architecture.</p>
<p>Node.js, born in 2009, is a runtime environment based on Chrome’s V8 JavaScript engine, and it kept growing in interest in the developer community ever since. There are several Node.js frameworks like Express.js, Meteor, Koa.js, etc. But they somehow lacked a solution to an ever-increasing problem. How can we create backend applications in JS, where large teams can work, build to scale and maintain it with time?</p>
<p><a href="https://twitter.com/kammysliwiec"><strong>Kamil Mysliwiec</strong></a> had an answer when he introduced NestJS, the fastest-growing Node.js framework of 2018.</p>
<p><img src="/assets/img/articles/2019-11-04-NestJS-Your-Angular-Window-to-Server-side-Programming/nestjs.webp" alt=""></p>
<p>Before we introduce NestJS, let’s look at the key takeaways of Angular a bit. If you are working in a large team, opinionated frameworks are way to go. The code becomes consistent and easily maintainable. Angular being modular, import and exporting modules allow components to be re-used in other applications, saving much more development time. Furthermore, since Angular has an impressive DI mechanism, almost all of the good programming practices are by default adopted while coding.</p>
<p><img src="/assets/img/articles/2019-11-04-NestJS-Your-Angular-Window-to-Server-side-Programming/angular.webp" alt=""></p>
<p><strong>NestJS Takeaways</strong></p>
<p>Similarly, NestJS have this Angular philosophy out of the box. Let’s run down that same list for Nest:</p>
<p><strong>Platform and Framework</strong></p>
<p>NestJS is more than a framework, as it gives developers more options outside the box, like abstractions for Microservices, WebSockets, GraphQL, etc. to name a few.</p>
<p><strong>Typescript</strong></p>
<p>Uses Typescript, which as we know is the Javascript that scales. Programming thus can be done just right with any or all of them, Object Oriented Programming, Functional Programming, and Functional Reactive Programming. But if you love vanilla JS, you can still use it.</p>
<p><strong>Opinionated</strong></p>
<p>Nest provides a set of guidelines, as to how we should use the framework, but never limits us to work with the underlying APIs like that of Express, or Fastify. Opinionated means the code is much more consistent across large teams but doesn’t necessarily imply that you can’t be creative while working with abstractions.</p>
<p><strong>Modular</strong></p>
<p>That means it is domain driven, each module holding a set of their own set of business logic. The modules are singletons by default, but we can still share the same instance of any provider between multiple modules effortlessly in Nest.</p>
<p><strong>Controllers</strong></p>
<p>Nest does not have any components like that of Angular. Instead, they have controllers that accept a request and sends a response, the core idea behind Server Side Programming.</p>
<p><strong>Dependency Injection</strong></p>
<p>Nest heavily uses decorator functions. Decorator functions provide metadata to the compiler during runtime so to ensure each dependency is resolved while building the application. Decorators ensure that SOLID principles are ensured throughout the application.</p>
<p><strong>Providers</strong></p>
<p>Providers can be any values, class or factory that contains certain metadata related to various injection scopes. Dependency Injection is a core part of NestJS, with its own built-in IoC to resolve dependencies.</p>
<p><strong>CLI</strong></p>
<p>Life becomes easy when CLI helps you to scaffold, serve, build and bundle the application for you.</p>
<p>We can see that all the goodness of Angular is prevalent in NestJS. So why not we code some “Angular” in the backend? Disclaimer, even if you are not Angular developers, you can still easily pick up Nest as your backend framework, thanks to its well-crafted documentation.</p>
<p>We will be building a few APIs for one entity. Let’s say we want to create a resource called Events, like tech community events. We will make some routes that handle requests, based on the HTTP verb, we then fetch/update data from a database, and return a response.</p>
<p>The full code, can be found <a href="https://github.com/Saad-Amjad/ng-bd-demo">here</a>, but I will still go over few steps and highlight how the code was written.</p>
<p>Install nestjs cli, we need to have <a href="https://nodejs.org/">Node.js</a> (>= 8.9.0):</p>
<pre><code class="hljs language-css">npm <span class="hljs-selector-tag">i</span> -g <span class="hljs-keyword">@nestjs</span>/cli
</code></pre>
<p>Creating a new project, we will name it as <code>ng-bd-demo</code>:</p>
<pre><code class="hljs language-arduino">nest <span class="hljs-keyword">new</span> ng-bd-demo
</code></pre>
<p>We will see a project got scaffolded, and if you want to run the project:</p>
<pre><code class="hljs language-arduino">npm run start
</code></pre>
<p>While development, you want the changes to be reflected, then the command is:</p>
<pre><code class="hljs language-arduino">npm run start:dev
</code></pre>
<p>If you explore the folder structure, it will look as a familiar territory like that of Angular. Let’s focus on the <code>main.ts</code> file that kicks of the entire application. The bootstrap code looks like this:</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">bootstrap</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> app = <span class="hljs-keyword">await</span> <span class="hljs-title class_">NestFactory</span>.<span class="hljs-title function_">create</span>(<span class="hljs-title class_">AppModule</span>);
  <span class="hljs-keyword">await</span> app.<span class="hljs-title function_">listen</span>(<span class="hljs-number">3000</span>);
}
<span class="hljs-title function_">bootstrap</span>();
</code></pre>
<p>Let’s create a module called events:</p>
<pre><code class="hljs language-arduino">nest g <span class="hljs-keyword">module</span> events

</code></pre>
<p>Create a controller called events:</p>
<pre><code class="hljs">nest g controller events
</code></pre>
<p>Create a service called events:</p>
<pre><code class="hljs">nest g service events
</code></pre>
<p>These commands will ensure that <code>AppModule</code> contains <code>EventsModule</code>, and <code>EventsModule</code> contains <code>EventsController</code> and <code>EventsService</code> declared as controllers and providers respectively.</p>
<p>We will try to create some functions that will enable us to have CRUD APIs for <code>Events</code>. For that, we need these decorators from <a href="http://twitter.com/nestjs/common" title="Twitter profile for @nestjs/common">@nestjs/common</a>.</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">import</span> {
  <span class="hljs-title class_">Controller</span>,
  <span class="hljs-title class_">Get</span>,
  <span class="hljs-title class_">Post</span>,
  <span class="hljs-title class_">Body</span>,
  <span class="hljs-title class_">Param</span>,
  <span class="hljs-title class_">Patch</span>,
  <span class="hljs-title class_">Delete</span>,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@nestjs/common"</span>;
</code></pre>
<p>For start, let’s return a simple string for a <code>GET index()</code> method call:</p>
<pre><code class="hljs language-typescript"><span class="hljs-meta">@Get</span>()
<span class="hljs-title function_">index</span>(): <span class="hljs-built_in">string</span> {
 <span class="hljs-keyword">return</span> <span class="hljs-string">'Events list'</span>;
}
</code></pre>
<p>If we run the server now, and hit <code>GET events/</code> API, it will return ‘Events list’. Before we complete the other routes, let’s first make our application talk to a database. We will be using MySQL, you can choose any since Nest is database agnostic.</p>
<p>Nest provides TypeORM, Object Relational Mapper (ORM) for TypeScript, package out of the box, which can be installed using this command.</p>
<pre><code class="hljs language-css">npm install <span class="hljs-attr">--save</span> <span class="hljs-keyword">@nestjs</span>/typeorm typeorm mysql
</code></pre>
<p>After install import the <code>TypeORM</code> module’s <code>forRoot()</code> method in your <code>app.module</code>, and in <code>events.module</code> import it with TypeORM module <code>forFeature()</code> method.</p>
<p><code>forRoot</code> method accepts configurations of your database connections, and <code>forFeature()</code> method defines the repositories for that module which imports it.</p>
<p>Now we create an <code>Event</code> entity that we will import in <code>EventsModule</code> like this: <code>TypeOrmModule.forFeature([Event])</code> .</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">import</span> {
  <span class="hljs-title class_">Entity</span>,
  <span class="hljs-title class_">PrimaryGeneratedColumn</span>,
  <span class="hljs-title class_">Column</span>,
  <span class="hljs-title class_">CreateDateColumn</span>,
  <span class="hljs-title class_">Timestamp</span>,
  <span class="hljs-title class_">UpdateDateColumn</span>,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"typeorm"</span>;

<span class="hljs-meta">@Entity</span>()
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Event</span> {
  <span class="hljs-meta">@PrimaryGeneratedColumn</span>()
  <span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>;

  <span class="hljs-meta">@Column</span>()
  <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;

  <span class="hljs-meta">@Column</span>()
  <span class="hljs-attr">description</span>: <span class="hljs-built_in">string</span>;

  <span class="hljs-meta">@CreateDateColumn</span>({ <span class="hljs-attr">nullable</span>: <span class="hljs-literal">true</span> })
  <span class="hljs-attr">created_at</span>: <span class="hljs-title class_">Timestamp</span>;

  <span class="hljs-meta">@UpdateDateColumn</span>({ <span class="hljs-attr">nullable</span>: <span class="hljs-literal">true</span> })
  <span class="hljs-attr">updated_at</span>: <span class="hljs-title class_">Timestamp</span>;
}
</code></pre>
<p>We will be using Dependency Injection in our service to use this entity in our repository.</p>
<pre><code class="hljs language-typescript"><span class="hljs-meta">@Injectable</span>()
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">EventsService</span> {

<span class="hljs-title function_">constructor</span>(<span class="hljs-params"><span class="hljs-meta">@InjectRepository</span>(Event)
 <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> eventRepository: Repository&#x3C;Event></span>) { }

<span class="hljs-keyword">async</span> <span class="hljs-title function_">findAll</span>(): <span class="hljs-title class_">Promise</span>&#x3C;<span class="hljs-title class_">Event</span>\[\]> {
 <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">eventRepository</span>.<span class="hljs-title function_">find</span>();
 }

<span class="hljs-keyword">async</span> <span class="hljs-title function_">find</span>(<span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>): <span class="hljs-title class_">Promise</span>&#x3C;<span class="hljs-title class_">Event</span>> {
 <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">eventRepository</span>.<span class="hljs-title function_">findOne</span>(id);
 }

}
</code></pre>
<p>We are injecting the repository in the service class and the <code>findAll()</code> method will call the database for all the <code>events</code> asynchronously. You might wonder how will this entity be used to migrate a real table in the database.</p>
<p>When we provided configs in <code>forRoot()</code>method, we explicitly told <code>TypeORM</code> to synchronize the <code>entities</code>, i.e. run auto migrations for us, using the array values provided in the <code>entities</code> options.</p>
<pre><code class="hljs language-typescript"><span class="hljs-title class_">TypeOrmModule</span>.<span class="hljs-title function_">forRoot</span>({
 <span class="hljs-attr">type</span>: <span class="hljs-string">'mysql'</span>,
 <span class="hljs-attr">host</span>: <span class="hljs-string">'localhost'</span>,
 <span class="hljs-attr">port</span>: <span class="hljs-number">3306</span>,
 <span class="hljs-attr">username</span>: ‘username’,
 <span class="hljs-attr">password</span>: ‘password’,
 <span class="hljs-attr">database</span>: ‘database<span class="hljs-string">',
 entities: \[Event\],
 synchronize: true,
})
</span></code></pre>
<p>The controller <code>GET</code> index will finally look like the following. We can now also use <code>Param()</code> to retrieve URL query params.</p>
<pre><code class="hljs language-typescript"><span class="hljs-title function_">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">private</span> eventService: EventsService</span>) { }

<span class="hljs-meta">@Get</span>()
 <span class="hljs-title function_">index</span>(): <span class="hljs-title class_">Promise</span>&#x3C;<span class="hljs-title class_">Event</span>\[\]> {
 <span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">eventService</span>.<span class="hljs-title function_">findAll</span>();
 }

<span class="hljs-meta">@Get</span>(<span class="hljs-string">':id'</span>)
 <span class="hljs-title function_">show</span>(<span class="hljs-meta">@Param</span>(<span class="hljs-string">'id'</span>) <span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>): <span class="hljs-title class_">Promise</span>&#x3C;<span class="hljs-title class_">Event</span>> {
 <span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">eventService</span>.<span class="hljs-title function_">find</span>(id);
 }
</code></pre>
<p>But for POST, instead of query params, we need to get the payload from the body of the request. To ensure the payloads are consistent for a specific API, we will now be creating a Data Transfer Object (DTO).</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">IsString</span>, <span class="hljs-title class_">IsNotEmpty</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"class-validator"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">CreateEventDto</span> {
  <span class="hljs-meta">@IsString</span>()
  <span class="hljs-meta">@IsNotEmpty</span>()
  <span class="hljs-keyword">readonly</span> <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;

  <span class="hljs-meta">@IsString</span>()
  <span class="hljs-meta">@IsNotEmpty</span>()
  <span class="hljs-keyword">readonly</span> <span class="hljs-attr">description</span>: <span class="hljs-built_in">string</span>;
}
</code></pre>
<p>We installed ‘class-validator’ to give validation rules to the request body.</p>
<pre><code class="hljs language-arduino">npm i --save <span class="hljs-keyword">class</span>-validator <span class="hljs-keyword">class</span>-transformer
</code></pre>
<p><code>POST store()</code>would now look like this:</p>
<pre><code class="hljs language-typescript"><span class="hljs-meta">@Post</span>()
<span class="hljs-title function_">store</span>(<span class="hljs-meta">@Body</span>() <span class="hljs-attr">createEventDto</span>: <span class="hljs-title class_">CreateEventDto</span>): <span class="hljs-title class_">Promise</span>&#x3C;<span class="hljs-title class_">Event</span>> {
 <span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">eventService</span>.<span class="hljs-title function_">create</span>(createEventDto);
}
</code></pre>
<p>and <code>PATCH update()</code> and <code>DELETE delete()</code> like this:</p>
<pre><code class="hljs language-typescript"><span class="hljs-meta">@Patch</span>(<span class="hljs-string">':id'</span>)
<span class="hljs-title function_">update</span>(
 <span class="hljs-meta">@Param</span>(<span class="hljs-string">'id'</span>) <span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>,
 <span class="hljs-meta">@Body</span>() <span class="hljs-attr">updateEventDto</span>: <span class="hljs-title class_">UpdateEventDto</span>): <span class="hljs-title class_">Promise</span>&#x3C;<span class="hljs-title class_">Event</span>> {
 <span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">eventService</span>.<span class="hljs-title function_">update</span>(id, updateEventDto);
}

<span class="hljs-meta">@Delete</span>(<span class="hljs-string">':id'</span>)
<span class="hljs-title function_">delete</span>(<span class="hljs-meta">@Param</span>() <span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>): <span class="hljs-title class_">Promise</span>&#x3C;<span class="hljs-title class_">DeleteResult</span>> {
 <span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">eventService</span>.<span class="hljs-title function_">delete</span>(id);
}
</code></pre>
<p>The service now should have the methods to implement these API calls to our project, and then send it back to the appropriate client.</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">async</span> <span class="hljs-title function_">create</span>(<span class="hljs-attr">createPayload</span>: <span class="hljs-title class_">CreateEventDto</span>): <span class="hljs-title class_">Promise</span>&#x3C;<span class="hljs-title class_">Event</span>> {
 <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">eventRepository</span>.<span class="hljs-title function_">save</span>(createPayload);
}
</code></pre>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">async</span> <span class="hljs-title function_">update</span>(<span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>,
 <span class="hljs-attr">updatePayload</span>: <span class="hljs-title class_">UpdateEventDto</span>): <span class="hljs-title class_">Promise</span>&#x3C;<span class="hljs-title class_">Event</span>> {
 <span class="hljs-keyword">const</span> event = <span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">eventRepository</span>.<span class="hljs-title function_">findOne</span>(id);
 <span class="hljs-keyword">const</span> data = <span class="hljs-title class_">Object</span>.<span class="hljs-title function_">assign</span>(event, updatePayload);
 <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">eventRepository</span>.<span class="hljs-title function_">save</span>(data);
}

<span class="hljs-keyword">async</span> <span class="hljs-title function_">delete</span>(<span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>): <span class="hljs-title class_">Promise</span>&#x3C;<span class="hljs-title class_">DeleteResult</span>> {
 <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">eventRepository</span>.<span class="hljs-title function_">delete</span>(id);
}
</code></pre>
<p>We can either use Postman or Swagger to manipulate the APIs. For Swagger you can run this to install it in the application:</p>
<p><code>npm install --save @nestjs/swagger swagger-ui-express</code></p>
<p>Then in <code>main.ts</code>we bootstrap the Swagger module:</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">bootstrap</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> app = <span class="hljs-keyword">await</span> <span class="hljs-title class_">NestFactory</span>.<span class="hljs-title function_">create</span>(<span class="hljs-title class_">AppModule</span>);

  app.<span class="hljs-title function_">useGlobalPipes</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">ValidationPipe</span>());

  <span class="hljs-keyword">const</span> options = <span class="hljs-keyword">new</span> <span class="hljs-title class_">DocumentBuilder</span>()
    .<span class="hljs-title function_">setTitle</span>(<span class="hljs-string">"ng-bd-demo"</span>)
    .<span class="hljs-title function_">setDescription</span>(<span class="hljs-string">"Application that showcases backend in Angular"</span>)
    .<span class="hljs-title function_">build</span>();

  <span class="hljs-keyword">const</span> <span class="hljs-variable language_">document</span> = <span class="hljs-title class_">SwaggerModule</span>.<span class="hljs-title function_">createDocument</span>(app, options);
  <span class="hljs-title class_">SwaggerModule</span>.<span class="hljs-title function_">setup</span>(<span class="hljs-string">"api"</span>, app, <span class="hljs-variable language_">document</span>);
  <span class="hljs-keyword">await</span> app.<span class="hljs-title function_">listen</span>(<span class="hljs-number">3000</span>);
}
<span class="hljs-title function_">bootstrap</span>();
</code></pre>
<p>If you run the server and go to <code>/api</code>, you can see Swagger documenting the list of APIs for you. We are done making 5 API calls, and pretty much that’s it for this introduction.</p>
<p>You import things you need, you create controllers, services, and try to send back a response to the client application. You can now export this module to any application you want.</p>
<p>I wanted to showcase how short the learning curve is for Angular developers to use NestJS. There are many more to Nest than what I described here, support for GraphQL Microservices, WebSockets, testing utilities are to name a few. Clean code, consistency, and maintenance of NodeJS based applications where you expect scaling and reusability, Nest should be your go-to platform.</p>
<p>Happy coding folks!</p>
<p><em>Article Photo by <a href="https://unsplash.com/@gaellemarcel">Gaelle Marcel</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Droidcon Berlin 2019]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/10/08/Droidcon-Berlin</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/10/08/Droidcon-Berlin</guid>
            <pubDate>Tue, 08 Oct 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>Droidcon Berlin 2019</h2>
<p>Back in July, Lucas and I took a trip to Berlin for the Droidcon 2019 conference. It was the second mobile development conference that I've been to and the first one entirely dedicated to Android, so I had high hopes. In this blog I'd like to highlight some of the talks I chose to attend.</p>
<h2>Quick facts</h2>
<ol>
<li>3 days filled with talks and workshops</li>
<li>100+ talks &#x26; workshops</li>
<li>5 tracks running in parallel, so you always have a choice for what to see next.</li>
<li>Website <a href="https://www.de.droidcon.com/">https://www.de.droidcon.com/</a></li>
</ol>
<h3>You get a talk. And you. Everyone gets a talk.</h3>
<p>Straightaway, I was surprised by the Keynotes. Not sure if it is Droidcon tradition, or it is something newly introduced, but anyone from the audience could pitch a talk he/she was interested in giving.</p>
<p>Here's how it works: everyone interested in giving a talk had about 30 seconds to introduce himself and his/her topic. So whether you were declined with the opportunity of being a speaker, or you were late in the applying process, or you simply have something you want to share or discuss - you could've totally done that! The newly introduced speaker then received a time slot, and the timetable was updated accordingly. This led to a lot of new exciting talks and discussions. So thumbs up for that!</p>
<h2>Android Development in 5 years.</h2>
<p>This was not as much of talk as a discussion between very opinionated people. Long story short, we are pretty safe out here.</p>
<p>They have started with speculating how Android development could change after the next five years. There were no doubts whether Android will still be a popular technology used - the Android community is constantly growing, and the platform evolves rather rapidly. So it's a good thing.</p>
<p>Then the speculations started. It included even more automation in the development process, new wearables, and new ways to interact with devices like hovering gestures and audio interactions. Another thing that was discussed and for us to consider is a change to UI/UX: Morphing screen states to replace classic screen-to-screen navigation.</p>
<p>Another part of the discussion was related to other solutions like PWAs and cross-platform frameworks. They argued that all these solutions provide a different user experience and won't replace native Android development. Still, it shouldn't -- it is its own solution and the rule "Right tool for the right job" still applies.</p>
<p>Lastly, some of the skills that could be useful in the near future:</p>
<ul>
<li>Stop separating designers and developers</li>
<li>Pairing Backend and Frontend developers
These two can be seen as motivation for us to work more closely together, know the constraints of different platforms, and make decisions accordingly.</li>
<li>Soft Skills (well how else without them)</li>
<li>Checking WEB frameworks and trends once in a while
Web development is a very fast-growing field, and some of the trends could easily make it to Android in some way or another, and some have already done it.</li>
</ul>
<h2>Diving into Jetpack Compose</h2>
<p>Next talk was my second introduction to the Jetpack Compose. If anything, that talk got me a bit more excited about this new UI framework than a recent glimpse I had at a Google I/O. Using the simple example of the counter widget, the speaker talked about the philosophy behind Jetpack Compose and made a couple of points about how its introduction might help the developers.</p>
<ul>
<li>
<p><code>UI = F(s)</code> or declaring the UI as the function of the state. This allows us to declare stateless widgets and promote a single source of truth without trying to figure out where should the updated state be stored.</p>
</li>
<li>
<p>Code structure matches view hierarchy with the state located the closest to the root</p>
</li>
<li>
<p>No <code>Views</code> references - we only have <code>@Composable</code> functions to draw the UI on Canvas for us</p>
</li>
</ul>
<h2>MVI made for Android</h2>
<p>We have already started to use MVI in our projects at Nodes, so I was quite interested to see how others approached this. The talk started with an introduction to Model-View-Intent pattern and slowly transformed into the speaker describing their solution to the problem: Roxie library. Roxie is the Android implementation of principles introduced by Redux and all of its concepts:</p>
<ul>
<li>State - a <code>data class</code> that represents the UI,</li>
<li>Change - a <code>sealed class</code> that represents the result of the domain interaction (i.e. <code>Interactor</code> result)</li>
<li>Action - User interactions with the UI, which may result in triggering a change</li>
<li>Reducer - a function that combines State and the Change to produce a new State.</li>
<li><code>ViewModel</code> is then declared as follows: <code>BaseViewModel&#x3C;Action, State></code></li>
</ul>
<p>I am eager to try this approach in my upcoming projects, and there is also <a href="https://github.com/nodes-android/kotlin-template/tree/feature/redux">a branch in the Nodes Kotlin Template</a> that shows how it could work with our template. There it uses <code>Channel</code> and <code>Flow</code> from Kotlin Coroutines instead of <code>RxJava</code>. You can also check out <a href="https://github.com/levinzonr/roxie">my fork</a> of this library for more examples of using this approach.</p>
<p>Also if I got any of you interested, you could check this <a href="https://proandroiddev.com/unidirectional-data-flow-with-roxie-bec546c18598">article</a> with some examples and excellent points about readability and writing tests.</p>
<h2>Why SKY failed at modularising their application</h2>
<p>This is the kind of talk where you get to listen about other people failures and mistakes, which is always fun but still, you get to learn from their experiences.
Having a rather big flavor-based application, at some point, they have decided to start splitting it into modules by features.</p>
<p>The very first problem they have encountered was a problem defining what is a feature. SKY have started by trying to separate the most complicated part of the app ( failed ), then the simplest one (splash, success, not that it helps anyone). Turns out, this approach was a mistake, so they continued by separating the parts of the app that would be most valuable to have in modules.
At some point, they have also encountered problems with circled dependencies, resource conflicts, etc. So here is a word of advice from them when it comes to modularizing Android applications:</p>
<ul>
<li>Treat each feature module as a library that you can outsource... maybe.</li>
<li>Your first feature module must serve as the blueprint for all the others module, so don't compromise on them and integrate it as fast as you can.</li>
</ul>
<h2>Wrapping up</h2>
<p>Three days long conference contains a large number of talks -- far more that I would be able to summarize in one blog post. I tried to highlight the ones that I found the most interesting. A small part of the talks, the ones that were dedicated to Architecture Components, I found pretty repetitive, mostly because they were covered pretty well during Google I/O. All in all, it was a great experience, and I'd recommend attending one of the Droidcons for anyone interested.</p>
<p><em>Article Photo by @droidconBerlin</em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Using USDZ for a better AR experience]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/10/07/Using-USDZ-for-a-better-AR-experience</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/10/07/Using-USDZ-for-a-better-AR-experience</guid>
            <pubDate>Mon, 07 Oct 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><a href="https://developer.apple.com/augmented-reality/arkit/">ARKit 3.0</a> was officially released together with iOS 13.0 and it includes a lot of augmented reality goodies, such as people occlusion and motion capture. You can read about all the new features [here]({% if site.baseurl %} {% if site.baseurl %}{{ site.baseurl }}{% endif %} {% endif %}{% post_url 2019-06-26-ARKit-3-brings-more-power-to-Apples-Augmented-Reality %}).</p>
<p>The AR focused file format (USDZ) was announced at the WWDC Conference in 2018 and it's supported on iOS, macOS and tvOS. In this post we will dive into what this format is and how we can convert and create USDZ files.</p>
<h2>What is USDZ?</h2>
<p>One of the biggest advantages of USDZ is its seamless integration with iOS apps like Messages, Mail, Safari, Notes, News, Files and of course, 3rd party apps.</p>
<p>The technology behind it is called <a href="http://openusd.org">USD (Universal Scene Description)</a>. It is a 3D file format developed by Pixar and it has a focus on <strong>speed, scalability and collaboration</strong>. USDZ is the distribution format for USD, which is a compact single file that is optimised for sharing. It is also the file used for AR quick look (checkout some AR quick look examples <a href="https://developer.apple.com/augmented-reality/quick-look/">here</a>).</p>
<p><img src="/assets/img/articles/2019-10-07-Using-USDZ-for-a-better-AR-experience/usdz1.webp" alt=""></p>
<h2>How to convert an existing file to USDZ?</h2>
<p>As it is early ages for this format, you will probably still have your 3D content available in other formats like .obj, .gltf, .fbx or .abc. But the good news is that converting any of those to .usdz has been made easy by Apple with the <code>usdzconvert</code> command line tool. It also performs asset validation so you will be able to know if your files has been converted properly.</p>
<p><strong>Installing the USD Python Tools</strong></p>
<ol>
<li>Download the USD Python Tools from <a href="https://developer.apple.com/download/more/?=USDPython">here</a></li>
<li>Open the unzipped folder</li>
<li>Double click on the <code>USD.command</code></li>
</ol>
<p><strong>See all options</strong></p>
<pre><code class="hljs language-shell">usdzconvert -h
</code></pre>
<p><strong>Convert a file to .usdz</strong></p>
<pre><code class="hljs language-shell">usdzconvert &#x3C;file>
</code></pre>
<p><strong>Convert a file to .usdz while also adding materials</strong></p>
<pre><code class="hljs language-shell">usdzconvert &#x3C;file> -diffuseColor &#x3C;file> -normal &#x3C;file> -metallic &#x3C;file> -roughness &#x3C;file>
</code></pre>
<p><strong>Plain text representation of your .usdz file</strong></p>
<pre><code class="hljs language-shell">usdcat &#x3C;file>
</code></pre>
<p><strong>The structure of the model</strong></p>
<pre><code class="hljs language-shell">usdtree &#x3C;file>
</code></pre>
<p><strong>Asset validator</strong></p>
<pre><code class="hljs language-shell">usdchecker &#x3C;file>
</code></pre>
<h2><a href="https://en.wikipedia.org/wiki/GlTF">Converting a glTF file example</a></h2>
<p>For testing the .usdz converter with a .glTF file I used the <a href="https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/Buggy/glTF">Buggy</a> model from the <a href="https://github.com/KhronosGroup/glTF-Sample-Models">glTF-Sample-Models</a> repository by KhronosGroup. In my test folder I made sure to have both the .glTF and .bin files for the conversion to work properly.</p>
<pre><code class="hljs language-shell">usdzconvert Buggy.gltf
Input file: Buggy.gltf
Output file: Buggy.usdz
usdARKitChecker: [Pass] Buggy.usdz
</code></pre>
<p>To test out the file, I drag and dropped the <strong>Buggy.usdz</strong> file into my <strong>Notes</strong> app on my MacBook which is synchronised with my iPhone.
Then I was able to open the file directly from my iPhone. Simple as that!</p>
<p><img src="/assets/img/articles/2019-10-07-Using-USDZ-for-a-better-AR-experience/usdz2.webp" alt=""></p>
<h2><a href="https://en.wikipedia.org/wiki/FBX">Converting a FBX file example</a></h2>
<p>To be able to convert .fbx (Filmbox) files you will need to take some extra steps as it doesn't come supported out of the box.</p>
<ol>
<li>Install the Autodesk FBX SDK and FBX Phython bindings from <a href="https://www.autodesk.com/developer-network/platform-technologies/fbx-sdk-2019-5">here</a></li>
<li>Edit the <code>USD.command</code> file from the USD Python Tools folder, uncomment and update the file path to point to the location of the fbx.so (Phython 2.7). In my case I had to update it to <code>export PYTHONPATH=$PYTHONPATH:/Applications/Autodesk/FBX\ Python\ SDK/2019.5/lib/Python27_ub</code></li>
<li>Rerun the <code>USD.command</code>. Now you should be able to convert FBX files.</li>
</ol>
<p>For a testing example I used the <a href="https://free3d.com/3d-model/rose-31675.html">Rose</a> model from Free3d.com website. I downloaded the model and copied the .fbx file and textures to my test folder.</p>
<pre><code class="hljs language-shell">usdzconvert rose.fbx
Input file: rose.fbx
Output file: rose.usdz
usdARKitChecker: [Pass] rose.usdz
</code></pre>
<p>You can preview the converted model by selecting it in the folder and pressing <code>SPACE</code> on your Mac keyboard. Another way is by opening it with Xcode, or if you want to see it in real life, copy it into your iPhone and test it out.</p>
<p><img src="/assets/img/articles/2019-10-07-Using-USDZ-for-a-better-AR-experience/usdz3.webp" alt=""></p>
<p>A few notes to keep in mind:</p>
<ul>
<li>If you have an FBX file with external texture files make sure to have them in the same folder when making the conversion</li>
<li>Texture files can be of type .png or .jpg</li>
<li>Experimenting more with other models, it is quite often that the models fail conversion from first try. Most of the time, the issue is caused by missing or wrong formatted textures. This could be avoided by passing on the requirements for a good conversion to the 3d model designer.</li>
</ul>
<h2>How to create a USDZ file?</h2>
<p>If you want to create your 3D assets directly in the USDZ file format there are different options and here are some examples:</p>
<ol>
<li><a href="https://www.adobe.com/products/substance.html">Substance painter by Adobe</a> - Supported</li>
<li><a href="https://www.autodesk.com/products/maya/overview">Autodesk Maya</a> - Supported through a plugin (written and maintained by Pixar)</li>
<li><a href="https://developer.apple.com/documentation/scenekit">SceneKit</a> - Create or load a <code>SCNScene</code> that you can export to USDZ with an API call or directly from the user interface.</li>
</ol>
<h2>Resources</h2>
<ul>
<li><a href="https://graphics.pixar.com/usd/docs/Usdz-File-Format-Specification.html">USDZ File Format Specification</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Simplifying Dependency Injection And IoC Concepts Using TypeScript]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/10/02/simplifying-dependency-injection-and-ioc-concepts-with-typescript</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/10/02/simplifying-dependency-injection-and-ioc-concepts-with-typescript</guid>
            <pubDate>Wed, 02 Oct 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>It is never easy to do everything by yourself. Since the beginning of time, humans truly understood, often with a huge cost, that their true power lies not in conflicts but in collaboration. Programming paradigm is also not quite different. For an application to live long, it must figure out the dependencies and try to look for ways to delegate others to serve the dependencies it needs. Not only this can solve its own problem efficiently, but it can also, in turn, help other programs by presenting itself as their dependency.</p>
<p><code>Dependency Injection</code> is a crucial application design pattern for almost all the frameworks out there to create reusable, manageable and testable code. Today let’s try to simplify Dependency Injection, which is a subset of Inversion of Control principle, with TypeScript.</p>
<p>But first, let us set the table straight. If you have noticed any top class restaurant’s kitchen operation then you will find this article’s motivation a bit similar. In a restaurant, each Station Chef is responsible for running a specific section of the kitchen and they are being managed directly by the Head Chef or by the second-in-command Sous Chef. Station chefs (for e.g. butcher chef, fish chef, grill chef) can be in charge of different things respectively.</p>
<p>Whenever one order comes to the kitchen via a Caller, the Sous Chef simply doesn’t start preparing it all by himself. He runs down what are the things he needs to deliver, and start instructing them accordingly to each Station Chef. He expects they would handover their prepared items on a common table, where the entire dish can then be prepared or garnished for serving.</p>
<p>This is an important takeaway that we will soon find out in this article but before that here is a rundown of the topics you will get introduced in this article.</p>
<pre><code class="hljs language-diff"><span class="hljs-deletion">- Dependency Injection</span>
<span class="hljs-deletion">- Dependency Inversion Principle</span>
<span class="hljs-deletion">- Inversion of Control</span>
<span class="hljs-deletion">- Inversion of Control Container</span>
<span class="hljs-deletion">- TypeScript Interfaces</span>
<span class="hljs-deletion">- Decorator Functions</span>
<span class="hljs-deletion">- Reflection APIs</span>
<span class="hljs-deletion">- TypeScript IoC</span>
</code></pre>
<h2>Problem Statement</h2>
<p>Let’s present a problem we are going to recreate and solve, called “Pizza making chronicles”.</p>
<p><img src="/assets/img/articles/2019-10-02-simplifying-dependency-injection-and-ioc-concepts-with-typescript/comic.webp" alt=""></p>
<p>Pizza making got several dependencies to start with. Ignoring cheese and toppings (even sauce) for simplicity, we can see that a pizza needs dough and dough needs flour and yeast. Flour and Yeast needs water. Water needs salt. Here is a directed graph diagram that roughly shows those dependencies:</p>
<p><img src="/assets/img/articles/2019-10-02-simplifying-dependency-injection-and-ioc-concepts-with-typescript/flowchart.webp" alt=""></p>
<h2>Dependency Injection</h2>
<p><img src="/assets/img/articles/2019-10-02-simplifying-dependency-injection-and-ioc-concepts-with-typescript/dependencies.webp" alt=""></p>
<blockquote>
<p>Dependency injection is a fancy phrase that essentially means this: class dependencies are “injected” into the class via the constructor or, in some cases, “setter” methods. - <a href="https://laravel.com/docs/5.8/container">From Laravel</a>.</p>
</blockquote>
<p>A pizza needs dough to start with. Let’s see how does that look in a typical code base.</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Pizza</span> {
  <span class="hljs-keyword">private</span> dough;

  <span class="hljs-keyword">public</span> <span class="hljs-title function_">constructor</span>(<span class="hljs-params"></span>) {
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">dough</span> = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Dough</span>();
  }
}
</code></pre>
<p>Soon we figure out that this <code>Dough</code> class is also required for making bread, so its redundant to allow both <code>Pizza</code> and <code>Bread</code> to be in charge of <code>Dough</code> all by themselves. So let’s delegate the creation of <code>Dough</code> to someone else. We inject the dependency <code>Dough</code> in <code>Pizza</code> constructor, an example of the most famous constructor based Dependency Injection.</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Pizza</span> {
  <span class="hljs-keyword">private</span> dough;

  <span class="hljs-keyword">public</span> <span class="hljs-title function_">constructor</span>(<span class="hljs-params">dough: DoughEntity</span>) {
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">dough</span> = dough;
  }
}
</code></pre>
<p>However, we passed a concrete implementation of <code>Dough</code> in <code>Pizza</code>, called <code>DoughEntity</code>. But customers might order different types of dough for different pizzas later.</p>
<h2>Dependency Inversion Principle</h2>
<blockquote>
<p>“..DIP says that our classes should depend upon abstractions, not on concrete details.” — Robert C. Martin</p>
</blockquote>
<p>Since dependency implementations can be swapped easily as they are injected during runtime, we should rather inject an interface, like <code>IDough</code> so that we can later swap it with any concrete implementation of <code>IDough</code>, i.e <code>DoughEntity</code> like <code>SourdoughDough</code>, <code>BriocheDough</code>, <code>ChallahDough</code>, <code>FocacciaDough</code> etc.</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Pizza</span> {
  <span class="hljs-keyword">private</span> dough;

  <span class="hljs-keyword">public</span> <span class="hljs-title function_">constructor</span>(<span class="hljs-params">dough: IDough</span>) {
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">dough</span> = dough;
  }
}
</code></pre>
<p>With dependency injections, we ensured that the chef making pizza can’t make his own dough, and with dependency inversion principle we ensured that different kinds of dough can now be used for making pizza. Same goes for the dough maker. The dough maker doesn’t decide which flour and what kind of flour he will use rather it is handed to him by someone else in the charge of the pantry.
There is a slight problem with this pattern in applications. This might lead to a nested and complicated dependency graph because we keep manually initiate the dependencies and pass them up to the ones who need it. So what to do?</p>
<h2>Inversion of Control</h2>
<p><img src="/assets/img/articles/2019-10-02-simplifying-dependency-injection-and-ioc-concepts-with-typescript/ioc.webp" alt=""></p>
<blockquote>
<p>In software engineering, inversion of control (IoC) is a programming principle. IoC inverts the flow of control as compared to traditional control flow. In IoC, custom-written portions of a computer program receive the flow of control from a generic framework. - <a href="https://en.wikipedia.org/wiki/Inversion_of_control">From Wiki</a></p>
</blockquote>
<p>Dependency Injection so far looked like this: The Caller needs a pizza and announces it in the kitchen. The Pizza Chef doesn’t make the dough himself, he asks for it from the dough maker.
But imagine the dough maker now says, “Don’t ask me for the dough, I will keep my dough on a table when I am done myself.”
There is a Hollywood Principle, which states:</p>
<blockquote>
<p>“Don’t Call Us, We’ll Call You”</p>
</blockquote>
<p>This is what inversion of control looks like. The Pizza Chef doesn’t directly gets the dough from the dough maker, rather he will get it from an external place without him having the knowledge of who added it there or when was it added.</p>
<h2>Inversion of Control Container</h2>
<blockquote>
<p><a href="https://www.danclarke.com/all-about-inversion-of-control-containers">The basic idea is that on startup of your application, you can define mappings between your abstractions and your implementations — eg. between your interfaces and concrete types. Then the IoC Container library will handle creating your objects for you and will automatically inject any dependencies.</a></p>
</blockquote>
<p>So finally we have a kitchen table which contains the prepared items. The pizza chef takes everything from there when he needs it and starts making pizza. A programming container also behaves as such, which typically bind interfaces to implementations and serves them as dependencies when someone needs it. It will look something like this in the code:</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">const</span> pizzaContainer = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Container</span>();
pizzaContainer.<span class="hljs-property">bind</span>&#x3C;<span class="hljs-title class_">IDough</span>>(<span class="hljs-title class_">IDough</span>).<span class="hljs-title function_">to</span>(<span class="hljs-title class_">DoughEntity</span>);
pizzaContainer.<span class="hljs-property">bind</span>&#x3C;<span class="hljs-title class_">IFlour</span>>(<span class="hljs-title class_">IFlour</span>).<span class="hljs-title function_">to</span>(<span class="hljs-title class_">FlourEntity</span>);
</code></pre>
<p>Now let’s code the talk using TypeScript. The demo code is a Node.js application build using Express framework. We will be using InversifyJS, an IoC container for TypeScript, in this project. Here are the dependencies used:</p>
<pre><code class="hljs language-json"> <span class="hljs-attr">"dependencies"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
    <span class="hljs-attr">"@types/express"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"^4.17.1"</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"express"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"^4.17.1"</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"inversify"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"^5.0.1"</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"reflect-metadata"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"^0.1.13"</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"tslint"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"^5.20.0"</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"typescript"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"^3.6.3"</span>
  <span class="hljs-punctuation">}</span>
</code></pre>
<p>I used lightweight <a href="https://github.com/inversify/InversifyJS">InversifyJS</a> to simply demonstrate how containers work in applications, and then it will be easier for us to understand how frameworks like Angular or Laravel use DI and IoCs. You can check out the entire repository here:
<a href="https://github.com/Saad-Amjad/inversify-di-ioc">Saad-Amjad/inversify-di-doc</a>.</p>
<h2>TypeScript Interfaces</h2>
<p>Let us create an interface called <code>Dough</code>. I don’t like using <code>IDough</code> naming, so would name dough as <code>Dough</code> for the rest of the code.</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">Dough</span> {
  <span class="hljs-title function_">getFlour</span>(): <span class="hljs-title class_">Flour</span>;
  <span class="hljs-title function_">getYeast</span>(): <span class="hljs-title class_">Yeast</span>;
}
</code></pre>
<blockquote>
<p><a href="https://weblogs.asp.net/dwahlin/the-role-of-interfaces-in-typescript">TypeScript — interfaces are only used when you’re writing code (the editor can show you errors) and when you compile. They’re not used at all in the generated JavaScript.</a></p>
</blockquote>
<p>We usually want to code using interfaces so that we have better consistency across classes, but in the generated JavaScript, we do not have any references for it. It is simply non-existent. So how can we use Dependency Injection in TypeScript?</p>
<h2>Decorator Functions</h2>
<blockquote>
<p><a href="https://www.typescriptlang.org/docs/handbook/decorators.html">A Decorator is a special kind of declaration that can be attached to a class declaration, method, accessor, property, or parameter. Decorators use the form @expression, where expression must evaluate to a function that will be called at runtime with information about the decorated declaration.</a></p>
</blockquote>
<p>Frameworks like Angular and NestJS use decorators to tell their own DI mechanism what are the dependencies other classes require and and how to initialise them.
Those decorator functions basically add metadata to our classes. The metadata are used to gain information about what dependencies it needs during runtime and thus helps in resolving them.
In Inversify, whenever a class (it will always have @injectable decorators on them) have a dependency on an interface, we use the @inject decorator to define an identifier for the interface that will be available at runtime.</p>
<h2>Reflection APIs</h2>
<p>Reflection allows inspection and modification of a code base, including its own. Unlike JavaScript, TypeScript does support experimental reflection features, though few in number. Using metadata reflection API we can standardize how we achieve details of unknown objects during runtime.
For the class <code>Pizza</code>, when <code>DoughEntity</code> dependency is called, the Injectable Decorator adds a metadata entry for the property using the <code>Reflect.metadata</code> function from the <code>reflect-metadata</code> library, which gives us an array of dependencies that the class requires. It under the hood presents us with the right information about <code>DoughEntity</code> being a dependency for <code>Pizza</code>.
Here is a simplified example of the metadata generated when we pass a concrete implementation. You can find this behaviour in Node.js frameworks like NestJS, and its decorator usage.</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">const</span> metadata = <span class="hljs-title class_">Reflect</span>.<span class="hljs-title function_">getMetadata</span>(<span class="hljs-string">"design: paramtypes"</span>, <span class="hljs-title class_">Pizza</span>);
<span class="hljs-comment">// metadata = [DoughEntity]</span>
</code></pre>
<p>We can see that the metadata points to exact <code>DoughEntity</code>. But what if we injected <code>Dough</code> interface?</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">const</span> metadata = <span class="hljs-title class_">Reflect</span>.<span class="hljs-title function_">getMetadata</span>(<span class="hljs-string">"design: paramtypes"</span>, <span class="hljs-title class_">Pizza</span>);
<span class="hljs-comment">// metadata = [Object]</span>
</code></pre>
<p>When the code transpiles to JavaScript, we get metadata as an Object, with no way for us to say which specific object it is. We need to do something to uniquely identify them so that during runtime the proper class is resolved.
In our demo code after having coded in the interfaces, we type-hint our real classes instead of interfaces. We use <code>Symbol</code> to allow identification of <code>Dough</code> interface with “Dough”.</p>
<pre><code class="hljs language-typescript"><span class="hljs-comment">//symbols as identifiers but you can also use classes and or string literals.</span>

<span class="hljs-keyword">const</span> <span class="hljs-variable constant_">TYPES</span> = {
  <span class="hljs-title class_">Dough</span>: <span class="hljs-title class_">Symbol</span>.<span class="hljs-title function_">for</span>(<span class="hljs-string">"Dough"</span>),
  <span class="hljs-title class_">Flour</span>: <span class="hljs-title class_">Symbol</span>.<span class="hljs-title function_">for</span>(<span class="hljs-string">"Flour"</span>),
  <span class="hljs-title class_">Salt</span>: <span class="hljs-title class_">Symbol</span>.<span class="hljs-title function_">for</span>(<span class="hljs-string">"Salt"</span>),
  <span class="hljs-title class_">Yeast</span>: <span class="hljs-title class_">Symbol</span>.<span class="hljs-title function_">for</span>(<span class="hljs-string">"Yeast"</span>),
  <span class="hljs-title class_">Water</span>: <span class="hljs-title class_">Symbol</span>.<span class="hljs-title function_">for</span>(<span class="hljs-string">"Water"</span>),
};

<span class="hljs-keyword">export</span> { <span class="hljs-variable constant_">TYPES</span> };
</code></pre>
<p>Let’s create a concrete implementation of <code>Dough</code> now and call it <code>DoughEntity</code>. Here you can see, we mentioned the class as injectable using @injectable decorator, and passed in interfaces in the constructor with @inject decorator so to define the identifiers.</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">import</span> { injectable, inject } <span class="hljs-keyword">from</span> <span class="hljs-string">"inversify"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title class_">Dough</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/dough.interface"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title class_">Flour</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/flour.interface"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title class_">Yeast</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/yeast.interface"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-variable constant_">TYPES</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"../types/types"</span>;

<span class="hljs-meta">@injectable</span>()
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">DoughEntity</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Dough</span> {
  <span class="hljs-keyword">public</span> <span class="hljs-attr">flour</span>: <span class="hljs-title class_">Flour</span>;
  <span class="hljs-keyword">public</span> <span class="hljs-attr">yeast</span>: <span class="hljs-title class_">Yeast</span>;

  <span class="hljs-keyword">public</span> <span class="hljs-title function_">constructor</span>(<span class="hljs-params">
    <span class="hljs-meta">@inject</span>(TYPES.Flour) flour: Flour,
    <span class="hljs-meta">@inject</span>(TYPES.Yeast) yeast: Yeast
  </span>) {
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">"Dough class initiated"</span>);
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">flour</span> = flour;
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">yeast</span> = yeast;
  }

  <span class="hljs-keyword">public</span> <span class="hljs-title function_">getFlour</span>(): <span class="hljs-title class_">Flour</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">flour</span>;
  }
  <span class="hljs-keyword">public</span> <span class="hljs-title function_">getYeast</span>(): <span class="hljs-title class_">Yeast</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">yeast</span>;
  }
}
</code></pre>
<p>Now we create a container that binds the interfaces to their implementations such that if <code>Dough</code> types are called, we get concrete implementation of <code>DoughEntity</code>.</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">const</span> pizzaContainer = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Container</span>();

<span class="hljs-comment">// pizzaContainer.bind&#x3C;>().to();</span>
pizzaContainer.<span class="hljs-property">bind</span>&#x3C;<span class="hljs-title class_">Dough</span>>(<span class="hljs-variable constant_">TYPES</span>.<span class="hljs-property">Dough</span>).<span class="hljs-title function_">to</span>(<span class="hljs-title class_">DoughEntity</span>);
pizzaContainer.<span class="hljs-property">bind</span>&#x3C;<span class="hljs-title class_">Flour</span>>(<span class="hljs-variable constant_">TYPES</span>.<span class="hljs-property">Flour</span>).<span class="hljs-title function_">to</span>(<span class="hljs-title class_">FlourEntity</span>);
pizzaContainer.<span class="hljs-property">bind</span>&#x3C;<span class="hljs-title class_">Salt</span>>(<span class="hljs-variable constant_">TYPES</span>.<span class="hljs-property">Salt</span>).<span class="hljs-title function_">to</span>(<span class="hljs-title class_">SaltEntity</span>);
pizzaContainer.<span class="hljs-property">bind</span>&#x3C;<span class="hljs-title class_">Water</span>>(<span class="hljs-variable constant_">TYPES</span>.<span class="hljs-property">Water</span>).<span class="hljs-title function_">to</span>(<span class="hljs-title class_">WaterEntity</span>);
pizzaContainer.<span class="hljs-property">bind</span>&#x3C;<span class="hljs-title class_">Yeast</span>>(<span class="hljs-variable constant_">TYPES</span>.<span class="hljs-property">Yeast</span>).<span class="hljs-title function_">to</span>(<span class="hljs-title class_">YeastEntity</span>);

<span class="hljs-keyword">export</span> { pizzaContainer };
</code></pre>
<p>We create the <code>Pizza</code> class as following:</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">import</span> { injectable, inject } <span class="hljs-keyword">from</span> <span class="hljs-string">"inversify"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"reflect-metadata"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title class_">Dough</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"./interfaces/dough.interface"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-variable constant_">TYPES</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"./types/types"</span>;

<span class="hljs-meta">@injectable</span>()
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Pizza</span> {
  <span class="hljs-keyword">public</span> dough;

  <span class="hljs-keyword">public</span> <span class="hljs-title function_">constructor</span>(<span class="hljs-params"><span class="hljs-meta">@inject</span>(TYPES.Dough) dough: Dough</span>) {
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">dough</span> = dough;
  }
}
</code></pre>
<p>Before we do the rest of the code, here is a peek into the future for better understanding. Since we are using decorators and reflection API, if we look at the generated JavaScript code of pizza.class.ts by setting <code>“emitDecoratorMetadata”: true</code> in <code>tsconfig.json</code>, we could see that the <code>Object</code> we get in metadata will be of type <code>Types.Dough</code>.</p>
<pre><code class="hljs language-js"><span class="hljs-title class_">Pizza</span> = <span class="hljs-title function_">__decorate</span>(
  [
    inversify_1.<span class="hljs-title function_">injectable</span>(),
    <span class="hljs-title function_">__param</span>(<span class="hljs-number">0</span>, inversify_1.<span class="hljs-title function_">inject</span>(types_1.<span class="hljs-property">TYPES</span>.<span class="hljs-property">Dough</span>)),
    <span class="hljs-title function_">__metadata</span>(<span class="hljs-string">"design:paramtypes"</span>, [<span class="hljs-title class_">Object</span>]),
  ],
  <span class="hljs-title class_">Pizza</span>
);
</code></pre>
<p>And the generated JavaScript code of <code>dough.entity.ts</code> would look like this, where param orders tell us about <code>DoughEntity</code> dependencies:</p>
<pre><code class="hljs language-js"><span class="hljs-title class_">DoughEntity</span> = <span class="hljs-title function_">__decorate</span>(
  [
    inversify_1.<span class="hljs-title function_">injectable</span>(),
    <span class="hljs-title function_">__param</span>(<span class="hljs-number">0</span>, inversify_1.<span class="hljs-title function_">inject</span>(types_1.<span class="hljs-property">TYPES</span>.<span class="hljs-property">Flour</span>)),
    <span class="hljs-title function_">__param</span>(<span class="hljs-number">1</span>, inversify_1.<span class="hljs-title function_">inject</span>(types_1.<span class="hljs-property">TYPES</span>.<span class="hljs-property">Yeast</span>)),
    <span class="hljs-title function_">__metadata</span>(<span class="hljs-string">"design:paramtypes"</span>, [<span class="hljs-title class_">Object</span>, <span class="hljs-title class_">Object</span>]),
  ],
  <span class="hljs-title class_">DoughEntity</span>
);
</code></pre>
<p>It surely seems that the decorators and reflection API have done its part in figuring out the dependency.
And now the moment of truth, let’s resolve a dependency here in the main.ts file and see it in action.</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">"express"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title class_">Pizza</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"./pizza.class"</span>;
<span class="hljs-keyword">import</span> { pizzaContainer } <span class="hljs-keyword">from</span> <span class="hljs-string">"./inversify.config"</span>;

<span class="hljs-keyword">const</span> <span class="hljs-attr">pizza</span>: <span class="hljs-title class_">Pizza</span> = pizzaContainer.<span class="hljs-property">resolve</span>&#x3C;<span class="hljs-title class_">Pizza</span>>(<span class="hljs-title class_">Pizza</span>);
</code></pre>
<p>If we run the application and open the console, we can see the following output telling us the Dependency Injection worked using the IoC container.
<code>Salt</code> gets initiated first. <code>Water</code>, <code>Flour</code>, <code>Yeast</code> and finally <code>Dough</code> classes were successfully resolved and finally got injected into the <code>Pizza</code> class.</p>
<pre><code class="hljs language-arduino">Salt <span class="hljs-keyword">class</span> <span class="hljs-title class_">initiated</span>
Water <span class="hljs-keyword">class</span> <span class="hljs-title class_">initiated</span>
Flour <span class="hljs-keyword">class</span> <span class="hljs-title class_">initiated</span>
Salt <span class="hljs-keyword">class</span> <span class="hljs-title class_">initiated</span>
Water <span class="hljs-keyword">class</span> <span class="hljs-title class_">initiated</span>
Yeast <span class="hljs-keyword">class</span> <span class="hljs-title class_">initiated</span>
Dough <span class="hljs-keyword">class</span> <span class="hljs-title class_">initiated</span>
Pizza <span class="hljs-keyword">class</span> <span class="hljs-title class_">initiated</span>
</code></pre>
<h2>Conclusion</h2>
<p>There is a reason why good kitchens operate similar to this. A real chef can vouch about it more than me for sure. They can truly deliver the best when each of the chefs has singular, separate responsibilities, each knowing exactly what, where and when to contribute in making a perfect dish for the restaurant-goers, with someone managing it, who is however not directly being involved in any of the process details.
I am really impressed with the DIs and IoCs of Laravel, Angular and NestJS. It has really made both the backend and frontend code much more manageable, reusable and testable with time. The key concepts are the same in all the frameworks and are rightly so. I hope this simplifies how to handle dependencies in the future.</p>
<p>Happy coding folks!</p>
<p><strong>References/Further Readings:</strong></p>
<p><a href="https://martinfowler.com/articles/injection.html">Inversion of Control Containers and the Dependency Injection pattern</a></p>
<p><em>Article Photo by <a href="https://unsplash.com/@fabmag">Fabrizio Magoni</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Github And Jira Integration]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/09/17/Github-And-Jira-Integration</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/09/17/Github-And-Jira-Integration</guid>
            <pubDate>Tue, 17 Sep 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Thousands of organizations around the world use GitHub and Jira together to manage software projects. With your GitHub account linked to Jira, it gives your team the ability to see branches, commit messages, and pull request in the Jira tickets they’re working on. On the GitHub side, you can view relevant Jira ticket information in issues and pull requests.</p>
<p>Once the GitHub account is connected with Jira, there is no need to switch between Github &#x26; Jira and the entire team will be able to see the work status.</p>
<h2>Migration</h2>
<p>The legacy ability to connect Jira Software and GitHub via the Jira DVCS connector is now deprecated in favor of this new <strong>GitHub-maintained integration</strong>. Once the migration is complete, the legacy integration (DVCS connector) is disabled automatically.</p>
<h2>How To Link GitHub And Jira</h2>
<p><strong>Configure Github</strong></p>
<ol>
<li>
<p>Go to <a href="https://github.com/marketplace/jira-software-github">https://github.com/marketplace/jira-software-github</a> and click <strong>Install</strong></p>
</li>
<li>
<p>During installation, you will be asked to select repositories you want to use with the Jira Integration
<img src="/assets/img/articles/2019-09-17-Github-And-Jira-Integration/Github1.webp" alt=""></p>
</li>
<li>
<p>After installation, enter the site name and click <strong>Continue</strong>
<img src="/assets/img/articles/2019-09-17-Github-And-Jira-Integration/Github2.webp" alt=""></p>
</li>
<li>
<p>Click on <strong>Authorize Jira by GitHub</strong> to give access to Jira
<img src="/assets/img/articles/2019-09-17-Github-And-Jira-Integration/Github3.webp" alt=""></p>
</li>
</ol>
<p><strong>Configure Jira</strong></p>
<ol>
<li>
<p>Sign into your Jira Cloud account</p>
</li>
<li>
<p>Go to Jira settings -> Apps -> Find new apps -> Search for <strong>GitHub for Jira</strong>
<img src="/assets/img/articles/2019-09-17-Github-And-Jira-Integration/Jira1.webp" alt=""></p>
</li>
<li>
<p>Click on <strong>GitHub for Jira</strong> and Click on Get App -> <strong>Get it now</strong>
<img src="/assets/img/articles/2019-09-17-Github-And-Jira-Integration/Jira2.webp" alt=""></p>
</li>
<li>
<p>Click on <strong>Get Started</strong> to connect your GitHub Account</p>
</li>
</ol>
<p><strong>Manage Repositories</strong></p>
<p>If you gave access to "All repositories" or "Single Repository", you can edit repository selection by:</p>
<ol>
<li>Sign into your Jira Cloud account.</li>
<li>Select Jira settings -> Apps -> Manage Apps -> GitHub -> Get Started -> <strong>Configure</strong></li>
</ol>
<h2>Advantages</h2>
<ul>
<li>
<p>See the status of development work right from the Jira board</p>
</li>
<li>
<p>Use smart commits to update the status, leave a comment, or log time without leaving your command line or GitHub
<img src="/assets/img/articles/2019-09-17-Github-And-Jira-Integration/Commits.webp" alt="Commits"></p>
</li>
<li>
<p>View associated pull requests, commits, and branches from within a Jira ticket
<img src="/assets/img/articles/2019-09-17-Github-And-Jira-Integration/Status.webp" alt="Status"></p>
</li>
<li>
<p>Search for Jira issues based on related GitHub information, such as open pull requests
<img src="/assets/img/articles/2019-09-17-Github-And-Jira-Integration/Branch.webp" alt="Branch"></p>
</li>
<li>
<p>You can automatically keep your Jira issues up to date while working in GitHub
<img src="/assets/img/articles/2019-09-17-Github-And-Jira-Integration/PullRequest.webp" alt="PullRequest"></p>
<p><strong>References:</strong></p>
</li>
</ul>
<p><a href="https://github.com/integrations/jira">https://github.com/integrations/jira</a></p>
<p><a href="https://www.atlassian.com/blog/jira-software/github-for-jira">https://www.atlassian.com/blog/jira-software/github-for-jira</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Go Docker]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/09/09/Go-Docker</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/09/09/Go-Docker</guid>
            <pubDate>Mon, 09 Sep 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Let’s say you are starting a new project. You have selected the language to be Go and the database Mongodb. You already have both set up in your computer be it a Mac or a Windows or a Linux machine. However, you had installed the tools a long time ago and both are now backdated. You want the new project to use the latest versions; you also need the older versions for the old (still running) projects you need to work on now and then. What do you do?</p>
<p>Buy new computers for each new project?</p>
<p>No.</p>
<p>However, you can imitate a new computer for each project, using virtual machines. We spin up virtual machines for each project to give it its own environment according to the requirements. Docker is one way to achieve this. And currently a very popular one too.</p>
<p>If you don’t know what Docker is or how it works I suggest you to read their own overview of the software <a href="https://www.docker.com/what-docker">here</a>.</p>
<blockquote>
<p>Only independent container platform that enables organizations to seamlessly build, share and run any application, anywhere — from hybrid cloud to the edge.<br>
— <a href="https://www.docker.com/why-docker">https://www.docker.com/why-docker</a></p>
</blockquote>
<p>In this article I will show a brief overview of a simple docker setup for Go application development.</p>
<p>To get started, you need Docker installed in your system. Get the community edition <a href="https://www.docker.com/community-edition">here</a>.</p>
<h3>Project setup</h3>
<p>Creating something too complex for this exercise will take focus away from the Docker configuration to application architecture and business logic. So I’ll stick to a “Hello world” implementation; let’s say everything else other than mainly the Docker part of the application is abstracted away.</p>
<p><em>Project structure:</em></p>
<pre><code class="hljs language-bash">.
├── .env
├── .gitignore
├── main.go
├── go.mod
├── docker-compose.yml
├── dev.dockerfile
└── prod.dockerfile
</code></pre>
<p><em>Initialize a new go module:</em></p>
<p>Run this in the root directory and it will create the <em>go.mod</em> file.</p>
<pre><code class="hljs language-bash">go mod init github.com/war1oc/go-docker
</code></pre>
<p>Replace <em>war1oc</em> with your Github username.</p>
<p>This creates a <em>go.mod</em> file with the following contents:</p>
<pre><code class="hljs language-go">module github.com/war1oc/<span class="hljs-keyword">go</span>-docker

<span class="hljs-keyword">go</span> <span class="hljs-number">1.13</span>
</code></pre>
<p><em>main.go:</em></p>
<pre><code class="hljs language-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> “fmt”

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    fmt.Println(“Hello, world!”)
}
</code></pre>
<p>If I run the app now, I get “Hello, world!” printed on the console.</p>
<h3>Development Docker Environment</h3>
<p>This is my go to Dockerfile for development environment:</p>
<pre><code class="hljs language-dockerfile"><span class="hljs-keyword">FROM</span> golang:<span class="hljs-number">1.13</span>-buster

<span class="hljs-keyword">RUN</span><span class="bash"> go get github.com/cespare/reflex</span>

<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>

<span class="hljs-keyword">COPY</span><span class="bash"> . .</span>

<span class="hljs-keyword">ENTRYPOINT</span><span class="bash"> [“reflex”, “-c”, “reflex.conf”]</span>
</code></pre>
<p>As of writing this, 1.13 was the latest Go version. I also use <a href="https://github.com/cespare/reflex">reflex</a> for restarting the server when files change; this helps a lot during development as the code is automatically reloaded.</p>
<p>The <em>reflex.conf</em> file:</p>
<pre><code class="hljs language-bash">-r ‘(\.go$|go\.mod)’ -s go run .
</code></pre>
<p>This means whenever a <em>.go</em> or <em>go.mod</em> file changes do <strong>go run .</strong></p>
<p>This is all I need to put in my development Dockerfile.</p>
<p>Build an image and run a container from this:</p>
<pre><code class="hljs language-bash">docker build -t go-docker -f dev.dockerfile .

docker run -it --rm -v `<span class="hljs-built_in">pwd</span>`:/app --name go-docker go-docker
</code></pre>
<p>This will start the application in a container named <em>go-docker</em> from the image named <em>go-docker</em> and mount current directory on the host to the <em>/app</em> directory on the container. Any changes in the host project directory will be reflected to the container <em>/app</em> directory due to the volume. And if there is a file change, reflex will automatically restart the application.</p>
<h4>Docker Compose</h4>
<p>I use docker-compose with my projects as there are almost always different components involved (i.e., databases, swagger-ui, etc).</p>
<p>The <em>docker-compose.yml</em> file:</p>
<pre><code class="hljs language-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">“3”</span>

<span class="hljs-attr">services:</span>
  <span class="hljs-attr">server:</span>
    <span class="hljs-attr">build:</span>
      <span class="hljs-attr">context:</span> <span class="hljs-string">.</span>
      <span class="hljs-attr">dockerfile:</span> <span class="hljs-string">dev.dockerfile</span>
    <span class="hljs-attr">volumes:</span> <span class="hljs-string">—</span> <span class="hljs-string">.:/app</span>
</code></pre>
<p>Now run the application using <code>docker-compose up</code>. This is how you’ll usually start you app during development.</p>
<p>This docker-compose file is very tiny. Generally you are going to use docker-compose to <strong>compose</strong> several components together. An example of a more practical docker-compose file:</p>
<pre><code class="hljs language-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">"3"</span>

<span class="hljs-attr">services:</span>
  <span class="hljs-attr">server:</span>
    <span class="hljs-attr">build:</span>
      <span class="hljs-attr">context:</span> <span class="hljs-string">.</span>
      <span class="hljs-attr">dockerfile:</span> <span class="hljs-string">dev.dockerfile</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">8080</span><span class="hljs-string">:1323</span> <span class="hljs-comment"># host:container</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">.:/app</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">AWS_REGION</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">AWS_ACCESS_KEY_ID</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">AWS_SECRET_ACCESS_KEY</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">APP_ENV</span>

  <span class="hljs-attr">swagger-ui:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">swaggerapi/swagger-ui</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">8081</span><span class="hljs-string">:8080</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">API_URL</span>

  <span class="hljs-attr">mongo:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo:4</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">mongodb_data:/data/db</span>

  <span class="hljs-attr">mongo-express:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo-express</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">8181</span><span class="hljs-string">:8081</span>

<span class="hljs-attr">volumes:</span>
  <span class="hljs-attr">mongodb_data:</span>
</code></pre>
<p>Here you can see we have four components to our system. The server which runs at port 1323 forwarded to port 8080 on the host. A swagger-ui container for api documentation. A Mongodb container and a Mongo Express container for database. The volume of the Mongodb data ensures our data is persisted even if the Mongo container is destroyed.</p>
<p>When running the system using docker-compose, we can access components from inside other components by just referring the service name. For example, to access the mongo container, we don’t need to use the container’s ip address, rather we can just use <em>mongodb://mongo</em> as the connection string.</p>
<p>If we set up auth in our Mongodb database, we could use the connection string as the following:<br>
<em>mongodb://username:passwd@mongo</em></p>
<p>We can also pass environment variables to our containers as you may have already noticed. We are passing AWS credentials to the server container. You can create a <em>.env</em> file in the root directory and place the environment variables there. Make sure you add the file to <em>.gitignore</em>; you don’t want these secrets to be in the version control.</p>
<p>A sample <em>.env</em> file:</p>
<pre><code class="hljs language-bash">API_URL=[http://localhost:8080/swagger.yaml](http://localhost:8080/swagger.yaml)
AWS_ACCESS_KEY_ID=YOUR_AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY=YOUR_AWS_SECRET_ACCESS_KEY
AWS_REGION=YOUR_AWS_REGION
</code></pre>
<p>Docker compose will by default look for a <em>.env</em> file in the root directory of your project and pass those to the mapped ones on the <em>docker-compose.yml</em> file.</p>
<h3>Production Docker Environment</h3>
<p>For production, I build the image in two stages. The first stage builds the app, the built app and required files (i.e., configuration files) are copied to the second stage which runs the app in an alpine linux based container. Alpine linux has a tiny size and reduces the size of the final image drastically.</p>
<pre><code class="hljs language-dockerfile"><span class="hljs-comment"># build stage</span>
<span class="hljs-keyword">FROM</span> golang:<span class="hljs-number">1.13</span>-alpine AS builder
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>
<span class="hljs-keyword">COPY</span><span class="bash"> . .</span>
<span class="hljs-keyword">RUN</span><span class="bash"> apk add --no-cache git</span>
<span class="hljs-keyword">RUN</span><span class="bash"> go build -v -o go-docker .</span>

<span class="hljs-comment"># final stage</span>
<span class="hljs-keyword">FROM</span> alpine:latest
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /root</span>
<span class="hljs-keyword">RUN</span><span class="bash"> apk --no-cache add ca-certificates</span>
<span class="hljs-keyword">COPY</span><span class="bash"> --from=builder /app/go-docker .</span>
<span class="hljs-keyword">ENTRYPOINT</span><span class="bash"> ./go-docker</span>
</code></pre>
<p>The final image size is 6.97MB only. This is absolutely fantastic because it reduces cost for storing these images in the cloud.</p>
<p>As a comparison, our development environment image size was 832MB. This is because it was a Debian 10 base image.<br>
We can use Alpine linux for development environment as well if we want to, but it isn’t really necessary. We would also need to add several build tools which will eventually increase the image size.</p>
<h4>Deploying to production</h4>
<p>You can take your production image and deploy it to a swarm cluster. You can build the image and push it to a remote repository such as <a href="https://aws.amazon.com/ecr/">AWS’s ECR</a>, and pull it from your server to update the service.</p>
<p>If you want to manage the cluster yourself, I highly recommend you go through the documentation for <a href="https://docs.docker.com/engine/swarm/">Docker Swarm</a>; and use it.</p>
<p>Most of the time, I prefer running the cluster in a managed service such as <a href="https://aws.amazon.com/ecs/">AWS’s ECS</a>. This saves both time and effort in creating and managing the cluster. A managed service also provides a lot of tools off the shelf such as viewing logs and metric. We can create revisions of task definitions and update the services with zero downtime.</p>
<h3>Conclusion</h3>
<p>I have been using Docker for a while now with multiple stacks and can assure you that it will ease the life of everyone in your team if used well. Getting new team members on-boarded can become a breeze when using Docker.</p>
<p>Find the source code for the above example <a href="https://github.com/war1oc/go-docker">here</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Sharing code between iOS and Vapor]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/08/22/Sharing-code-between-iOS-and-Vapor</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/08/22/Sharing-code-between-iOS-and-Vapor</guid>
            <pubDate>Thu, 22 Aug 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Code reusability has always been one of the ultimate goals for us developers. Building your backend with Vapor not only gives you highly scalable apps with shallow memory footprint, but also the potential to share some of your backend code with your iOS app without paying the extra cost. The downside comes when you try to share networking code between iOS and Vapor. This is due to the fact that Vapor is built on the top of <a href="https://github.com/apple/swift-nio">SwiftNIO</a>, which means you can not share your URLSession based code. SwiftNIO is Event-driven (based on Futures and promises) which is not compatible with URLSession's callback approach. However, the good news is that Apple released <a href="https://github.com/apple/swift-nio-transport-services">NIO Transport Services</a> - "Extensions for SwiftNIO to support Apple platforms as first-class citizens." In this post, I will outline some basic concepts about sharing entity objects between iOS and Vapor.</p>
<h2>Create entity objects </h2>
<p>In the main repository, we will store the Swift package which holds the framework target with our codable entity objects:</p>
<pre><code class="hljs language-bash">swift package init
</code></pre>
<p>Add your entity objects under the <code>Sources/MyFramework/</code> directory. They have to be <code>public</code> and conform to the <code>Codable</code> protocol, e.g.:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">public</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">SomeResponse</span>: <span class="hljs-title class_">Codable</span> {
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">let</span> id: <span class="hljs-type">Int</span>
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">let</span> name: <span class="hljs-type">String</span>
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">let</span> isDefault: <span class="hljs-type">Bool</span>

  <span class="hljs-keyword">enum</span> <span class="hljs-title class_">CodingKeys</span>: <span class="hljs-title class_">String</span>, <span class="hljs-title class_">CodingKey</span> {
    <span class="hljs-keyword">case</span> id
    <span class="hljs-keyword">case</span> name
    <span class="hljs-keyword">case</span> isDefault <span class="hljs-operator">=</span> <span class="hljs-string">"is_default"</span>
  }
}
</code></pre>
<h2>Create shim repository </h2>
<p>We need to create a Carthage shim repository (e.g. <code>https://github.com/MyOrganization/MyFramework-shim</code>), the idea is to have some "glue" between the main repository which is Swift Package Manager based and the iOS project. Of course, if your iOS project supports Swift Package Manager, this is not necessary. In the newly created repository you need to add your main repository as a git submodule like so:</p>
<pre><code class="hljs language-bash">git submodule add https://github.com/MyOrganization/MyFramework
</code></pre>
<p>After that, in the root of your shim repository, you need to create a few symbolic links, for the <code>Package.swift</code>, <code>Sources</code> and <code>Tests</code> :</p>
<pre><code class="hljs language-bash">ln -s MyFramework/Package.swift Package.swift
ln -s MyFramework/Sources Sources
ln -s MyFramework/Tests Tests
</code></pre>
<p>Carthage requires shared <code>.xcscheme</code>s, so we first need to generate <code>.xcodeproj</code>:</p>
<pre><code class="hljs language-bash">swift package generate-xcodeproj
</code></pre>
<p>Open the <code>.xcodeproj</code> in the Xcode, edit the schemes and ensure they are shared. Then <code>git commit</code> and <code>git push</code> the changes.</p>
<h2>Add Carthage shim to your iOS app</h2>
<p>Open <code>Cartfile</code> and add the Carthage shim, e.g.:</p>
<pre><code class="hljs language-swift">github <span class="hljs-string">"MyOrganization/MyFramework-shim"</span> <span class="hljs-string">"master"</span>
</code></pre>
<p>After that, run <code>carthage update</code>.</p>
<h2>Add the package to your Vapor app</h2>
<p>Open <code>Package.swift</code> and add next the line under <code>dependencies</code>:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> package <span class="hljs-operator">=</span> <span class="hljs-type">Package</span>(
  name: <span class="hljs-string">"MyAppName"</span>,
  dependencies: [
  .package(url: https:<span class="hljs-comment">//github.com/MyOrganization/MyFramework.git", branch: "master")</span>
  ]
)
</code></pre>
<p>Don't forget to add <code>MyFramework</code> under target dependencies, e.g.:</p>
<pre><code class="hljs language-swift"> targets: [
    .target(name: <span class="hljs-string">"App"</span>, dependencies: [
  <span class="hljs-string">"MyFramework"</span>,
]);
</code></pre>
<h2>Conclusion </h2>
<p>With a few little tricks, we made our entity objects sharable between iOS and Vapor. However, this strategy could be utilized to share any functionality which does not depend on iOS SDKs.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Google Docs viewer with Flutter]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/08/21/flutter-google-docs</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/08/21/flutter-google-docs</guid>
            <pubDate>Wed, 21 Aug 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Flutter is a powerful framework when it comes to rendering complex nested widgets with countless styles and customizations. This small project will explain the basics of using Google APIs and Services, in particular Google Drive, Google Docs and rendering a Google Document using Flutter widgets. The app supports basic text styling and displaying of images included in the document. Let's dive in and see how Flutter makes it easy to dynamically structure a complex page without any performance issues.</p>
<h2>About the structure</h2>
<p>The project is composed of three pages: LoginPage (handles the user authentication), FilesPage (displays all the Google Docs documents), DocumentPage (displays the content of the document) and AuthManager which handles the authentication that I can reuse in all pages to keep them clean. There are also two custom widgets to display Ripple effect and Circular Progress but I will not cover them because they are used only for convenience.</p>
<p><img src="/assets/img/articles/2019-08-21-flutter-google-docs/flutter-google-docs-project-structure.webp" alt="project structure"></p>
<p>Let's start with the Login page. I'm using the <a href="https://pub.dev/packages/google_sign_in">google_sign_in</a> package to handle the user authentication.</p>
<h2>Actual code</h2>
<p>The <code>AuthManager</code> code is self-explanatory and only makes sure all the errors are handled and defines the <code>GoogleSignIn</code> scopes so I'm granted the required permissions to the user account.</p>
<ul>
<li><code>email</code> access to user info like email</li>
<li><code>documents.readonly</code> access to read the documents</li>
<li><code>drive.readonly</code> access to fetch the list of documents</li>
</ul>
<pre><code class="hljs language-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:google_sign_in/google_sign_in.dart'</span>;

GoogleSignIn _googleSignIn = GoogleSignIn(
  scopes: &#x3C;<span class="hljs-built_in">String</span>>[
    <span class="hljs-string">'email'</span>,
    <span class="hljs-string">'https://www.googleapis.com/auth/documents.readonly'</span>,
    <span class="hljs-string">'https://www.googleapis.com/auth/drive.readonly'</span>
  ],
);

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AuthManager</span> </span>{
  <span class="hljs-keyword">static</span> Future&#x3C;GoogleSignInAccount> signIn() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> account = <span class="hljs-keyword">await</span> _googleSignIn.signIn();
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'account: <span class="hljs-subst">${account?.toString()}</span>'</span>);
      <span class="hljs-keyword">return</span> account;
    } <span class="hljs-keyword">catch</span> (error) {
      <span class="hljs-built_in">print</span>(error);
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
    }
  }

  <span class="hljs-keyword">static</span> Future&#x3C;GoogleSignInAccount> signInSilently() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">var</span> account = <span class="hljs-keyword">await</span> _googleSignIn.signInSilently();
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'account: <span class="hljs-subst">$account</span>'</span>);
    <span class="hljs-keyword">return</span> account;
  }

  <span class="hljs-keyword">static</span> Future&#x3C;<span class="hljs-keyword">void</span>> signOut() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      _googleSignIn.disconnect();
    } <span class="hljs-keyword">catch</span> (error) {
      <span class="hljs-built_in">print</span>(error);
    }
  }
}
</code></pre>
<p>And this is how our Login page looks like.</p>
<p><img src="/assets/img/articles/2019-08-21-flutter-google-docs/flutter-google-login-page.webp" alt="login page"></p>
<pre><code class="hljs language-dart"><span class="hljs-meta">@override</span>
Widget build(BuildContext context) {
  <span class="hljs-keyword">return</span> Scaffold(
    body: Container(
      color: Colors.deepPurple,
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: &#x3C;Widget>[
            Spacer(),
            Text(
              <span class="hljs-string">'Log in using your Google account'</span>,
              style: TextStyle(fontSize: <span class="hljs-number">16</span>, color: Colors.white),
            ),
            SizedBox(height: <span class="hljs-number">20.0</span>),
            RaisedButton(
              child: Text(<span class="hljs-string">'Log in'</span>),
              onPressed: _handleSignIn,
            ),
            Spacer(),
          ],
        ),
      ),
    ),
  );
}
</code></pre>
<p>When the user enters the Login page I check silently if the user was already signed-in in the past (the Authentication token is cached by the <strong>google_sign_in</strong> package). If the user is already signed-in <code>AuthManager.signInSilently()</code> returns the user account and I redirect to the <strong>Files</strong> page.</p>
<pre><code class="hljs language-dart">Future&#x3C;<span class="hljs-keyword">void</span>> signInSilently() <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">var</span> account = <span class="hljs-keyword">await</span> AuthManager.signInSilently();
  <span class="hljs-keyword">if</span> (account != <span class="hljs-keyword">null</span>) {
    Navigator.pushReplacementNamed(context, AppRoute.files);
  }
}
</code></pre>
<p>In case this is first app launch the user has to tap the <strong>Login</strong> button and the <strong>google_sign_in</strong> package will show the <em>Account chooser</em> and the <em>Permissions</em> dialogs as showed below.</p>
<p><img src="/assets/img/articles/2019-08-21-flutter-google-docs/flutter-google-signin-account-dialog.webp" alt="google signin account dialog"></p>
<p><img src="/assets/img/articles/2019-08-21-flutter-google-docs/flutter-google-signin-permissions-dialog.webp" alt="google signin permissions dialog"></p>
<p>In case the sign-in is successful, the Navigator is used to redirect the user to the Files page:</p>
<pre><code class="hljs language-dart">Future&#x3C;<span class="hljs-keyword">void</span>> _handleSignIn() <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">var</span> account = <span class="hljs-keyword">await</span> AuthManager.signIn();
  <span class="hljs-keyword">if</span> (account != <span class="hljs-keyword">null</span>) {
    Navigator.pushReplacementNamed(context, AppRoute.files);
  }
}
</code></pre>
<p><code>AppRoute</code> holds the route names. The routing is organized as follows:</p>
<pre><code class="hljs language-dart">Route onGenerateRoute(RouteSettings settings) {
  <span class="hljs-keyword">switch</span> (settings.name) {
    <span class="hljs-keyword">case</span> AppRoute.login:
      <span class="hljs-keyword">return</span> MaterialPageRoute(
        builder: (context) => LoginPage(),
      );
    <span class="hljs-keyword">case</span> AppRoute.files:
      <span class="hljs-keyword">return</span> MaterialPageRoute(
        builder: (context) => FilesPage(),
      );
    <span class="hljs-keyword">case</span> AppRoute.<span class="hljs-built_in">document</span>:
      <span class="hljs-keyword">final</span> args = settings.arguments <span class="hljs-keyword">as</span> <span class="hljs-built_in">Map</span>;
      <span class="hljs-keyword">return</span> MaterialPageRoute(
        builder: (context) => DocumentPage(fileId: args[Args.fileId]),
      );
    <span class="hljs-keyword">default</span>:
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Args</span> </span>{
  <span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> fileId = <span class="hljs-string">'fileId'</span>;
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppRoute</span> </span>{
  <span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> login = <span class="hljs-string">'/login'</span>;
  <span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> files = <span class="hljs-string">'/files'</span>;
  <span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-built_in">document</span> = <span class="hljs-string">'/document'</span>;
}
</code></pre>
<p>Let's switch to the Files page. Despite the fact the code looks longer there's only a small important part where I fetch the file list using Google Drive Api. The rest is related to showing the file list UI.</p>
<pre><code class="hljs language-dart">  Future&#x3C;<span class="hljs-keyword">void</span>> _loadFiles() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">if</span> (_currentUser == <span class="hljs-keyword">null</span>) <span class="hljs-keyword">return</span>;

    GoogleSignInAuthentication authentication =
        <span class="hljs-keyword">await</span> _currentUser.authentication;
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'authentication: <span class="hljs-subst">$authentication</span>'</span>);
    <span class="hljs-keyword">final</span> client = MyClient(defaultHeaders: {
      <span class="hljs-string">'Authorization'</span>: <span class="hljs-string">'Bearer <span class="hljs-subst">${authentication.accessToken}</span>'</span>
    });
    DriveApi driveApi = DriveApi(client);
    <span class="hljs-keyword">var</span> files = <span class="hljs-keyword">await</span> driveApi.files
        .list(q: <span class="hljs-string">'mimeType=\'application/vnd.google-apps.document\''</span>);
    setState(() {
      _items = files.items;
      _loaded = <span class="hljs-keyword">true</span>;
    });
  }
</code></pre>
<p>I use the access token retrieved from <code>GoogleSignInAuthentication</code> object to access all the Google APIs. One thing to note here is that a custom HTTP client is used so the <code>Authorization</code> header is added to all the requests made by the Drive and Docs API clients. An alternative solution can be to use the <em>googleapis_auth</em> package which provides a few other ways to authenticate the user but I thought they required more code to implement.</p>
<p>To display the file list I use a simple ListView widget.</p>
<pre><code class="hljs language-dart">  Widget <span class="hljs-keyword">get</span> _listView {
    <span class="hljs-keyword">return</span> Container(
      color: Colors.white.withOpacity(<span class="hljs-number">0.8</span>),
      child: ListView.separated(
        itemBuilder: (context, index) {
          <span class="hljs-keyword">var</span> item = _items[index];
          <span class="hljs-keyword">final</span> formatter = DateFormat(<span class="hljs-string">'hh:mm EEE, MMM d, yyyy'</span>);

          <span class="hljs-keyword">return</span> Card(
            elevation: <span class="hljs-number">0.0</span>,
            shape: RoundedRectangleBorder(
              side: BorderSide(width: <span class="hljs-number">1.0</span>, color: Colors.blue.withOpacity(<span class="hljs-number">0.2</span>)),
              borderRadius: BorderRadius.circular(<span class="hljs-number">8.0</span>),
            ),
            color: Colors.white.withOpacity(<span class="hljs-number">0.8</span>),
            child: RippleWidget(
              radius: <span class="hljs-number">8.0</span>,
              onTap: () {
                <span class="hljs-keyword">var</span> args = {Args.fileId: item.id};
                Navigator.pushNamed(context, AppRoute.<span class="hljs-built_in">document</span>,
                    arguments: args);
              },
              child: Padding(
                padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">8.0</span>),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: &#x3C;Widget>[
                    Text(
                      item.title,
                      style: TextStyle(
                        fontSize: <span class="hljs-number">15.0</span>,
                        fontWeight: FontWeight.bold,
                        color: Colors.black.withOpacity(<span class="hljs-number">0.7</span>),
                      ),
                    ),
                    SizedBox(height: <span class="hljs-number">4.0</span>),
                    Row(
                      children: &#x3C;Widget>[
                        Expanded(
                          child: Text(
                            formatter.format(item.createdDate),
                            style: TextStyle(
                              fontSize: <span class="hljs-number">12.0</span>,
                            ),
                          ),
                        ),
                        SizedBox(width: <span class="hljs-number">8.0</span>),
                        Text(
                          KtList.from(item.ownerNames).joinToString(),
                          style: TextStyle(
                            fontSize: <span class="hljs-number">12.0</span>,
                          ),
                        ),
                      ],
                    ),
                  ],
                ),
              ),
            ),
          );
        },
        separatorBuilder: (context, index) {
          <span class="hljs-keyword">return</span> SizedBox(height: <span class="hljs-number">8.0</span>);
        },
        itemCount: _items.length,
        padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">8.0</span>),
      ),
    );
  }
</code></pre>
<p><img src="/assets/img/articles/2019-08-21-flutter-google-docs/google-drive-file-list.webp" alt="google drive file list"></p>
<p>There's also a logout button to log-out the user and open the Login page.</p>
<pre><code class="hljs language-dart">  _logout() {
    setState(() {
      _currentUser = <span class="hljs-keyword">null</span>;
    });
    AuthManager.signOut();
    Navigator.pushReplacementNamed(context, AppRoute.login);
  }
</code></pre>
<p>When we tap on a list item the <code>fileId</code> is passed to the <strong>Document</strong> page so we can fetch our document using Docs API.
In the Document page first thing called when the user enters the screen is <code>_loadDocument()</code> which fetches the document and extracts the data we intend to display. Currently only a small subset of all the document features is supported.</p>
<pre><code class="hljs language-dart">  Future&#x3C;<span class="hljs-keyword">void</span>> _loadDocument() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">if</span> (_currentUser == <span class="hljs-keyword">null</span>) <span class="hljs-keyword">return</span>;

    GoogleSignInAuthentication authentication =
        <span class="hljs-keyword">await</span> _currentUser.authentication;
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'authentication: <span class="hljs-subst">$authentication</span>'</span>);
    <span class="hljs-keyword">final</span> client = MyClient(defaultHeaders: {
      <span class="hljs-string">'Authorization'</span>: <span class="hljs-string">'Bearer <span class="hljs-subst">${authentication.accessToken}</span>'</span>
    });

    <span class="hljs-keyword">final</span> docsApi = docsV1.DocsApi(client);
    <span class="hljs-keyword">var</span> <span class="hljs-built_in">document</span> = <span class="hljs-keyword">await</span> docsApi.documents.<span class="hljs-keyword">get</span>(widget.fileId);
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'document.title: <span class="hljs-subst">${document.title}</span>'</span>);
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'content.length: <span class="hljs-subst">${document.body.content.length}</span>'</span>);
    _parseDocument(<span class="hljs-built_in">document</span>);
  }
</code></pre>
<p>The <a href="https://pub.dev/packages/kt_dart">kt_dart</a> package is used to simplify parsing of the interested fields. <strong>kt_dart</strong> wraps Dart List, Map, Set classes with custom classes prefixed with <em>Kt</em> which bring Kotlin operators used in <em>Kotlin Standard library</em> as they are much more complete compared to Dart standard classes.</p>
<pre><code class="hljs language-dart">  Future&#x3C;<span class="hljs-keyword">void</span>> _parseDocument(docsV1.Document <span class="hljs-built_in">document</span>) <span class="hljs-keyword">async</span> {
    _documentTitle = <span class="hljs-built_in">document</span>.title;
    <span class="hljs-keyword">var</span> content = KtList.from(<span class="hljs-built_in">document</span>.body.content);

    <span class="hljs-keyword">final</span> elements = content
        .mapNotNull(
          (element) => (element?.paragraph?.elements != <span class="hljs-keyword">null</span> ||
                  element?.paragraph?.positionedObjectIds != <span class="hljs-keyword">null</span>)
              ? element
              : <span class="hljs-keyword">null</span>,
        )
        .asList();

    <span class="hljs-keyword">var</span> inlineObjects = emptyMap&#x3C;<span class="hljs-built_in">String</span>, DocImageData>();
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">document</span>.inlineObjects?.isNotEmpty == <span class="hljs-keyword">true</span>) {
      inlineObjects = KtMap.from(<span class="hljs-built_in">document</span>.inlineObjects).map((inlineObject) {
        <span class="hljs-keyword">var</span> embeddedObject =
            inlineObject.value.inlineObjectProperties.embeddedObject;
        <span class="hljs-keyword">return</span> KtPair(
          inlineObject.key,
          DocImageData(
            url: embeddedObject.imageProperties.contentUri,
            width: embeddedObject.size.width.magnitude,
            height: embeddedObject.size.height.magnitude,
          ),
        );
      }).associate((pair) => pair);
    }

    <span class="hljs-keyword">var</span> positionedObjects = emptyMap&#x3C;<span class="hljs-built_in">String</span>, DocImageData>();
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">document</span>.positionedObjects?.isNotEmpty == <span class="hljs-keyword">true</span>) {
      positionedObjects =
          KtMap.from(<span class="hljs-built_in">document</span>.positionedObjects).map((positionedObject) {
        <span class="hljs-keyword">var</span> embeddedObject =
            positionedObject.value.positionedObjectProperties.embeddedObject;
        <span class="hljs-keyword">return</span> KtPair(
          positionedObject.key,
          DocImageData(
            url: embeddedObject.imageProperties.contentUri,
            width: embeddedObject.size.width.magnitude,
            height: embeddedObject.size.height.magnitude,
          ),
        );
      }).associate((pair) => pair);
    }

    setState(() {
      _listItems = elements;
      _imagesData = inlineObjects.plus(positionedObjects).asMap();
      _contentLoaded = <span class="hljs-keyword">true</span>;
    });
  }
</code></pre>
<p>What we are interested in are the paragraph text, inline and positioned images. The text can be found in the <code>ParagraphElement.textRun.content</code> field as you can see from the image below.</p>
<p><img src="/assets/img/articles/2019-08-21-flutter-google-docs/google-doc-paragraph-content.webp" alt="google drive file list"></p>
<p>The inline image id is stored in the <code>ParagraphElement.inlineObjectElement.inlineObjectId</code>.</p>
<p><img src="/assets/img/articles/2019-08-21-flutter-google-docs/google-doc-inline-object.webp" alt="google drive file list"></p>
<p>The positioned image ids are stored directly in the document in <code>Document.positionedObjects</code>. In our case we treat positioned images and inline images the same and show them inline just to make things easier.
The most important part in all this is probably mapping the Document elements to Flutter Widgets. All of this is happening in <code>_elementToWidget()</code> which takes an element we support and create a widget for it.</p>
<pre><code class="hljs language-dart">  Widget _elementToWidget(docsV1.StructuralElement element) {
    <span class="hljs-keyword">final</span> alignment =
        _getAlignment(element.paragraph.paragraphStyle?.alignment);
    <span class="hljs-keyword">final</span> paragraphSpans = KtList.from(element.paragraph.elements)
        .mapNotNull((element) => element?.textRun)
        .map(
      (textRun) {
        <span class="hljs-keyword">if</span> (textRun.textStyle != <span class="hljs-keyword">null</span>) {
          <span class="hljs-keyword">return</span> TextSpan(
            text: textRun.content,
            style: _getTextStyle(
              element.paragraph.paragraphStyle?.namedStyleType,
              textRun.textStyle,
            ),
          );
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-keyword">return</span> TextSpan(text: textRun.content);
        }
      },
    );
    <span class="hljs-keyword">var</span> paragraphText = paragraphSpans.isNotEmpty()
        ? Container(
            width: <span class="hljs-built_in">double</span>.infinity,
            child: RichText(
              textAlign: alignment,
              text: TextSpan(
                children: paragraphSpans.asList(),
                style: TextStyle(color: Colors.black),
              ),
            ),
          )
        : <span class="hljs-keyword">null</span>;

    <span class="hljs-keyword">var</span> paragraphInlineImages = [];
    <span class="hljs-keyword">if</span> (element.paragraph.elements != <span class="hljs-keyword">null</span>) {
      paragraphInlineImages = KtList.from(element.paragraph.elements)
          .mapNotNull((element) => element.inlineObjectElement?.inlineObjectId)
          .map((<span class="hljs-built_in">String</span> objectId) => _imagesData[objectId])
          .map((DocImageData imgData) {
        <span class="hljs-keyword">return</span> Image.network(
          imgData.url,
          width: imgData.width,
          height: imgData.height,
        );
      }).asList();
    }

    <span class="hljs-keyword">var</span> paragraphPositionedImages = [];
    <span class="hljs-keyword">if</span> (element.paragraph.positionedObjectIds != <span class="hljs-keyword">null</span>) {
      paragraphPositionedImages =
          KtList.from(element.paragraph.positionedObjectIds)
              .map((<span class="hljs-built_in">String</span> objectId) {
        <span class="hljs-keyword">return</span> _imagesData[objectId];
      }).map((DocImageData imgData) {
        <span class="hljs-keyword">return</span> Image.network(
          imgData.url,
          width: imgData.width,
          height: imgData.height,
        );
      }).asList();
    }

    <span class="hljs-keyword">final</span> hasImages = (paragraphInlineImages.isNotEmpty == <span class="hljs-keyword">true</span>) ||
        (paragraphPositionedImages.isNotEmpty == <span class="hljs-keyword">true</span>);

    <span class="hljs-keyword">return</span> hasImages
        ? Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: &#x3C;Widget>[
              ...paragraphInlineImages,
              ...paragraphPositionedImages,
              <span class="hljs-keyword">if</span> (paragraphText != <span class="hljs-keyword">null</span>) paragraphText,
            ],
          )
        : (paragraphText != <span class="hljs-keyword">null</span>) ? paragraphText : Container();
  }
</code></pre>
<p>Text paragraphs are mapped to <code>RichText</code> widgets so they can support text styles (bold, italic), colors, alignment, size, background color. The images are displayed without any modifications by only applying the provided size.</p>
<p>![google document rendering](/assets/img/articles/2019-08-21-flutter-google-docs/google-doc-rendering.webp</p>
<p>The widgets are displayed in a LitView lazily. This way we don't create all the widgets at once but only as the user scrolls the page.</p>
<p>In order to run this project you will need to create a Firebase project and download the <code>google-services.json</code>. The process is also described in <a href="https://pub.dev/packages/googleapis_auth">googleapis_auth</a> and <a href="https://pub.dev/packages/google_sign_in">google_sign_in</a> pages.
Remember to also enable the Google Docs and Google Drive services in <strong>APIs and Services</strong> (console)[<a href="https://console.developers.google.com">https://console.developers.google.com</a>]. The services will usually take around 5-10 minutes to be enabled.</p>
<p>![google document rendering](/assets/img/articles/2019-08-21-flutter-google-docs/google-apis-services.webp</p>
<p>And last but not least we have to configure the <strong>support email</strong> in your newly created Firebase project otherwise the APIs will return <em>ApiException 12500</em> error and not work (<a href="https://stackoverflow.com/a/56313867/1502079">setting support email in Firebase</a>).</p>
<p>The <a href="https://github.com/nodes-android/google-docs-viewer-flutter">source code</a> for this project is available on <a href="https://github.com/nodes-android/google-docs-viewer-flutter">GitHub</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Universal App Platform - State of Progressive Web Apps in 2019]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/08/16/Universal-App-Platform</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/08/16/Universal-App-Platform</guid>
            <pubDate>Fri, 16 Aug 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>What started as best of both worlds, web and native, Progressive Web Apps can now also be installed and run on desktop, from Windows to Mac, from Linux to Chrome OS. <strong>You code once and it can run anywhere</strong>, ensuring PWAs as the <strong>Universal App Platform.</strong></p>
<p><img src="/assets/img/articles/2019-08-16-Universal-App-Platform/pwa.webp" alt=""></p>
<p>In March 2019, <a href="https://developers.google.com/web/updates/2019/03/nic73">it was announced that PWAs can now be installed on Mac OS</a>, thus bringing support for PWAs to all desktop and mobile platforms.</p>
<p><a href="https://developers.google.com/web/updates/2019/07/nic76#pwa-install">As of Chrome 76</a>, announced in July 2019, whenever any PWA URL is hit, the install prompt would be shown in the address bar/omnibox, thus making it easier to discover.</p>
<p><img src="/assets/img/articles/2019-08-16-Universal-App-Platform/plusSign.webp" alt=""></p>
<p>Here you can see the (+) sign. It is the install prompt. Once clicked, you can run your PWA on macOS in a separate window!</p>
<p><img src="/assets/img/articles/2019-08-16-Universal-App-Platform/demo.webp" alt=""></p>
<p>Progressive Web Apps, PWAs, use modern web capabilities to deliver an app-like user experience. ‘App-like’ referring to the fact that PWAs are the perfect symbiosis between web and native apps.</p>
<p>I would highly recommend you all to watch this year’s <a href="https://youtu.be/2KhRmFHLuhE">Google I/O 19 talk by Paul Covell and Dominick Ng</a>, which tells you about the traditional differences between web and native apps and how PWAs successfully manage to bring to the table the best of both worlds.</p>
<p>Back in June 2015, <a href="https://medium.com/@slightlylate">Alex Russell</a> first posted about the <a href="https://infrequently.org/2015/06/progressive-apps-escaping-tabs-without-losing-our-soul/">Progressive Web App</a> and the idea soon began to gain traction.</p>
<blockquote>
<p>“Critically, these apps can <a href="https://www.youtube.com/watch?v=d5_6yHixpsQ">deliver an even better user experience</a> than traditional web apps. Because it’s also possible to build this performance in as progressive enhancement, the tangible improvements make it worth building this way regardless of “appy” intent.</p>
</blockquote>
<blockquote>
<p><a href="http://fberriman.com/">Frances</a> called them “Progressive Open Web Apps” and we both came around to just “Progressive Apps”. They existed before, but now they have a name.”</p>
</blockquote>
<p>When users first came across a pop-up in their mobile chrome saying: “Do you want to add this site to your Home Screen?”, and pressed ‘Yes’, they noticed something amazing, beyond a simple bookmark, on their mobile devices.</p>
<p>After installation, users could see an icon on their phone screen. When tapped it opened in full screen, without any tabs of the browser. It became their first app that didn’t come from any App store/Play store!</p>
<p>At Google I/O 2016, when <a href="https://twitter.com/jaffathecake">Jake Archibald</a> showed <a href="https://www.youtube.com/watch?v=cmGr0RszHc8">the magic of how web apps can work offline</a>, developers started to develop and convert existing applications into PWAs with much interest.</p>
<p>I had the privilege to introduce <a href="https://slides.com/saadbinamjad/pwa#/"><strong>PWAs</strong></a> in my office back in September 2017, and fast forward to 2019 we have already deployed PWAs for production as well.</p>
<p>But let’s go over the <strong>core features</strong> of PWAs again today and relate their state in 2019.</p>
<p><img src="/assets/img/articles/2019-08-16-Universal-App-Platform/pwaDefinition.webp" alt=""></p>
<p>As <a href="https://twitter.com/dominickng">Dominick Ng</a> puts it, PWAs are just like another <strong>website but with some added pieces in it</strong>. And the pieces are increasing in number, progressively with time.</p>
<p>Being <strong>progressive</strong> has several meanings. PWA <strong>app experiences get better with time</strong>, progressively, as frequent usage leads to better caching. Push notifications, for various events inside the applications, make it even more engaging for the user to keep on using the app. PWAs never discriminate against who can install and use them. From a mobile user with a 2G connection to a 4G user, both can use it without any data concerns. It doesn’t even discriminate which device they are using, unlike certain native apps and their devices/OS bindings.</p>
<p><strong>Incremental feature sets</strong>, expanded capabilities of modern web APIs, and their adoption, powers PWA to deliver more native-like experiences. For example, we will soon have a native file system API integrated into PWAs, that will allow users to access their phone storage directly using PWAs, and that too with prior consent.</p>
<p>PWAs are <strong>reliable</strong>, can only be served over https and are also <strong>fresh</strong>, meaning that users do not need to run updates whenever there is a change from the developer’s side, unlike those of native applications.</p>
<p><strong>Responsiveness</strong> and <strong>full-screen UI</strong>, powered by the Web App Manifest (a JSON-based manifest file that controls the web application’s name, links to icons, sets the default orientation of the app), also helps to generate an <strong>app-like</strong> experience for the users.</p>
<p>Hard to differentiate which one is native and which one is a PWA.</p>
<p><img src="/assets/img/articles/2019-08-16-Universal-App-Platform/whichOneIsWhich.webp" alt=""></p>
<p>Features like <strong>offline support</strong> which are powered by Service Workers (think of it as a proxy server between your browser and the network, which controls what requests are being sent to and from the network) help users to engage more often with their application and thus feeling a sense of ownership like that with any other native apps independent of network connection.</p>
<p>Service Worker’s ability over caching and how requests are being handled ensure implementation of strategies like Cache First Strategies, which offers PWAs as a <strong>fast</strong> application that consumes <strong>less network</strong> <strong>data.</strong></p>
<p>The App-Shell on mobile allows PWAs to have a <strong>faster first-paint</strong>, i.e. how fast the screen loads before dynamic content is loaded on a user's device, even with flaky internet connections.</p>
<p>Since PWAs are applications which we <strong>do not need to download from the app stores/play stores</strong>, it is like any other website <strong>link</strong> that can be found in a google search result and also can be shared to anybody. When anyone visits that URL, it will ask users to <strong>install</strong> the app on their phone or desktop. And done. It is installed! Thus making it highly <strong>discoverable</strong> over native apps, as still now we can’t find app links in google search results.</p>
<p>But as of now, many clients want to move their PWAs to play stores as to be <strong>discoverable in play stores</strong>. Yes, you can also ship your <strong>PWA to the Play store</strong>, as Chrome announced <a href="https://developers.google.com/web/updates/2019/02/using-twa">Trusted Web Activity (TWA)</a> in February 2019.</p>
<p>In short, TWAs are better than traditional sandboxed WebViews, as they handle more features than PWAs in browsers, e.g. OAuth redirections, sharing cookies, local storage, etc.</p>
<p>When it comes to <strong>expanding capabilities</strong>, there are many modern web APIs that can be used by PWAs. Also, the PWA Google team showed a roadmap as to what to expect and the tentative launches for the in-flight APIs.</p>
<p>However, it is <strong>true that adoption of most of the expanded capabilities is not present in Safari as of now</strong>, but the fact that Service Workers are included from Safari 11.1 onwards, PWA features similar to Chrome will surely be adopted over time. <a href="https://medium.com/@firt/progressive-web-apps-on-ios-are-here-d00430dee3a7">For now, features like geolocation, sensors (Magnetometer, Accelerometer, Gyroscope), and camera are included but push notifications are still not available to iOS PWA experiences.</a></p>
<p>There is a <a href="https://developers.google.com/web/progressive-web-apps/checklist">checklist</a> outlining the characteristics of a PWA. Feel free to check it out. And also once you make an app, you can use a tool, <a href="https://developers.google.com/web/tools/lighthouse/">Lighthouse</a> to see your PWA score to better understand how well your app performs as a PWA.</p>
<blockquote>
<p>You can make a web app, you can make them offline first, turn them into PWAs, leverage installation, and system integrations, and take advantage of the best of the web and native worlds. Hopefully, we’ve shown you just how compelling Progressive Web Apps are as a universal app platform and how much more compelling they’re going to get over the next year.” — Dominick Ng at <a href="https://www.youtube.com/watch?v=2KhRmFHLuhE&#x26;feature=youtu.be&#x26;list=LLyozycENfJgBB6BlL5BfFQw&#x26;t=1987">Google I/O 2019</a>.</p>
</blockquote>
<p>When <a href="http://www.comscore.com/Insights/Presentations-and-Whitepapers/2016/The-2016-US-Mobile-App-Report">comScore 2016 reported</a> that <strong>1/2 of U.S. smartphone users downloaded 0 apps/month</strong>, it set off an alarm and a bit of a shock in the ecosystem. Native apps are always meant to stay for their specific use cases, that certainly cannot be denied. However, with PWAs making giant steps in redefining how we view and develop web applications, a paradigm shift awaits. The development landscape for applications in the coming years will surely change as users look for applications in search results and appreciate ways to easily download/install apps with minimum prerequisites. Clients would also love to re-use a single code base for multiple platforms to cut down costs without hampering quality.</p>
<p>That's all for today, happy coding folks!</p>
<p><em>Article Photo by <a href="https://unsplash.com/@skmuse_">Suad Kamardeen</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Fetching files easily in Vapor when you are writing tests]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/08/15/Fetching-files-easily-in-Vapor-when-you-are-writing-tests</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/08/15/Fetching-files-easily-in-Vapor-when-you-are-writing-tests</guid>
            <pubDate>Thu, 15 Aug 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><a href="https://vapor.codes/">Vapor</a> has a nifty build-in feature to derive the working directory of a project.
This makes it easy for you to fetch files from your project and serve their content;
fx if you want to seed some data in your database, if you are building an initial mock api at the beginning of a project, if you want to ease the process of testing data parsing, or something fourth.<br>
In this tutorial we'll use Vapor's build in function to derive the working directory and thereby easily fetch files from our project.
We'll then decode the content of our file into a custom Swift object and test that it works as expected<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>.</p>
<h2>Get started</h2>
<p>First we'll create a new Vapor project in the terminal<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a></p>
<pre><code class="hljs language-bash">vapor new nobel-laureates-example
</code></pre>
<p>next we'll relocate into the project folder and generate our Xcode project.</p>
<pre><code class="hljs language-bash"><span class="hljs-built_in">cd</span> nobel-laureates-example/
vapor xcode -y
</code></pre>
<p>You now have a fully functioning Vapor project; All you have to do is run it.</p>
<h2>Where to store your files</h2>
<p>Next step is to decide where to store our files. Our newly created Vapor project came with
a number of predefined folders. The app itself goes into <em>Sources</em>, while all test should be
in <em>Tests</em>, finally are the <em>Public</em> and <em>Resources</em> folders.
(You may not have the <em>Resources</em> by default, but then you can just create it yourself).<br>
The <em>Public</em> folder should only be used for files that you want anybody from outside of the app to get access to.
Those could be your CSS and Javascript files for your frontend or a document anybody should be able to download.<br>
<em>Resources</em> is for files that you do not want the outside to gain access to (but at the same time not super secret files that need encryption, password etc.).<br>
We will be storing our files in <em>Resources</em> for this tutorial.</p>
<h2>Fetching the file</h2>
<p>Add a sub-folder called <em>Utilities</em> inside <em>Sources</em>. This is for any general methods we'll be needing during the tutorial.
Inside <em>Utilities</em> we'll add a new Swift file called "Data+fromJSONFile.swift".</p>
<p>First we'll <code>import Core</code> so we can get easy access to the working directory. <a href="https://github.com/vapor/core"><code>Core</code></a> is a utility package from Vapor that contains tools for byte manipulation, Codable, OS APIs, and debugging.
Then we'll extend <code>Data</code> with a method, which we'll call <code>fromFile</code>.</p>
<p><code>fromFile(_:,folder:)</code> takes the file name and the relative location of the file as arguments.
It then returns the content of the file as <code>Data</code> (Since we are only going to work with json files in this tutorial, we'll add the relative path to the json folder as a default value).
If no file exists with the provided name and location the method will throw an error.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> Core

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">Data</span> {
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">fromFile</span>(
        <span class="hljs-keyword">_</span> <span class="hljs-params">fileName</span>: <span class="hljs-type">String</span>,
        <span class="hljs-params">folder</span>: <span class="hljs-type">String</span> <span class="hljs-operator">=</span> <span class="hljs-string">"Resources/json"</span>
    ) <span class="hljs-keyword">throws</span> -> <span class="hljs-type">Data</span> {
        <span class="hljs-keyword">let</span> directory <span class="hljs-operator">=</span> <span class="hljs-type">DirectoryConfig</span>.detect()
        <span class="hljs-keyword">let</span> fileURL <span class="hljs-operator">=</span> <span class="hljs-type">URL</span>(fileURLWithPath: directory.workDir)
            .appendingPathComponent(folder, isDirectory: <span class="hljs-literal">true</span>)
            .appendingPathComponent(fileName, isDirectory: <span class="hljs-literal">false</span>)

        <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> <span class="hljs-type">Data</span>(contentsOf: fileURL)
    }
}
</code></pre>
<p>We use Vapor's <code>DirectoryConfig.detect()</code> to get the location of the working directory of the project.
From here we can use Swift's <code>URL</code> type initialized by providing the file URL, <code>URL((fileURLWithPath: String)</code>.
In the end, the URL will look like the following:</p>
<pre><code class="hljs language-swift"><span class="hljs-string">"/[working-directory]/[folder]/[fileName]"</span>
</code></pre>
<p>Now that we have the full address of the file, we can fetch it and return it as <code>Data</code>.</p>
<p>So let's test what happens when we provide a correct and a wrong file name, respectively:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">testCorrectFileName</span>() <span class="hljs-keyword">throws</span> {
    <span class="hljs-keyword">let</span> fileName <span class="hljs-operator">=</span> <span class="hljs-string">"femaleNobelLaureates.json"</span>
    <span class="hljs-type">XCTAssertNotNil</span>(<span class="hljs-keyword">try</span> <span class="hljs-type">Data</span>.fromFile(fileName)) <span class="hljs-comment">// Success</span>
}

<span class="hljs-keyword">func</span> <span class="hljs-title function_">testWrongFileName</span>() <span class="hljs-keyword">throws</span> {
    <span class="hljs-keyword">let</span> fileName <span class="hljs-operator">=</span> <span class="hljs-string">"femaleNobelLaureate.json"</span>
    <span class="hljs-type">XCTAssertThrowsError</span>(<span class="hljs-keyword">try</span> <span class="hljs-type">Data</span>.fromFile(fileName)) <span class="hljs-comment">// Success</span>
}
</code></pre>
<p>In the first test the correct file name is provided and the content of the file is encode into <code>Data</code>.
In the second test there is a spelling error and so the method throws the following error, as expected: <code>"The file “femaleNobelLaureate.json” couldn’t be opened because there is no such file."</code>.</p>
<h2>Decoding into a custom swift object</h2>
<p>For the rest of the blog we'll be working with a list of all female Nobel Laureates in the categories Physics, Chemistry and Physiology or Medicine<a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a>. You can find the json file <a href="https://github.com/heidipuk/nobel-laureates-example/blob/master/Resources/json/femaleNobelLaureates.json">here</a>.<br>
Each scientific accomplishment is summed down to a json dictionary that looks like the following:</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
  <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">19</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"fullName"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Donna Strickland"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"category"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"physics"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"year"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2018</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"rationale"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"for their method of generating high-intensity, ultra-short optical pulses"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"isShared"</span><span class="hljs-punctuation">:</span> <span class="hljs-keyword">true</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<p>We'll be using Swift's <code>Codable</code> protocol for easy decoding of the data into an array of our custom object. For that we'll set up a <code>class</code> called <code>NobelLaureates</code> that has the same six variables as in the json dictionary and add a memberwise initializer. And so, the model is ready.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">NobelLaureates</span>: <span class="hljs-title class_">Codable</span> {
    <span class="hljs-keyword">var</span> id: <span class="hljs-type">Int</span>
    <span class="hljs-keyword">var</span> fullName: <span class="hljs-type">String</span>
    <span class="hljs-keyword">var</span> category: <span class="hljs-type">String</span>
    <span class="hljs-keyword">var</span> year: <span class="hljs-type">Int</span>
    <span class="hljs-keyword">var</span> rationale: <span class="hljs-type">String</span>
    <span class="hljs-keyword">var</span> isShared: <span class="hljs-type">Bool</span>

    <span class="hljs-operator">...</span> <span class="hljs-comment">// memberwise initializer has been left out here</span>
}
</code></pre>
<p>Next we'll set up our custom decoder, which we need for decoding the data. We'll add it in an extension to <code>NobelLaureates</code> along with a <code>private struct</code> that functions as a container for the decoded data.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">extension</span> <span class="hljs-title class_">NobelLaureates</span> {
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">loadFromFile</span>(
      <span class="hljs-keyword">_</span> <span class="hljs-params">fileName</span>: <span class="hljs-type">String</span> <span class="hljs-operator">=</span> <span class="hljs-string">"femaleNobelLaureates.json"</span>
    ) <span class="hljs-keyword">throws</span> -> [<span class="hljs-type">NobelLaureates</span>] {
        <span class="hljs-keyword">let</span> decoder <span class="hljs-operator">=</span> <span class="hljs-type">JSONDecoder</span>()
        <span class="hljs-keyword">let</span> laureatesData <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">Data</span>.fromFile(fileName)
        <span class="hljs-keyword">let</span> decodedNobelLaureates <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> decoder.decode(
            <span class="hljs-type">LaureatesDecoderObject</span>.<span class="hljs-keyword">self</span>,
            from: laureatesData
        )
        <span class="hljs-keyword">return</span> decodedNobelLaureates.data
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">LaureatesDecoderObject</span>: <span class="hljs-title class_">Content</span> {
        <span class="hljs-keyword">var</span> data: [<span class="hljs-type">NobelLaureates</span>]
    }
}
</code></pre>
<p>Okay, we are now ready to test if the decoder is working. First we'll test that we have decoded the correct number of recipients of the Nobel price in Physics, Chemistry and Medicine or Physiology, which is 20.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">testDecodeLaureatesCount</span>() <span class="hljs-keyword">throws</span> {
    <span class="hljs-keyword">let</span> testData <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">NobelLaureates</span>.decodeFromData()
    <span class="hljs-type">XCTAssertEqual</span>(testData.data.count, <span class="hljs-number">20</span>) <span class="hljs-comment">// Success</span>
}
</code></pre>
<p>Next we'll test who was the seventh woman to receive a Nobel in the natural sciences<a href="#fn4" class="footnote-ref" id="fnref4" role="doc-noteref"><sup>4</sup></a>. Her name was Rosalyn Sussman Yalow. She received the price in 1977 for her work in developing radioimmunoassays of peptide hormones.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">testDecodeLaureatesAtIndex</span>() <span class="hljs-keyword">throws</span> {
    <span class="hljs-keyword">let</span> testData <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">NobelLaureates</span>.decodeFromData()
    <span class="hljs-type">XCTAssertEqual</span>(testData.data[<span class="hljs-number">6</span>].fullName, <span class="hljs-string">"Rosalyn Sussman Yalow"</span>) <span class="hljs-comment">// Success</span>
    <span class="hljs-type">XCTAssertEqual</span>(testData.data[<span class="hljs-number">6</span>].year, <span class="hljs-number">1977</span>) <span class="hljs-comment">// Success</span>
}
</code></pre>
<p>Success, our decoder works as expected!</p>
<h2>Final notes</h2>
<p>In this tutorial we have created a convenient method for fetching files in our Vapor project. Next we created a custom decoder to convert the data in our json file to Swift objects. Finally we covered each step with tests to verify that they worked.<br>
And a very final note; We used a json file in the tutorial, but the methods presented here can just as well be used to fetch and decode the content of a csv/xlsx/whatever file.</p>
<p><em>Article photo by <a href="https://unsplash.com/photos/dC6Pb2JdAqs">Fabian Grohs</a></em></p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn1" role="doc-endnote">Download the example project <a href="https://github.com/heidipuk/nobel-laureates-example">here.</a><a href="#fnref1" class="footnote-back" role="doc-backlink">↩</a></li>
<li id="fn2" role="doc-endnote">If you do not have Vapor installed already, you can follow <a href="https://docs.vapor.codes/3.0/install/macos/">this tutorial</a>.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩</a></li>
<li id="fn3" role="doc-endnote">The list of female laureates are extracted from <a href="https://www.nobelprize.org/nobel-prizes-and-laureates/">here</a>.<a href="#fnref3" class="footnote-back" role="doc-backlink">↩</a></li>
<li id="fn4" role="doc-endnote">Actually, Rosalyn Sussman Yalow was the sixth woman to receive a Nobel in the natural science topics, as Marie Skłodowska Curie receive the price twice. [Physics in 1903 and Chemistry in 1911]<a href="#fnref4" class="footnote-back" role="doc-backlink">↩</a></li>
</ol>
</section>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Android navigation with MVVM and State made easy]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/08/15/android-navigation-tools</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/08/15/android-navigation-tools</guid>
            <pubDate>Thu, 15 Aug 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Working on a large app with a large number of screens which we organized by feature (Messaging, Settings, Profile, etc.) made me test the single activity - multiple fragments approach by using the Navigation component. This technique has the advantage of not having to deal with activities and only define all the UI in the Fragments. In specific cases where we have to adapt the screen to more complex layouts, Tablet for example, we can define a parent Fragment and switch between the nested fragments based on device screen size and other criteria.</p>
<h2>About the structure</h2>
<p>We are using one <code>MainActivity</code> class which will hold our <code>NavHostFragment</code> and manage all the navigation.</p>
<p><em>activity_main.xml</em></p>
<pre><code class="hljs language-xml"><span class="hljs-meta">&#x3C;?xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"utf-8"</span>?></span>
<span class="hljs-tag">&#x3C;<span class="hljs-name">fragment</span> <span class="hljs-attr">xmlns:android</span>=<span class="hljs-string">"http://schemas.android.com/apk/res/android"</span>
    <span class="hljs-attr">xmlns:app</span>=<span class="hljs-string">"http://schemas.android.com/apk/res-auto"</span>
    <span class="hljs-attr">xmlns:tools</span>=<span class="hljs-string">"http://schemas.android.com/tools"</span>
    <span class="hljs-attr">android:id</span>=<span class="hljs-string">"@+id/nav_host_fragment"</span>
    <span class="hljs-attr">android:name</span>=<span class="hljs-string">"androidx.navigation.fragment.NavHostFragment"</span>
    <span class="hljs-attr">android:layout_width</span>=<span class="hljs-string">"match_parent"</span>
    <span class="hljs-attr">android:layout_height</span>=<span class="hljs-string">"match_parent"</span>
    <span class="hljs-attr">app:defaultNavHost</span>=<span class="hljs-string">"true"</span>
    <span class="hljs-attr">app:navGraph</span>=<span class="hljs-string">"@navigation/main_nav_graph"</span>
    <span class="hljs-attr">tools:context</span>=<span class="hljs-string">".ui.MainActivity"</span> /></span>
</code></pre>
<p>So let's take a look at our <code>main_nav_graph</code> which is the main graph containing all our <a href="https://developer.android.com/guide/navigation/navigation-nested-graphs">nested graphs</a>, they will appear as child <navigation> elements.
<code>main_nav_graph.xml</code></navigation></p>
<pre><code class="hljs language-xml"><span class="hljs-meta">&#x3C;?xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"utf-8"</span>?></span>
<span class="hljs-tag">&#x3C;<span class="hljs-name">navigation</span>
    <span class="hljs-attr">xmlns:android</span>=<span class="hljs-string">"http://schemas.android.com/apk/res/android"</span>
    <span class="hljs-attr">xmlns:app</span>=<span class="hljs-string">"http://schemas.android.com/apk/res-auto"</span>
    <span class="hljs-attr">xmlns:tools</span>=<span class="hljs-string">"http://schemas.android.com/tools"</span>
    <span class="hljs-attr">android:id</span>=<span class="hljs-string">"@+id/mainNavGraph"</span>
    <span class="hljs-attr">app:startDestination</span>=<span class="hljs-string">"@id/loginNavGraph"</span>></span>

    <span class="hljs-tag">&#x3C;<span class="hljs-name">include</span> <span class="hljs-attr">app:graph</span>=<span class="hljs-string">"@navigation/login_nav_graph"</span> /></span>

    <span class="hljs-tag">&#x3C;<span class="hljs-name">include</span> <span class="hljs-attr">app:graph</span>=<span class="hljs-string">"@navigation/settings_nav_graph"</span> /></span>

<span class="hljs-tag">&#x3C;/<span class="hljs-name">navigation</span>></span>
</code></pre>
<p><img src="/assets/img/articles/2019-08-15-android-navigation-tools/main_graph_preview.webp" alt="Main graph preview"></p>
<p>We have two nested graphs: <code>login_nav_graph</code> and <code>settings_nav_graph</code> which we use to separate two unrelated parts of our UI: Login and Settings. We can decide when to define a separate graph based on the amount of independent sections. For example the Settings section usually contains multiple screens like Notifications, Privacy, Account which can be naturally included into the same Settings graph.</p>
<p>Let's explore the Login graph.</p>
<p><em>login_nav_graph.xml</em></p>
<pre><code class="hljs language-xml"><span class="hljs-tag">&#x3C;<span class="hljs-name">navigation</span>
    <span class="hljs-attr">xmlns:android</span>=<span class="hljs-string">"http://schemas.android.com/apk/res/android"</span>
    <span class="hljs-attr">xmlns:app</span>=<span class="hljs-string">"http://schemas.android.com/apk/res-auto"</span>
    <span class="hljs-attr">xmlns:tools</span>=<span class="hljs-string">"http://schemas.android.com/tools"</span>
    <span class="hljs-attr">android:id</span>=<span class="hljs-string">"@+id/loginNavGraph"</span>
    <span class="hljs-attr">app:startDestination</span>=<span class="hljs-string">"@id/credentialsFragment"</span>></span>

    <span class="hljs-tag">&#x3C;<span class="hljs-name">fragment</span>
        <span class="hljs-attr">android:id</span>=<span class="hljs-string">"@+id/credentialsFragment"</span>
        <span class="hljs-attr">android:name</span>=<span class="hljs-string">"com.example.navigation_mvvm.ui.login.credentials.CredentialsFragment"</span>
        <span class="hljs-attr">android:label</span>=<span class="hljs-string">"CredentialsFragment"</span>
        <span class="hljs-attr">tools:layout</span>=<span class="hljs-string">"@layout/credentials_fragment"</span>></span>
        <span class="hljs-tag">&#x3C;<span class="hljs-name">action</span>
            <span class="hljs-attr">android:id</span>=<span class="hljs-string">"@+id/action_credentialsFragment_to_termsConditionsFragment"</span>
            <span class="hljs-attr">app:destination</span>=<span class="hljs-string">"@id/termsConditionsFragment"</span> /></span>
    <span class="hljs-tag">&#x3C;/<span class="hljs-name">fragment</span>></span>

    <span class="hljs-tag">&#x3C;<span class="hljs-name">fragment</span>
        <span class="hljs-attr">android:id</span>=<span class="hljs-string">"@+id/termsConditionsFragment"</span>
        <span class="hljs-attr">android:name</span>=<span class="hljs-string">"com.example.navigation_mvvm.ui.login.terms.TermsConditionsFragment"</span>
        <span class="hljs-attr">android:label</span>=<span class="hljs-string">"TermsConditionsFragment"</span>
        <span class="hljs-attr">tools:layout</span>=<span class="hljs-string">"@layout/terms_conditions_fragment"</span> /></span>

<span class="hljs-tag">&#x3C;/<span class="hljs-name">navigation</span>></span>
</code></pre>
<p>For simplicity it only contains two fragments <code>CredentialsFragment</code> and <code>TermsConditionsFragment</code> with the former also being the start destination of this graph. Essentially what that means is that in case you navigate to Login graph, Credentials fragment will be the first screen to be shown. Check <a href="https://developer.android.com/guide/navigation/navigation-getting-started">Navigation - Getting started</a> if you're unfamiliar with these concepts.</p>
<p>What we decided to do is to create some utility functions around state and navigation so it can be managed directly in the ViewModel instead of passing the control back to the Fragment. To achieve this we make use of two very simple base classes <code>BaseFragment</code> and <code>BaseViewModelImpl</code></p>
<p><em>BaseFragment.kt</em></p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BaseFragment</span>&#x3C;<span class="hljs-type">VS : BaseViewState, VM : BaseViewModel&#x3C;VS</span>>> : <span class="hljs-type">Fragment</span></span>(), LifecycleOwner {

    <span class="hljs-keyword">var</span> viewModelFactory: ViewModelProvider.Factory = SharedViewModelFactory()

    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">abstract</span> <span class="hljs-keyword">val</span> viewModel: VM

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onViewCreated</span><span class="hljs-params">(view: <span class="hljs-type">View</span>, savedInstanceState: <span class="hljs-type">Bundle</span>?)</span></span> {
        <span class="hljs-keyword">super</span>.onViewCreated(view, savedInstanceState)
        requireActivity()
            .onBackPressedDispatcher
            .addCallback(viewLifecycleOwner, <span class="hljs-keyword">object</span> : OnBackPressedCallback(<span class="hljs-literal">true</span>) {
                <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">handleOnBackPressed</span><span class="hljs-params">()</span></span> {
                    onBackPressed()
                }
            })
    }

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onActivityCreated</span><span class="hljs-params">(savedInstanceState: <span class="hljs-type">Bundle</span>?)</span></span> {
        <span class="hljs-keyword">super</span>.onActivityCreated(savedInstanceState)
        observeState(::onStateChange)
        observeNavigationEvent()
    }

    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">observeState</span><span class="hljs-params">(callback: (<span class="hljs-type">state</span>: <span class="hljs-type">VS</span>) -> <span class="hljs-type">Unit</span>)</span></span> {
        <span class="hljs-keyword">val</span> observer = Observer&#x3C;VS> { state -> callback.invoke(state) }
        viewModel.viewState.observe(viewLifecycleOwner, observer)
        observer.onChanged(viewModel.state)     <span class="hljs-comment">// Deliver initial state because initial state was initialized when there wasn't an observer observing state live data.</span>
    }

    <span class="hljs-comment">/** Implement this in subclasses to listen to state changes */</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">abstract</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onStateChange</span><span class="hljs-params">(state: <span class="hljs-type">VS</span>)</span></span>

    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">observeNavigationEvent</span><span class="hljs-params">()</span></span> {
        viewModel.navigationEvent.observe(viewLifecycleOwner, Observer { navEvent ->
            <span class="hljs-keyword">val</span> consume = navEvent?.consume()
            consume?.invoke(findNavController())
        })
    }

    <span class="hljs-keyword">protected</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onBackPressed</span><span class="hljs-params">()</span></span> {
        viewModel.onBackPressed()
        onReturnToPreviousScreen()
    }

    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">open</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onReturnToPreviousScreen</span><span class="hljs-params">()</span></span> {
        findNavController().popBackStack()
    }
}
</code></pre>
<p>In <code>BaseFragment</code> we just observe our <strong>state</strong> and <strong>navigation events</strong>. All we do is listen to back button events and forward them to <code>onBackPressed</code> so we can track them in the ViewModel. We can override <code>onReturnToPreviousScreen</code> in any fragment and change the back button press event behaviour if needed. I skipped the part related to the creation of ViewModel (full source code included).</p>
<p><em>BaseViewModelImpl.kt</em></p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BaseViewModelImpl</span>&#x3C;<span class="hljs-type">VS : BaseViewState</span>> : <span class="hljs-type">ViewModel</span></span>(), BaseViewModel&#x3C;VS> {

    <span class="hljs-keyword">override</span> <span class="hljs-keyword">val</span> viewState: MediatorLiveData&#x3C;VS> = MediatorLiveData()
    <span class="hljs-keyword">override</span> <span class="hljs-keyword">val</span> navigationEvent: MutableLiveData&#x3C;SingleEvent&#x3C;NavController.() -> Any>> = MutableLiveData()

    <span class="hljs-keyword">override</span> <span class="hljs-keyword">var</span> state: VS
        <span class="hljs-keyword">get</span>() = viewState.value ?: initialState
        <span class="hljs-keyword">set</span>(value) = viewState.setValue(value)  <span class="hljs-comment">// Sets the value synchronously</span>

    <span class="hljs-keyword">override</span> <span class="hljs-keyword">var</span> stateAsync: VS
        <span class="hljs-keyword">get</span>() = state
        <span class="hljs-keyword">set</span>(value) = viewState.postValue(value)  <span class="hljs-comment">// Sets the value asynchronously</span>

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">navigateTo</span><span class="hljs-params">(route: <span class="hljs-type">RouteSection</span>, args: <span class="hljs-type">Bundle</span>?)</span></span> {
        withNavController { navigate(route.graph, args, defaultNavOptions) }
    }

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">navigateTo</span><span class="hljs-params">(route: <span class="hljs-type">RouteDestination</span>, args: <span class="hljs-type">Bundle</span>?, clearStack: <span class="hljs-type">Boolean</span>)</span></span> {
        <span class="hljs-keyword">when</span> {
            route <span class="hljs-keyword">is</span> RouteDestination.Back -> withNavController { popBackStack() }
            clearStack -> withNavController { popBackStack(route.destination, <span class="hljs-literal">false</span>) }
            <span class="hljs-keyword">else</span> -> withNavController { navigate(route.destination, args, defaultNavOptions) }
        }
    }

    <span class="hljs-keyword">protected</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">withNavController</span><span class="hljs-params">(block: <span class="hljs-type">NavController</span>.() -> <span class="hljs-type">Any</span>)</span></span> {
        navigationEvent.postValue(SingleEvent(block))
    }
}
</code></pre>
<p>The important part to note here is the <strong>state</strong> property which gives access to the ViewModel state and <strong>navigationEvent</strong> which is used to provide one time navigation events. Thanks to <a href="https://medium.com/androiddevelopers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150">SingleEvent</a> it means that as soon as we consume the navigation event it will be null and we will avoid issues as receiving the same event when the Fragment is resumed for example.</p>
<p>To navigate to a different screen within the same graph you can call <code>navigateTo</code> and pass the RouteDestination which is just a more convenient way to specify the destination instead of typing <em>R.id.fragmentName</em>. It helps with finding usages and debugging but if you prefer navigating by specifying the resource id then you can use <code>withNavController</code> directly from the ViewModel like this:</p>
<pre><code class="hljs language-kotlin">withNavController { navigate(R.id.notificationsFragment) }
</code></pre>
<p>Now we will see a real example of using the navigation and state utilities in the Credentials fragment.</p>
<pre><code class="hljs language-kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CredentialsFragment</span> : <span class="hljs-type">BaseFragment</span>&#x3C;<span class="hljs-type">CredentialsState, CredentialsViewModel</span>></span>() {

    <span class="hljs-keyword">override</span> <span class="hljs-keyword">val</span> viewModel: CredentialsViewModel <span class="hljs-keyword">by</span> lazyViewModel()

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onCreateView</span><span class="hljs-params">(inflater: <span class="hljs-type">LayoutInflater</span>, container: <span class="hljs-type">ViewGroup</span>?, savedInstanceState: <span class="hljs-type">Bundle</span>?)</span></span>: View? {
        <span class="hljs-keyword">return</span> inflater.inflate(R.layout.credentials_fragment, container, <span class="hljs-literal">false</span>)
    }

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onViewCreated</span><span class="hljs-params">(view: <span class="hljs-type">View</span>, savedInstanceState: <span class="hljs-type">Bundle</span>?)</span></span> {
        <span class="hljs-keyword">super</span>.onViewCreated(view, savedInstanceState)

        submitCredentialsBtn.setOnClickListener { viewModel.onCredentialsSubmitted() }
        continueBtn.setOnClickListener { viewModel.onContinueClick() }
    }

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onStateChange</span><span class="hljs-params">(state: <span class="hljs-type">CredentialsState</span>)</span></span> {
        continueBtn.isEnabled = state.credentialsValidated
    }
}
</code></pre>
<p>The only thing different from an usual definition of a Fragment is the <code>onStateChange()</code> function which is called as soon as the ViewModel state changes. So if the user submits the credentials we trigger <code>viewModel.onCredentialsSubmitted()</code> which will change the state and set <code>credentialsValidated = true</code>. and enable the <code>continueBtn</code> in this case.</p>
<pre><code class="hljs language-kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CredentialsViewModel</span> : <span class="hljs-type">BaseViewModelImpl</span>&#x3C;<span class="hljs-type">CredentialsState</span>></span>() {

    <span class="hljs-keyword">override</span> <span class="hljs-keyword">val</span> initialState = CredentialsState()

    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onCredentialsSubmitted</span><span class="hljs-params">()</span></span> {
        <span class="hljs-comment">// Validate credentials</span>
        state = state.copy(credentialsValidated = <span class="hljs-literal">true</span>)
    }

    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onContinueClick</span><span class="hljs-params">()</span></span> {
        navigateTo(RouteDestination.Login.TermsConditions)
    }
}
</code></pre>
<p>We define the state as a simple Data class so we can create a new state by copying the existing one and only changing some properties.</p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">data</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CredentialsState</span></span>(<span class="hljs-keyword">val</span> credentialsValidated: <span class="hljs-built_in">Boolean</span> = <span class="hljs-literal">false</span>) : BaseViewState
</code></pre>
<p>As I mentioned earlier the <code>RouteDestination</code> is mainly used for convenience and you can use any structure you want for it. In our case it looks like this:</p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">sealed</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RouteDestination</span></span>(<span class="hljs-meta">@IdRes</span> <span class="hljs-keyword">open</span> <span class="hljs-keyword">val</span> destination: <span class="hljs-built_in">Int</span>) {

    <span class="hljs-keyword">object</span> Back : RouteDestination(-<span class="hljs-number">1</span>)

    <span class="hljs-keyword">sealed</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Login</span></span>(<span class="hljs-meta">@IdRes</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">val</span> destination: <span class="hljs-built_in">Int</span>) : RouteDestination(destination) {

        <span class="hljs-keyword">object</span> Credentials : Login(R.id.credentialsFragment)
        <span class="hljs-keyword">object</span> TermsConditions : Login(R.id.termsConditionsFragment)
    }

    <span class="hljs-keyword">sealed</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Settings</span></span>(<span class="hljs-meta">@IdRes</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">val</span> destination: <span class="hljs-built_in">Int</span>) : RouteDestination(destination) {

        <span class="hljs-keyword">object</span> Profile : Settings(R.id.profileFragment)
        <span class="hljs-keyword">object</span> Notifications : Settings(R.id.notificationsFragment)
    }
}
</code></pre>
<h2>Final words</h2>
<p>Using this new approach is easy and adds some advantages like handling the navigation directly from ViewModel. By using the <code>RouteDestination</code> we can redirect the user to different parts of the app without accessing specific UI related resources, keeping the ViewModel free from references to navigation resources and having a single immutable state which is much easier to debug in case of something not working as expected.</p>
<p>Full source code available at <a href="https://github.com/vladostaci/NavigationMVVM">https://github.com/vladostaci/NavigationMVVM</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Deploying Node.js apps in Amazon Linux with pm2]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/07/31/Deploying-Node-js-apps-in-Amazon-Linux-with-pm2</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/07/31/Deploying-Node-js-apps-in-Amazon-Linux-with-pm2</guid>
            <pubDate>Wed, 31 Jul 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Running a Node.js application can be as trivial as <strong>node index.js</strong>, but running it in production and keeping it running are completely different. Whenever the application crashes or the server reboots unexpectedly, we want the application to come back alive.</p>
<p>There are several ways we can properly run a Node.js application in production. In this article, I will be talking about how to deploy one using pm2 in an AWS EC2 instance running Amazon Linux.</p>
<h3>AWS EC2</h3>
<p>Spin up an EC2 instance of your liking. Consider the load your server will be going through and the cost. Here you can get a pricing list for different types of instances:</p>
<p><a href="https://aws.amazon.com/ec2/pricing/on-demand/" title="https://aws.amazon.com/ec2/pricing/on-demand/"><strong>EC2 Instance Pricing — Amazon Web Services (AWS)</strong><br>
With On-Demand instances you only pay for EC2 instances you use. The use of On-Demand instances frees you from the…aws.amazon.com</a><a href="https://aws.amazon.com/ec2/pricing/on-demand/"></a></p>
<p>Choose Amazon Linux AMI. This is a free offering from Amazon.</p>
<blockquote>
<p>The Amazon Linux AMI is a supported and maintained Linux image provided by Amazon Web Services for use on Amazon Elastic Compute Cloud (Amazon EC2). It is designed to provide a stable, secure, and high performance execution environment for applications running on Amazon EC2. It supports the latest EC2 instance type features and includes packages that enable easy integration with AWS. Amazon Web Services provides ongoing security and maintenance updates to all instances running the Amazon Linux AMI. The Amazon Linux AMI is provided at no additional charge to Amazon EC2 users.</p>
</blockquote>
<p>Learn more at:</p>
<p><a href="https://aws.amazon.com/amazon-linux-ami/" title="https://aws.amazon.com/amazon-linux-ami/"><strong>AWS | Amazon Linux AMI</strong><br>
The Amazon Linux AMI includes packages and configurations that provide tight integration with Amazon Web Services. The…aws.amazon.com</a><a href="https://aws.amazon.com/amazon-linux-ami/"></a></p>
<h3>Server configuration</h3>
<p>After the instance is up and running, SSH into it, preferably using a non-root account.</p>
<p>Update packages:</p>
<pre><code class="hljs language-bash">sudo yum update -y
</code></pre>
<p>Install necessary dev tools:</p>
<pre><code class="hljs language-bash">sudo yum install -y gcc gcc-c++ make openssl-devel git
</code></pre>
<p>Install Node.js:</p>
<pre><code class="hljs language-bash">curl --silent --location [https://rpm.nodesource.com/setup_10.x](https://rpm.nodesource.com/setup_10.x) | sudo bash -

sudo yum install -y nodejs
</code></pre>
<p>This will install version 10 of Node.js. If you want to install a different version you can change the location.</p>
<p>We will run our application using pm2. Pm2 is a process manager for Node.js. It has a lot of useful features such as monitoring, clustering, reloading, log management, etc. I will discuss some of the features we will use and configure in our application.</p>
<p>The features I find most noteworthy:</p>
<ol>
<li>Clustering — runs multiple instances of an application (depending on configuration, in our case we will use number of cores to determine this)</li>
<li>Reloading — reloads applications when they crash or the server reboots.</li>
</ol>
<p>Install pm2:</p>
<pre><code class="hljs language-bash">sudo npm install pm2@latest -g
</code></pre>
<p>Generate a pm2 startup script:</p>
<pre><code class="hljs language-bash">pm2 startup
</code></pre>
<p>This will daemonize pm2 and initialize it on system reboots.</p>
<p>Learn more here:</p>
<p><a href="https://pm2.keymetrics.io/docs/usage/startup/">https://pm2.keymetrics.io/docs/usage/startup</a></p>
<h3>The source code</h3>
<p>You can use https to clone the source code. However, I find that using a deploy key is much better and I can give read-only access to the server.</p>
<p>Here is a simplified way of how to generate and use deploy keys:</p>
<p>Generate a new ssh key using:</p>
<pre><code class="hljs language-bash">ssh-keygen
</code></pre>
<p>Do not enter a passphrase.</p>
<p>Copy the public key contents printed by the command:</p>
<pre><code class="hljs language-bash">cat ~/.ssh/id_rsa.pub
</code></pre>
<p>If you are using Github, add it to the Deploy Keys section of your repository’s Settings page.</p>
<p><a href="https://developer.github.com/v3/guides/managing-deploy-keys/#deploy-keys" title="https://developer.github.com/v3/guides/managing-deploy-keys/#deploy-keys"><strong>Managing deploy keys</strong><br>
There are four ways to manage SSH keys on your servers when automating deployment scripts: SSH agent forwarding HTTPS…developer.github.com</a><a href="https://developer.github.com/v3/guides/managing-deploy-keys/#deploy-keys"></a></p>
<p>After the repository is cloned. Run the scripts you need to run in order to get your project ready.</p>
<p>For example, if my project uses yarn as the package manager and typescript as the language which needs to be transpiled to javascript when deploying, I will run the following commands:</p>
<pre><code class="hljs language-bash">yarn install

yarn build
</code></pre>
<p>The second command runs the <em>build</em> script from my <em>package.json</em> file which states: <em>“build”: “tsc”</em></p>
<p>We can now run the application by running:</p>
<pre><code class="hljs language-bash">node dist/index.js
</code></pre>
<p>But we are not going to. Because we want to use pm2 to run our application.</p>
<h3>The Ecosystem File</h3>
<p>Pm2 provides a way to configure our application in an ecosystem file where we can easily tune the various configurable options provided.</p>
<p>You can generate an ecosystem file by running:</p>
<pre><code class="hljs language-bash">pm2 ecosystem
</code></pre>
<p>Our application’s ecosystem file contains:</p>
<p><em>ecosystem.config.js:</em></p>
<pre><code class="hljs language-js"><span class="hljs-variable language_">module</span>.<span class="hljs-property">exports</span> = {
  apps : [{
    <span class="hljs-attr">name</span>: ‘<span class="hljs-title class_">My</span> <span class="hljs-title class_">App</span>’,
    <span class="hljs-attr">script</span>: ‘dist/index.<span class="hljs-property">js</span>’,
    <span class="hljs-attr">instances</span>: ‘max’,
    <span class="hljs-attr">max_memory_restart</span>: ‘256M’,
    <span class="hljs-attr">env</span>: {
      <span class="hljs-attr">NODE_ENV</span>: ‘development’
    },
    <span class="hljs-attr">env_production</span>: {
      <span class="hljs-attr">NODE_ENV</span>: ‘production’
    }
  }]
};
</code></pre>
<p>What this configuration tells pm2 is, run the application and name it <em>My App</em>. Run it using the script <em>dist/index.js</em>. Spawn as many instances of the application according to the number of CPUs present.</p>
<p>Mind the NODE_ENV environment variable. This has several benefits when running an express application. It boosts the performance of the app by tweaking a few things such as (Taken from express documentation):</p>
<ol>
<li>Cache view templates.</li>
<li>Cache CSS files generated from CSS extensions.</li>
<li>Generate less verbose error messages.</li>
</ol>
<p>Read more here:</p>
<p><a href="https://expressjs.com/en/advanced/best-practice-performance.html#set-node_env-to-production" title="https://expressjs.com/en/advanced/best-practice-performance.html#set-node_env-to-production"><strong>Performance Best Practices Using Express in Production</strong><br>
This article discusses performance and reliability best practices for Express applications deployed to production. This…expressjs.com</a><a href="https://expressjs.com/en/advanced/best-practice-performance.html#set-node_env-to-production"></a></p>
<p>There are a lot more options in pm2 that you can tweak, I am leaving those at default values. Check them out here:</p>
<p><a href="https://pm2.keymetrics.io/docs/usage/application-declaration/" title="https://pm2.keymetrics.io/docs/usage/application-declaration/"><strong>Ecosystem File · PM2</strong><br>
Advanced process manager for production Node.js applications. Load balancer, logs facility, startup script, micro…pm2.keymetrics.io</a><a href="https://pm2.keymetrics.io/docs/usage/application-declaration/"></a></p>
<p>Run the application:</p>
<pre><code class="hljs language-bash">pm2 reload ecosystem.config.js --env production
</code></pre>
<p>This command reloads the application with production environment declared in the ecosystem file. This process is also done with zero downtime. It compares the ecosystem configuration and currently running processes and updates as necessary.</p>
<p>We want to be able to write up a script for everytime we need to deploy. This way, the app is not shut down and started again (which a restart does).</p>
<p>Read more about it:</p>
<p><a href="http://pm2.keymetrics.io/docs/usage/cluster-mode/#reload" title="http://pm2.keymetrics.io/docs/usage/cluster-mode/#reload"><strong>Cluster Mode · PM2</strong><br>
The cluster mode allows networked Node.js applications (http(s)/tcp/udp server) to be scaled accross all CPUs…pm2.keymetrics.io</a><a href="http://pm2.keymetrics.io/docs/usage/cluster-mode/#reload"></a></p>
<p>When our application is up and running, we have to save the process list we want to respawn for when the system reboots unexpectedly:</p>
<pre><code class="hljs language-bash">pm2 save
</code></pre>
<p>We can check our running applications with:</p>
<pre><code class="hljs language-bash">pm2 status
</code></pre>
<p>Monitor our apps:</p>
<pre><code class="hljs language-bash">pm2 monit
</code></pre>
<p>View logs:</p>
<pre><code class="hljs language-bash">pm2 logs
</code></pre>
<p>Let’s create a handy script to deploy when there is a change:</p>
<p><em>deploy.sh:</em></p>
<pre><code class="hljs language-bash"><span class="hljs-meta">#!/bin/bash</span>

git pull
yarn install
npm run build
pm2 reload ecosystem.config.js --env production

<span class="hljs-comment"># EOF</span>
</code></pre>
<p>Make the file executable:</p>
<pre><code class="hljs language-bash">chmod +x deploy.sh
</code></pre>
<p>Now, every time you need to deploy changes, simply run:</p>
<pre><code class="hljs language-bash">./deploy.sh
</code></pre>
<h3>Conclusion</h3>
<p>Let’s recap:</p>
<ul>
<li>Create an EC2 instance running Amazon Linux</li>
<li>Update packages (might include security updates).</li>
<li>Install the desired Node.js version.</li>
<li>Use a process manager to run the application (such as pm2).</li>
<li>Use deploy keys to pull code from the source repository.</li>
<li>Create an ecosystem configuration file so that it is maintainable in the future.</li>
<li>Create a deploy script so that it is easy to run future deployments.</li>
<li>Run the deployment script whenever there is a change to be deployed.</li>
<li>Congratulations! Your application is up and running.</li>
</ul>
<p>There are several other ways to achieve the same end goal, such as using <a href="https://github.com/foreversd/forever">forever</a> instead of pm2, or even using <a href="https://www.docker.com">Docker</a> instead and deploy to <a href="https://aws.amazon.com/ecs/">Amazon ECS</a>. This is a documentation of how I deploy Node.js applications in production if running them on EC2 instances.</p>
<p>When your deployments become more frequent, you should consider a CI/CD integration to build and deploy whenever there is a change in the source code.</p>
<p>Make sure you monitor and keep an eye on your server’s resource usage.</p>
<p>Last but not least, make sure you have proper logging in your application. I cannot stress enough how important proper logging is.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/20F9ht9RY9s">Jonathan Gallegos</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Join the dark side and implement Dark Mode in iOS]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/07/03/Dark-Mode</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/07/03/Dark-Mode</guid>
            <pubDate>Wed, 03 Jul 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>WWDC this year brought to light a lot of amazing features Apple has been working on lately. One of these features, and maybe one of the most expected, was Dark Mode support.</p>
<p>As Apple mentioned in their keynote, Dark Mode is easy to implement and will bring a whole new look to your application.</p>
<p>Though easy to implement, support for Dark Mode doesn't come for free. In this post we will look at what is needed from us, the developers, to join the dark side.</p>
<p>Before we dive into specifics about Dark Mode implementation, let us look at Apples <a href="https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/dark-mode/">Human Interface Guidelines</a> and see what they say about Dark Mode.</p>
<p>The main rules to follow presented in Apple's guidelines are the following:</p>
<ul>
<li>Focus on your content.</li>
<li>Test your design in both light and dark appearances.</li>
<li>Ensure that your content remains comfortably legible in Dark Mode when you adjust the contrast and transparency accessibility settings.</li>
</ul>
<h3>Semantic Colors</h3>
<p>To support Dark Mode the main thing we have to consider in our iOS applications is the way we handle and implement colors. Traditionally each piece of our UI would have a color - created by providing a single RGB value - assigned, but in order to implement the dark appearance for our UI, we will need to change this approach and instead start using what is referred to as "Semantic Colors".</p>
<p>Making a color semantic simply means that instead of referring to the color directly (red, blue, yellow), we refer to the color by <em>the role</em> is has (background, text, important). This means that we shouldn't directly assign RGB colors to our views, but instead create the colors in a separate part of our application before we assign them. For example you will declare and assign a primary background color in a similar fashion.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// create an UIColor extension to declare our colors</span>
<span class="hljs-keyword">extension</span> <span class="hljs-title class_">UIColor</span> {
	<span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> primaryBackgroundColor <span class="hljs-operator">=</span> <span class="hljs-type">UIColor</span>.black
}
</code></pre>
<pre><code class="hljs language-swift"><span class="hljs-comment">// assign the primaryBackgroundColor to our backgroundView in our UIViewController or UIView</span>
backgroundView.backgroundColor <span class="hljs-operator">=</span> .primaryBackgroundColor

</code></pre>
<p>Semantic colors have more benefits than just support for Dark Mode, they will also empower you to easily manage your colors.</p>
<p>In Xcode 11 Apple has provided us with several semantic colors. For example <code>UIColor.systemBackground</code> will be white for a light appearance and dark for a dark appearance.</p>
<p>We can also create our own custom colors. To do so we need to navigate to our Assets catalog (Assets.xcassets) and add a <b>New Color Set</b>. Now that we have created a new color, we need to select the <b>Attribute inspector</b> in the top right corner and select values <b>Any, Dark</b> for our color <b>Appearances</b>.</p>
<p><img src="/assets/img/articles/2019-07-03-Dark-Mode/1.webp" alt=""></p>
<p>When a color has multiple appearance values, we call the color "Dynamic Color".</p>
<h3>Design Levels</h3>
<p>For dark appearance support Apple has introduced a two level design. This contains:</p>
<ul>
<li>base level: which is used when the view fills the whole screen</li>
<li>elevated level: which is used when the view is in a separate layer above the base</li>
</ul>
<p>To identify the level of the view, you can access the <code>UITraitCollection.userInterfaceLevel</code> value associated with the view.</p>
<p>This will allow you to create a more stylished UI by providing different values for the same color, depending on the <code>userInterfaceLevel</code>.</p>
<h3>Dynamic Colors</h3>
<p>To obtain the final color, dynamic colors get resolved using a <code>UITraitCollection</code>. For system defined colors this happens automatically, but, we can also declare our own dynamic colors programatically! "How?" you might wonder. Well lets see together.</p>
<p>I suggest that you create your own colors in an extension to <code>UIColor</code>. This will allow you to easily access your colors whenever a <code>UIColor</code> is needed, by reducing the need to call <code>UIColor.myColor</code> and instead just provide the color <code>.myColor</code>.</p>
<p>Now lets look at what it takes to create our own custom dynamic color.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> UIKit

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">UIColor</span> {

    <span class="hljs-comment">// create a dynamic color to use as a background color in our application</span>
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> dynamicBackgroundColor <span class="hljs-operator">=</span> <span class="hljs-type">UIColor</span> { (traitCollection: <span class="hljs-type">UITraitCollection</span>) -> <span class="hljs-type">UIColor</span> <span class="hljs-keyword">in</span>
    	 <span class="hljs-comment">// resolve the color by using traitCollection</span>
        <span class="hljs-keyword">switch</span> traitCollection.userInterfaceStyle {
        <span class="hljs-keyword">case</span> .dark:
            <span class="hljs-keyword">return</span> .black
        <span class="hljs-keyword">case</span> .light, .unspecified:
            <span class="hljs-keyword">return</span> .white
        <span class="hljs-keyword">@unknown</span> <span class="hljs-keyword">default</span>: <span class="hljs-comment">// may have additional userInterfaceStyles in the future</span>
            <span class="hljs-keyword">return</span> .white
        }
    }

}
</code></pre>
<p>This will allow you to assign your background color to your view with the following code <code>view.backgroundColor = .dynamicBackgroundColor</code>. As you can see nothing changed in the way we assign color, but how does it know to return the proper color if no <code>UITraitCollection</code> is specified? This is handled in the background by UIKit and it uses the newly introduced <code>UITraitCollection.current</code>.</p>
<h3>Resolving Dynamic Colors</h3>
<p>UIKit sets the current <code>UITraitCollection</code> for the view during the <code>draw()</code> method. As well, when the appearance changes, UIKit will automatically call <code>setNeedsDisplay</code> on your view, which will cause the view to be redrawn with the proper <code>UITraitCollection</code> value.</p>
<p>It is important to note that the value associated with <code>UITraitCollection.current</code> is <b>not</b> guaranteed to be correct if called outside of the following functions, in which UIKit sets the value for the <code>UITraitCollection</code>.</p>
<ul>
<li>
<p>UIView</p>
<ul>
<li><code>draw()</code></li>
<li><code>layoutSubviews()</code></li>
<li><code>traitCollectionDidChange()</code></li>
<li><code>tintColorDidChange()</code></li>
</ul>
</li>
<li>
<p>UIViewController</p>
<ul>
<li><code>viewWillLayoutSubviews()</code></li>
<li><code>viewDidLayoutSubviews()</code></li>
<li><code>traitCollectionDidChange()</code></li>
</ul>
</li>
<li>
<p>UIPresentationController</p>
<ul>
<li><code>containerViewWillLayoutSubviews()</code></li>
<li><code>containerViewDidLayoutSubviews()</code></li>
<li><code>traitCollectionDidChange()</code></li>
</ul>
</li>
</ul>
<p>To use dynamic colors outside of these methods you might need to manage the <code>UITratCollection</code>. This is needed when working with lower level classes such as <code>CALayer</code>, <code>CGColor</code> etc.</p>
<p>Lets see below how we can use dynamic colors when assigning a <code>borderColor</code> property to our view's <code>CALayer</code>.</p>
<pre><code class="hljs language-swift">  <span class="hljs-keyword">let</span> layer <span class="hljs-operator">=</span> <span class="hljs-type">CALayer</span>()
  <span class="hljs-comment">// get the current traitCollection used for our view</span>
  <span class="hljs-keyword">let</span> traitCollection <span class="hljs-operator">=</span> view.traitCollection
  traitCollection.performAsCurrent {
  	   <span class="hljs-comment">// assign a dynamic borderColor called borderColor</span>
      layer.borderColor <span class="hljs-operator">=</span> <span class="hljs-type">UIColor</span>.borderColor.cgColor
  }

</code></pre>
<p>When <code>performAsCurrent</code> is called on a <code>traitCollection</code> it makes the <code>traitCollection</code> become the current <code>traitCollection</code> whereafter it runs the code inside the closure. This means that our <code>borderColor</code> is resolved accordingly.</p>
<p>Now that we have resolved our dynamic colors when creating our views, we need to be aware that there can be times when we will need to resolve the dynamic colors again. This usually happens when the current <code>traitCollection</code> changes. To listen to <code>traitCollection</code> changes we can override <code>override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?)</code> in both our <code>UIView</code>'s and <code>UIViewController</code>'s. Though when overriding this function we should be aware that not all <code>traitCollection</code> changes will mean that we need to resolve our colors again. This will only be needed when the user interface appearance changes. We can check if the change is a change in apparance by using calling <code>traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection)</code>.</p>
<pre><code class="hljs language-swift">  <span class="hljs-keyword">override</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">traitCollectionDidChange</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">previousTraitCollection</span>: <span class="hljs-type">UITraitCollection</span>?) {
        <span class="hljs-keyword">super</span>.traitCollectionDidChange(previousTraitCollection)

        <span class="hljs-keyword">guard</span> traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> }

        <span class="hljs-comment">// Resolve Dynamic Colors here</span>
    }
</code></pre>
<h3>Resolving Dynamic Images</h3>
<p>So far we have discovered the concept of "Dynamic Colors". Similarly an <code>UIImage</code> can become a "Dynamic Image".</p>
<p>Dynamic Images are automatically resolved by <code>UIImageView</code> but if we need to resolve our <code>UIImage</code> independently we can do so by accessing the <code>imageAsset</code> property on our <code>UIImage</code>.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> image <span class="hljs-operator">=</span> <span class="hljs-type">UIImage</span>(named: <span class="hljs-string">"nodesLogoImage"</span>)
<span class="hljs-keyword">let</span> asset <span class="hljs-operator">=</span> image<span class="hljs-operator">?</span>.imageAsset
<span class="hljs-comment">// get the appropriate image by resolving the dynamic image</span>
<span class="hljs-keyword">let</span> resolvedImage <span class="hljs-operator">=</span> asset<span class="hljs-operator">?</span>.image(with: traitCollection)
</code></pre>
<h2>Final notes</h2>
<p>Trait Collections play an important role when implementing Dark Mode. One important thing worth remembering is that there isn't a single app wide <code>traitCollection</code> value but instead they cascade through the app's hierarchy.</p>
<p>Supporting Dark Mode for your application will definitely make your users happy, and as we saw above, it is not a really complex task to do so. Just remember to pay attention when creating and assigning your dynamic colors.</p>
<p>Hope to see you next time!</p>
<p>References:</p>
<ul>
<li><a href="https://developer.apple.com/videos/play/wwdc2019/214/">WWDC 2019 - Implementing Dark Mode</a></li>
<li><a href="https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/dark-mode/">Human Interface Guidelines</a></li>
</ul>
<p>Cover Photo by <a href="https://unsplash.com/@bubo?utm_source=unsplash&#x26;utm_medium=referral&#x26;utm_content=creditCopyText">Lubo Minar</a> on Unsplash.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Higher order functions in Swift]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/07/03/Higher-Order-Functions-In-Swift</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/07/03/Higher-Order-Functions-In-Swift</guid>
            <pubDate>Wed, 03 Jul 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><strong>Higher order functions</strong> in Swift are extremely powerful tools to have in your developer toolkit, the only issue is that it might take some time to get comfortable with them. Before we get started, let’s have a look at some important terms to know in relation with higher order functions:</p>
<p><em><strong><a href="https://developer.apple.com/documentation/swift/collection">Collections</a></strong> - a sequence whose elements can be traversed multiple times, nondestructively, and accessed by an indexed subscript.</em></p>
<p><em><strong><a href="https://docs.swift.org/swift-book/LanguageGuide/Closures.html">Closures</a></strong> - self-contained blocks of functionality that can be passed around and used in your code.</em></p>
<p><em><strong>Shorthand arguments</strong> - Swift automatically provides shorthand argument names to inline closures, which can be used to refer to the values of the closure’s arguments by the names $0, $1, $2, and so on.</em></p>
<p><em><strong><a href="https://developer.apple.com/documentation/swift/optional">Optional</a></strong> - A type that represents either a wrapped value or nil, the absence of a value.</em></p>
<p>So let’s look now into some examples including some that we have seen in real life project at Nodes using <strong>map, filter, reduce, sorted, flatMap and compactMap</strong>. You can also download the playground with all examples <a href="https://github.com/nodes-ios/higher-order-functions-swift">here</a>.</p>
<h2><a href="https://developer.apple.com/documentation/swift/array/3017522-map">Map</a></h2>
<p>Map is used when you want to apply the same operation to each element of a collection.
It takes a single argument in the form of a mapping closure and returns an array with the transformed elements of the input sequence.</p>
<p><strong>Time complexity: O(n)</strong> - Linear - where n is the length of the sequence.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Example 1: Convert Meters to Feet</span>
<span class="hljs-keyword">let</span> meters <span class="hljs-operator">=</span> [<span class="hljs-number">10.0</span>, <span class="hljs-number">22.0</span>, <span class="hljs-number">55.0</span>, <span class="hljs-number">74.0</span>]

<span class="hljs-keyword">let</span> feet <span class="hljs-operator">=</span> meters.map { <span class="hljs-variable">$0</span> <span class="hljs-operator">*</span> <span class="hljs-number">3.281</span>}
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Meters converted to feet: <span class="hljs-subst">\(feet)</span>"</span>)

<span class="hljs-comment">// -> Meters converted to feet: [32.81, 72.182, 180.455, 242.794]</span>
</code></pre>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Example 2: Make the planet names capitalized</span>
<span class="hljs-keyword">let</span> planetNames <span class="hljs-operator">=</span> [<span class="hljs-string">"mars"</span>, <span class="hljs-string">"jupiter"</span>, <span class="hljs-string">"mercury"</span>, <span class="hljs-string">"saturn"</span>, <span class="hljs-string">"earth"</span>, <span class="hljs-string">"neptune"</span>, <span class="hljs-string">"uranus"</span>, <span class="hljs-string">"venus"</span>]

<span class="hljs-keyword">let</span> capitalizedPlanetNames <span class="hljs-operator">=</span> planetNames.map { <span class="hljs-variable">$0</span>.capitalized }
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Planet names capitalized: <span class="hljs-subst">\(capitalizedPlanetNames)</span>"</span>)

<span class="hljs-comment">// -> Planet names capitalized: ["Mars", "Jupiter", "Mercury", "Saturn", "Earth", "Neptune", "Uranus", "Venus"]</span>
</code></pre>
<pre><code class="hljs language-swift"><span class="hljs-comment">//Example 3: Map the array of addresses to an array of zipcodes</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">Address</span> {
    <span class="hljs-keyword">var</span> street: <span class="hljs-type">String</span>
    <span class="hljs-keyword">var</span> zipcode: <span class="hljs-type">Int</span>

    <span class="hljs-keyword">init</span>(<span class="hljs-params">street</span>: <span class="hljs-type">String</span>, <span class="hljs-params">zipcode</span>: <span class="hljs-type">Int</span>) {
        <span class="hljs-keyword">self</span>.street <span class="hljs-operator">=</span> street
        <span class="hljs-keyword">self</span>.zipcode <span class="hljs-operator">=</span> zipcode
    }
}

<span class="hljs-keyword">var</span> addresses <span class="hljs-operator">=</span> [<span class="hljs-type">Address</span>]()
addresses.append(<span class="hljs-type">Address</span>(street: <span class="hljs-string">"Nice Boulevard"</span>, zipcode: <span class="hljs-number">1200</span>))
addresses.append(<span class="hljs-type">Address</span>(street: <span class="hljs-string">"Green Street"</span>, zipcode: <span class="hljs-number">4560</span>))

<span class="hljs-keyword">let</span> zipcodes <span class="hljs-operator">=</span> addresses.map { <span class="hljs-variable">$0</span>.zipcode }
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Zip codes: <span class="hljs-subst">\(zipcodes)</span>"</span>)

<span class="hljs-comment">//-> Zip codes: [1200, 4560]</span>
</code></pre>
<h2><a href="https://developer.apple.com/documentation/swift/sequence/3018365-filter">Filter</a></h2>
<p>Filter is used when you want to have a result with only elements that match a condition.</p>
<p><strong>Time complexity: O(n)</strong> - Linear - where n is the length of the sequence.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Example 1: Filter only the planets that start with the letter "M"</span>
<span class="hljs-keyword">let</span> filteredPlanetNames <span class="hljs-operator">=</span> planetNames.filter {<span class="hljs-variable">$0</span>.prefix(<span class="hljs-number">1</span>).uppercased() <span class="hljs-operator">==</span> <span class="hljs-string">"M"</span>}
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Count of filtered planet names: <span class="hljs-subst">\(filteredPlanetNames.count)</span>"</span>)

<span class="hljs-comment">//-> Count of filtered planet names: 2</span>
</code></pre>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Example 2: Filter the address array to only addresses from zip code 1200</span>
<span class="hljs-keyword">let</span> filteredAddresses <span class="hljs-operator">=</span> addresses.filter {<span class="hljs-variable">$0</span>.zipcode <span class="hljs-operator">==</span> <span class="hljs-number">1200</span>}
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Count of filtered addresses: <span class="hljs-subst">\(filteredAddresses.count)</span>"</span>)

<span class="hljs-comment">//-> Count of filtered addresses: 1</span>
</code></pre>
<h2><a href="https://developer.apple.com/documentation/swift/sequence/2907677-reduce">Reduce</a></h2>
<p>Reduce is used when you want to combine all elements in a collection into one value.</p>
<p><strong>Time complexity: O(n)</strong> - Linear - where n is the length of the sequence.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Example 1: Sum of numbers - version 1</span>
<span class="hljs-keyword">let</span> numbers <span class="hljs-operator">=</span> [<span class="hljs-number">5</span>, <span class="hljs-number">3</span>, <span class="hljs-number">2</span>, <span class="hljs-number">6</span>, <span class="hljs-number">10</span>, <span class="hljs-number">23</span>, <span class="hljs-number">01</span>, <span class="hljs-number">43</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]

<span class="hljs-keyword">let</span> sumOfNumbers <span class="hljs-operator">=</span> numbers.reduce(<span class="hljs-number">0</span>, {<span class="hljs-variable">$0</span> <span class="hljs-operator">+</span> <span class="hljs-variable">$1</span>})
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Sum of numbers - version 1: <span class="hljs-subst">\(sumOfNumbers)</span>"</span>)

<span class="hljs-comment">//-> Sum of numbers - version 1: 122</span>
</code></pre>
<p>The same can be written a bit shorter, like this:</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Example 2: Sum of numbers - version 2</span>
<span class="hljs-keyword">let</span> sumOfNumbers2 <span class="hljs-operator">=</span> numbers.reduce(<span class="hljs-number">0</span>,<span class="hljs-operator">+</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Sum of numbers - version 2: <span class="hljs-subst">\(sumOfNumbers)</span>"</span>)

<span class="hljs-comment">//-> Sum of numbers - version 2: 122</span>
</code></pre>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Example 3: Longest planet name</span>
<span class="hljs-keyword">let</span> longestPlanetName <span class="hljs-operator">=</span> planetNames.reduce(<span class="hljs-string">""</span>, {<span class="hljs-variable">$0</span>.count <span class="hljs-operator">></span> <span class="hljs-variable">$1</span>.count <span class="hljs-operator">?</span> <span class="hljs-variable">$0</span> : <span class="hljs-variable">$1</span> } )
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Longest planet name: <span class="hljs-subst">\(longestPlanetName)</span>"</span>)

<span class="hljs-comment">//-> Longest planet name: neptune</span>
</code></pre>
<h2><a href="https://developer.apple.com/documentation/swift/sequence/1641066-sorted">Sorted</a></h2>
<p>When calling sorted() on an array, it will return a new array that has the items sorted in ascending order. For this method to work, the elements in the array need to conform to the <a href="https://developer.apple.com/documentation/swift/comparable">Comparable</a> protocol.</p>
<p><strong>Time complexity: O(n log n)</strong> - where n is the length of the sequence.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Example 1: Sorting numbers ascending</span>
<span class="hljs-keyword">let</span> sortedNumbersAscending <span class="hljs-operator">=</span> numbers.sorted()
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Sorted numbers ascending: <span class="hljs-subst">\(sortedNumbersAscending)</span>"</span>)

<span class="hljs-comment">//-> Sorted numbers ascending: [1, 2, 3, 5, 5, 6, 7, 8, 9, 10, 23, 43]</span>
</code></pre>
<p>If you want the numbers sorted descending instead, you can use sorted like this as well:</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">//Example 2: Sorted numbers descending - version 1</span>
<span class="hljs-keyword">let</span> sortedNumbersDescending <span class="hljs-operator">=</span> numbers.sorted { (a, b) -> <span class="hljs-type">Bool</span> <span class="hljs-keyword">in</span>
    a <span class="hljs-operator">></span> b
}
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Sorted numbers descending - version 1: <span class="hljs-subst">\(sortedNumbersDescending)</span>"</span>)

<span class="hljs-comment">//-> Sorted numbers descending - version 1: [43, 23, 10, 9, 8, 7, 6, 5, 5, 3, 2, 1]</span>
</code></pre>
<p>The same can be written a bit easier, like this:</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">//Example 3: Sorted numbers descending - version 2</span>
<span class="hljs-keyword">let</span> sortedNumbersDescending2 <span class="hljs-operator">=</span> numbers.sorted{(<span class="hljs-variable">$0</span> <span class="hljs-operator">></span> <span class="hljs-variable">$1</span>)}
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Sorted numbers descending - version 2: <span class="hljs-subst">\(sortedNumbersDescending2)</span>"</span>)

<span class="hljs-comment">//-> Sorted numbers descending - version 2: [43, 23, 10, 9, 8, 7, 6, 5, 5, 3, 2, 1]</span>
</code></pre>
<p>Or like this:</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">//Example 4: Sorted numbers descending - version 3</span>
<span class="hljs-keyword">let</span> sortedNumbersDescending3 <span class="hljs-operator">=</span> numbers.sorted(by: <span class="hljs-operator">></span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Sorted numbers descending - version 3: <span class="hljs-subst">\(sortedNumbersDescending3)</span>"</span>)

<span class="hljs-comment">//-> Sorted numbers descending - version 3: [43, 23, 10, 9, 8, 7, 6, 5, 5, 3, 2, 1]</span>
</code></pre>
<h2><a href="https://developer.apple.com/documentation/swift/sequence/2905332-flatmap">FlatMap</a></h2>
<p>If you look up flatMap in the Swift documentations you will find the following explanation:
Returns an array containing the concatenated results of calling the given transformation with each element of this sequence.
Basically flatMap flattens an array, containing more arrays with the same content type, into one "flat" array.
If you're using a flatMap on a "flat" array, it will split all items in that array (taken they're of the same type), as the function flattens all subcollections into one single collection.</p>
<p><strong>Time complexity: O(m + n)</strong> - where n is the length of this sequence and m is the length of the result.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Example 1: Flatmap without optionals</span>
<span class="hljs-keyword">let</span> names <span class="hljs-operator">=</span> [[<span class="hljs-string">"roxana"</span>, <span class="hljs-string">"peter"</span>, <span class="hljs-string">"jacob"</span>, <span class="hljs-string">"morten"</span>],[<span class="hljs-string">"iben"</span>, <span class="hljs-string">"nour"</span>, <span class="hljs-string">"nicolai"</span>]]

<span class="hljs-keyword">let</span> flatNames <span class="hljs-operator">=</span> names.flatMap({<span class="hljs-variable">$0</span>.sorted()})
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Flatmap of names sorted: <span class="hljs-subst">\(flatNames)</span>"</span>)

<span class="hljs-comment">//-> Flatmap of names sorted: ["jacob", "morten", "peter", "roxana", "iben", "nicolai", "nour"]</span>
</code></pre>
<pre><code class="hljs language-swift"><span class="hljs-comment">//Example 2: Flat Flatmap without optionals</span>
<span class="hljs-keyword">let</span> flatFlatNames <span class="hljs-operator">=</span> flatNames.flatMap({<span class="hljs-variable">$0</span>.sorted()})
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Flat Flatmap of names sorted: <span class="hljs-subst">\(flatFlatNames)</span>"</span>)

<span class="hljs-comment">//-> Flat Flatmap of names sorted: ["a", "b", "c", "j", "o", "e", "m", "n", "o", "r", "t", "e", "e", "p", "r", "t", "a", "a", "n", "o", "r", "x", "b", "e", "i", "n", "a", "c", "i", "i", "l", "n", "o", "n", "o", "r", "u"]</span>
</code></pre>
<pre><code class="hljs language-swift"><span class="hljs-comment">//Example 3: Flat with optionals</span>
<span class="hljs-keyword">let</span> scores <span class="hljs-operator">=</span> [<span class="hljs-string">"1"</span>, <span class="hljs-string">"2"</span>, <span class="hljs-string">"three"</span>, <span class="hljs-string">"four"</span>, <span class="hljs-string">"5"</span>]

<span class="hljs-keyword">let</span> flatMapNumbers: [<span class="hljs-type">Int</span>?] <span class="hljs-operator">=</span> scores.flatMap { str <span class="hljs-keyword">in</span> <span class="hljs-type">Int</span>(str) }
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Flatmap numbers: <span class="hljs-subst">\(flatMapNumbers)</span>"</span>)

<span class="hljs-comment">//-> Flatmap numbers: [Optional(1), Optional(2), nil, nil, Optional(5)]</span>
</code></pre>
<p><img src="/assets/img/articles/2019-07-03-Higher-Order-Functions-In-Swift/flatMap.webp" alt="">
Which takes us to compactMap:</p>
<h2><a href="https://developer.apple.com/documentation/swift/sequence/2950916-compactmap">CompactMap</a></h2>
<p>CompactMap can be "used as flatMap", when you work with optional values. CompactMap can be used to either give you nil-values (which flapMap can't), but also to filter out nil-values from a sequence.</p>
<p><strong>Time complexity: O(m + n)</strong> - where n is the length of this sequence and m is the length of the result.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Example 1: Compact map with optionals</span>
<span class="hljs-keyword">let</span> compactMapped: [<span class="hljs-type">Int</span>?] <span class="hljs-operator">=</span> scores.compactMap { str <span class="hljs-keyword">in</span> <span class="hljs-type">Int</span>(str) }
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Compact map with optionals: <span class="hljs-subst">\(compactMapped)</span>"</span>)

<span class="hljs-comment">//-> Compact map with optionals: [Optional(1), Optional(2), nil, nil, Optional(5)]</span>
</code></pre>
<pre><code class="hljs language-swift"><span class="hljs-comment">//Example 2: Compact map without optionals</span>
<span class="hljs-keyword">let</span> compactMapNumbers: [<span class="hljs-type">Int</span>] <span class="hljs-operator">=</span> scores.compactMap { str <span class="hljs-keyword">in</span> <span class="hljs-type">Int</span>(str) }
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Compact map without optionals: <span class="hljs-subst">\(compactMapNumbers)</span>"</span>)

<span class="hljs-comment">//-> Compact map without optionals: [1, 2, 5]</span>
</code></pre>
<h2>Chaining</h2>
<p>Another great thing about higher order functions is that we can combine(chain) them. This means that what would normally have taken many lines of code can be reduced to a single line.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Example 1: An array of street names from a specific zipcode</span>
<span class="hljs-keyword">let</span> streetNamesFromZipcode <span class="hljs-operator">=</span> addresses.filter {<span class="hljs-variable">$0</span>.zipcode <span class="hljs-operator">==</span> <span class="hljs-number">1200</span>}.map {<span class="hljs-variable">$0</span>.street}
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Street names from specific zipcode: <span class="hljs-subst">\(streetNamesFromZipcode)</span>"</span>)

<span class="hljs-comment">//-> Street names from specific zipcode: ["Nice Boulevard"]</span>
</code></pre>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Example 2: An array of sorted capitalized planet names</span>
<span class="hljs-keyword">let</span> sortedCapitalizedPlanetNames <span class="hljs-operator">=</span> planetNames.map { <span class="hljs-variable">$0</span>.capitalized }.sorted()
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Sorted capitalized plane names: <span class="hljs-subst">\(sortedCapitalizedPlanetNames)</span>"</span>)

<span class="hljs-comment">//-> Sorted capitalized plane names: ["Earth", "Jupiter", "Mars", "Mercury", "Neptune", "Saturn", "Uranus", "Venus"]</span>
</code></pre>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Example 3: Names flat map sorted descending</span>
<span class="hljs-keyword">let</span> descendingFlatNames <span class="hljs-operator">=</span> names.flatMap({<span class="hljs-variable">$0</span>.sorted{<span class="hljs-variable">$0</span> <span class="hljs-operator">></span> <span class="hljs-variable">$1</span>}})
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Descending sorted flat map names: <span class="hljs-subst">\(descendingFlatNames)</span>"</span>)

<span class="hljs-comment">//-> Descending sorted flat map names: ["roxana", "peter", "morten", "jacob", "nour", "nicolai", "iben"]</span>
</code></pre>
<h3>Final Notes</h3>
<p>Getting to understand higher order functions, and learning how to use them in real life projects, especially chained, may require some practice.
When there's a lot happening "behind the scenes", things sometimes just seems like black magic, but when you get your head around them, they become really useful.
So if you're not already using higher order functions, now is a great time to start using them and get them under your skin. You will thank yourself later (as will future developers on your project), as it saves both time and lines of code.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/DuBNA1QMpPA">Ian Dooley</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ARKit 3 brings more power to Apple's Augmented Reality]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/06/26/ARKit-3-brings-more-power-to-Apples-Augmented-Reality</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/06/26/ARKit-3-brings-more-power-to-Apples-Augmented-Reality</guid>
            <pubDate>Wed, 26 Jun 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>We can already see Apple having a big lead when it comes to augmented reality and the new version of ARKit will push them even further. There are amazing opportunities for AR as a mass market E-commerce solution and we can already see big companies taking advantage of it.</p>
<p><img src="/assets/img/articles/2019-06-26-ARKit-3-brings-more-power-to-Apples-Augmented-Reality/arkit-logo.webp" alt=""></p>
<h2><a href="https://developer.apple.com/augmented-reality/arkit/">What is new in ARKit 3?</a></h2>
<p>To make AR a mainstream technology, the virtual content on top of the real world needs to look and behave as realistic as possible. There are a lot of major advances that will definitely make the experience more convincing and bring more use cases.</p>
<h3>People Occlusion</h3>
<p>What we have seen so far in terms of AR content rendering was directly “on top” of the camera. Apple now uses Machine Learning to recognise multiple people in the frame, even if they are only partially visible. Then, it isolates the pixels with people and using depth estimation it approximates the distances of people. If a person is closer to the camera than the AR content, those pixels will be rendered on top of everything, else they will be rendered behind the AR content.</p>
<p>It is an incredibly smart process that is being done on every frame and it is all possible thanks to Apple’s Neural Engine. This new feature will also enable green screen-style effects in almost any environment.</p>
<p><img src="/assets/img/articles/2019-06-26-ARKit-3-brings-more-power-to-Apples-Augmented-Reality/people-occlusion.webp" alt="">
<em>Source <a href="https://developer.apple.com/videos/">Apple</a></em></p>
<h3>Motion Capture</h3>
<p>Capturing the motion of a person and mapping it in real time was previously possible only with special equipment. ARKit now provides developers with the power of tracking the human body and having a skeleton representation of it in just a few lines of code. This is also done using Apple’s Neural Engine and will have a big impact on the game development industry.</p>
<h3>Simultaneous front and back camera</h3>
<p>World tracking is available on the back camera while face tracking uses the true depth camera from the front and now we can make use of both in the same time. This means we can have 2 new types of experiences like interacting with the AR content using your face or enabling face tracking with device orientation and position.</p>
<h3>Face tracking</h3>
<p>ARKit 3 can handle up to 3 faces simultaneously now, and if a person goes out of the view and comes back you can know if it’s the same person. Face filters are incredibly popular not only on social media platforms but also on beauty try-on apps so this improvement will be well received by developers and users.</p>
<p><img src="/assets/img/articles/2019-06-26-ARKit-3-brings-more-power-to-Apples-Augmented-Reality/face-tracking.gif%7D%7D" alt="">
<em>Source <a href="https://developer.apple.com/videos/">Apple</a></em></p>
<h3>A new tracking configuration</h3>
<p>Developers will now have access to a brand new tracking configuration: AR Positional Tracking Configuration. This will be used when you would want to track only the device’s position in space. It uses a lower resolution for the camera image so you can have a low power consumption on the device.</p>
<h3>New Ray-casting API</h3>
<p>To put it simply, this means more accurate placing and support for any kind of surface, not just vertical and horizontal. We will now be able to update the placement of an object if needed when we scanned more and the camera has a better understanding of the environment.</p>
<h3>Collaborative session</h3>
<p>With the previous version of ARKit you were able to create and save a world map, but in order to get a collaborative session you had to send it to another device.</p>
<p>With ARKit 3 you can continuously share the world map information between multiple devices across the network.</p>
<p>How does it work? Imagine you are using an AR app with a friend in a room. Each of you starts moving around and detects surfaces in one part of the room. When you start going around the same area of the room, and both of your devices detect the same feature points, you will now be able to merge maps and have access to each others detected feature points. So your device knows about your friend's scene understanding and the other way around. As you continue scanning the room, you continue sharing the data.</p>
<p>AR Anchors are shared as well, which means that if you add virtual content on your device, the other person can see it as well.
Great potential with those improved collaborative sessions that will open new doors for AR experiences.</p>
<h3>AR Coaching UI</h3>
<p>An important part of an AR experience is guiding your users through the process. Sometimes it's difficult for developers to understand different tracking events and show the relevant messages to users. There were guidelines and resources available from Apple but nothing standardised. The new AR Coaching View is a built-in user interface overlay for AR apps that will ensure a good tracking experience. It will be consistent in all apps so users will be familiar with it.</p>
<h3>Scene Understanding Improvements</h3>
<ul>
<li><strong>Detect up to 100 images at the same time</strong></li>
<li><strong>Detect the scale of an image</strong></li>
<li><strong>Object detection uses Machine Learning now</strong> meaning it is faster and more robust</li>
<li><strong>Plane estimation is also powered by Machine Learning</strong> so the boundaries will be detected faster and more accurately. Walls can now be detected with no feature points thanks to machine learning and you can also get a clarification of the <strong>7 types of plane with ARKit 3: wall, floor, ceiling, table, seat, door or window</strong></li>
</ul>
<h3>Visual coherence enhancements</h3>
<ul>
<li>Ability to <strong>activate and deactivate render options</strong></li>
<li><strong>Depth of field effect</strong> - this makes the virtual content blend with the environment more naturally by adjusting the focus of the objects to be the same as with the real content through the camera</li>
<li><strong>Motion blur</strong> - on virtual content when the object moves quickly or when you move quickly to add an extra dimension to the realism of the experience. When you don’t have any motion blur on your objects, they stand out from the rest of what you are seeing</li>
<li><strong>HDR environment textures</strong></li>
<li><strong>Camera grain</strong> - when we are in a low light environment the camera will produce grain and ARKit will now add that grain to the virtual content so it doesn’t stand out</li>
</ul>
<h3>Record and replay</h3>
<p>This is another extra nice feature for developers to be able to record an environment and take it to Xcode for a faster and more convenient AR work setup, great for prototyping.</p>
<hr>
<p><em>“People Occlusion and the use of motion capture, simultaneous front and back camera, and multiple face tracking will only be supported on devices using A12/A12X Bionic chips, ANE and TrueDepth Camera.”</em>
That means: iPhone XS, iPhone XS Max, iPhone XR and 11-inch and 12.9-inch 2018 iPad Pros. This is restrictive but with a good reason, if you want your users to have the best experience with a futuristic technology like this, you have to take advance of the latest devices and technologies.</p>
<p><a href="https://developer.apple.com/documentation/realitykit">RealityKit</a> and <a href="https://developer.apple.com/augmented-reality/reality-composer/">RealityComposer</a> were also announced and they will directly make the developers' work easier when it comes to creating rich augmented reality experiences.
ARKit 3 is available as a beta version now together with iOS 13 until the public release in the fall.</p>
<hr>
<h2>3 cool examples of companies that use AR today</h2>
<p><a href="https://www.wayfair.com/">Wayfair</a> is an online store for furniture, decor, lighting, outdoor and more and they are also early adopters of the technology. Potential clients can see <a href="https://apps.apple.com/us/app/wayfair-shop-all-things-home/id836767708">life-sized versions of the products directly in their space before buying</a>. The benefits of using AR in this saturated industry are well known and other companies like Ikea, Target, Home Depot, Neybers are placing themselves ahead of competition with augmented reality.</p>
<blockquote>
<p><em>“People who tried the View in Room feature on a product page were 3.4x more likely to buy that item.”</em> - Wayfair</p>
</blockquote>
<p><a href="https://www.redbubble.com">Redbubble</a> brings something fun to their users: <a href="https://www.redbubble.com/shop/ar+enabled">AR Enabled Art</a>. Together with the EyeJack Augmented Reality App and 12 artists from all over the world, they created a special collection of products. Using the <a href="https://eyejackapp.com/">Eyejack</a> app you can activate the artworks to become an immersive augmented reality experience.</p>
<p><div class="embed-container">
      <iframe
          src="https://www.youtube.com/embed/2lat70Aa3tU"
          width="700"
          height="480"
          frameborder="0"
          allowfullscreen="">
      </iframe>
    </div>
    </p>
<p><a href="https://www.loreal.com/">L’Oréal</a> expanded into beauty tech last year by acquiring <a href="http://modiface.com/">Modiface</a>, a well known 3D virtual make-up, colour and skin diagnosis service. There are multiple apps now powered by Modiface’s augmented reality technology like Facebook, Snapchat, Instagram, Garnier, Sephora and the latest one being Amazon.</p>
<blockquote align="center" class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">We are thrilled about the launch of Amazon's first-ever beauty virtual try-on experience powered by ModiFace! <a href="https://t.co/ppu5E1COyp">pic.twitter.com/ppu5E1COyp</a></p>— ModiFace (@ModiFace) <a href="https://twitter.com/ModiFace/status/1136645525044957185?ref_src=twsrc%5Etfw">June 6, 2019</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<hr>
<p><em>Fun fact: Apple takes the 3rd place as an industry smart glasses platform after Microsoft HoloLens and Magic Leap, even if they haven’t even launched any AR glasses yet. According to the <a href="https://www.digi-capital.com/reports/#augmented-virtual-reality">research firm Digi-Capital’s and Augmented World Expo’s report</a></em></p>
<h2>Resources</h2>
<ul>
<li><a href="https://developer.apple.com/augmented-reality/">Apple Augmented Reality</a></li>
<li><a href="https://blog.redbubble.com/2018/10/a-collaboration-with-eyejack/">Redbubble collaboration with Eyejack</a></li>
<li><a href="https://mediaroom.loreal.com/en/loreal-acquires-modiface-further-expanding-its-worldwide-expertise-in-beauty-tech/">L'oréal acquires Modiface further expanding its worldwide expertise in beauty tech</a></li>
</ul>
<p><em>Article Photo by <a href="https://unsplash.com/photos/87oz2SoV9Ug">Patrick Schneider</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Add a Swift Package to Your iOS Application]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/06/21/Add-A-Swift-Package-to-Your-iOS-Application</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/06/21/Add-A-Swift-Package-to-Your-iOS-Application</guid>
            <pubDate>Fri, 21 Jun 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>With the release of Swift 3.0, Apple introduced the Swift Package Manager, a tool for managing the distribution of Swift code. It is integrated with the Swift build system to automate the process of downloading, compiling, and linking dependencies.</p>
<p>Though in the beginning, Swift Package Manager didn’t easily support packages for iOS, watchOS, or tvOS, this year, at WWDC 2019, Apple announced that Swift Package Manager is now an integrated part of Xcode and will be available for iOS projects.</p>
<p>Now that Swift Package Manger is supported for iOS, we will see an increased adoption of it for the 3rd party iOS frameworks we use daily, so let us examine how we can add a package to our project using Swift Package Manager and also, how we can distribute our own packages using Swift Package Manager.</p>
<p>This two part series will focus on using Swift Package Manager (referenced as SPM) in a client side application and creating your own Swift Package.</p>
<h3>Add Packages to Your iOS Application</h3>
<p>Since SPM comes integrated in the Swift build system, if you have Xcode on your machine, and most likely you do, you already have everything you need to get started.</p>
<p>With the release of Xcode 11 SPM is an integrated part of Xcode, so in order to continue you will require to have this - at the time of the article, beta - version installed on your machine.</p>
<p>For this example we will integrate a small framework we have developed here at Nodes into an new Xcode project. The framework is called Rye and can be downloaded <a href="https://github.com/nodes-ios/Rye">here</a>.</p>
<p>To get started we are going to create a new Xcode projected, called <code>SwiftPackageIntegrationDemo</code>.</p>
<p>After you have created the project, integrating an existing Swift Package becomes a trivial thing.</p>
<p>To add a package navigate to <b>File -> Swift Packages -> Add Package Dependency </b>.</p>
<p>Once you have selected Add Package Dependency, you will be presented with a window where you can either selected to add a package from your Github account or add a package via a package repository URL.</p>
<p><img src="/assets/img/articles/2019-06-21-Add-A-Swift-Package-to-Your-iOS-Application/1.webp" alt=""></p>
<p>Go ahead and paste <code>https://github.com/nodes-ios/Rye</code> into the search field and press next.</p>
<p>Xcode will verify the package and present you with a window that will allow you to set a series of rules, that will define how this package will update.</p>
<p><img src="/assets/img/articles/2019-06-21-Add-A-Swift-Package-to-Your-iOS-Application/2.webp" alt=""></p>
<p>Since Swift Packages should adhere to the semantic versioning rules[“<a href="https://semver.org/%E2%80%9D">https://semver.org/”</a>], you can define what type of versions you want to update too.</p>
<p>The options will be:</p>
<p>Up to Next Major -> since the current version of our Rye package is 1.1.2, this means we will allow Xcode to update our package with every version released until the next major version 2.0.0.</p>
<p>Up to Next Minor -> selecting this will allow Xcode to update our package with every bug fix version released until the next minor version 1.2.0.</p>
<p>Range -> allows you to specify a custom range of versions</p>
<p>Exact -> will only support the exact package version, in our case, 1.1.2</p>
<p>After you have decided on the appropriate versioning rules, go ahead and click next. Xcode will resolve the package and you will be automatically able to use it in your project.</p>
<p>Go ahead and test this by editing your <code>ViewController.swift</code> to contain the following code:</p>
<pre><code class="hljs language-swift">
<span class="hljs-keyword">import</span> UIKit
<span class="hljs-keyword">import</span> Rye

<span class="hljs-keyword">class</span> <span class="hljs-title class_">ViewController</span>: <span class="hljs-title class_">UIViewController</span> {

    <span class="hljs-keyword">override</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">viewDidLoad</span>() {
        <span class="hljs-keyword">super</span>.viewDidLoad()

        <span class="hljs-keyword">let</span> ryeConfiguration: <span class="hljs-type">RyeConfiguration</span> <span class="hljs-operator">=</span> [<span class="hljs-type">Rye</span>.<span class="hljs-type">Configuration</span>.<span class="hljs-type">Key</span>.text: <span class="hljs-string">"Message for the user"</span>]

        <span class="hljs-type">DispatchQueue</span>.main.asyncAfter(deadline: .now() <span class="hljs-operator">+</span> <span class="hljs-number">0.5</span>) {
            <span class="hljs-keyword">let</span> rye <span class="hljs-operator">=</span> <span class="hljs-type">RyeViewController</span>.<span class="hljs-keyword">init</span>(alertType: .toast,
                                            viewType: .standard(configuration: ryeConfiguration),
                                            at: .bottom(inset: <span class="hljs-number">16</span>),
                                            timeAlive: <span class="hljs-number">2</span>)
                                            rye.show()
        }
    }

}

</code></pre>
<p>Build and Run the project and voila, you will see an instance of your Rye message created.</p>
<h2>Final notes</h2>
<p>In this post we saw how Swift Package Manager makes it easy to manage your dependencies, allowing you to focus more on developing the core functionality of your application.</p>
<p>In Part 2 of our series, <a href="/en2019-06-21-Create-Your-Own-Swift-Package">Create Your Own Swift Package</a>, we will see how you can create your own Swift Package or adopt Swift Package Manager for your existing framework.</p>
<p>Hope to see you next time!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Create Your Own Swift Package]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/06/21/Create-Your-Own-Swift-Package</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/06/21/Create-Your-Own-Swift-Package</guid>
            <pubDate>Fri, 21 Jun 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>In Part 1, <a href="/en2019-06-21-Add-A-Swift-Package-to-Your-iOS-Application">Add a Swift Package to Your iOS Application</a>, of this Swift Package Manager series we saw how you can add an already existing package to your application.</p>
<p>Now, you might be wondering, "How can I create my own package?" The answer is simpler than you might expect.</p>
<p>Below we will see how you can create your own package.</p>
<h3>Part 1: Creating a new Package</h3>
<p>Since Swift Package Manger is integrated with the Swift build system, once you have Swift installed on your machine, you can access Swift Package Manager via the Terminal. This means you can also create a package with a simple command.</p>
<p>To do so, create a new folder called <code>MySwiftPackage</code> and navigate to it in Terminal. Once you are here go ahead and run:</p>
<pre><code class="hljs language-go">
$ swift <span class="hljs-keyword">package</span> init --<span class="hljs-keyword">type</span> library

</code></pre>
<p>This command will generate the base for your package and all the attached configuration needed. To verify this worked for you, the output of this command will look something like this:</p>
<pre><code class="hljs language-groovy">
Creating library <span class="hljs-attr">package:</span> MySwiftPackage
Creating Package.swift
Creating README.md
Creating .gitignore
Creating Sources/
Creating Sources<span class="hljs-regexp">/MySwiftPackage/</span>MySwiftPackage.swift
Creating Tests/
Creating Tests/LinuxMain.swift
Creating Tests<span class="hljs-regexp">/MySwiftPackageTests/</span>
Creating Tests<span class="hljs-regexp">/MySwiftPackageTests/</span>MySwiftPackageTests.swift
Creating Tests<span class="hljs-regexp">/MySwiftPackageTests/</span>XCTestManifests.swift

</code></pre>
<p>The points of interests for us are the file called <code>Package.swift</code>, which represents the configuration file for our package. Here you can specify platform limitations, dependencies for our package, versions of the language supported etc. A nice thing about our manifest file is that the syntax is all Swift, which eliminates the need to learn other syntax types.</p>
<p>Sometimes you might want to make your package available only for specific platforms, such as iOS. You can do this by adding a section called <code>platforms</code> to your <code>Package.swift</code>.</p>
<pre><code class="hljs language-swift">
platforms: [
.iOS(.v12),
],

</code></pre>
<p>The above will make our package available only for the iOS, starting with version 12.</p>
<h3>Part 2: Adding Functionality to Your Package</h3>
<p>Now that we have laid out the base for our package, we need to add some functionality to it as well. To do that, we need to look at the structure that the init command has created for us.</p>
<p>In the root folder of your project, SPM has generated a folder called <code>MySwiftPackage</code> inside a folder called <code>Sources</code>. Here - once developed - is where we will add our .swift files containing our functionality.</p>
<p>Once you have done this, push your package to GitHub and create a release. To learn about how you can integrate your package in a new/existing project, make sure to check <a href="/en2019-06-21-Add-A-Swift-Package-to-Your-iOS-Application">Part 1</a> of this Swift Package Manager series.</p>
<h3>Part 3: Updating an Existing Framework to a Swift Package.</h3>
<p>Typically, you might already have created a iOS, macOS, watchOS etc. framework, which people are using, and now you would like to enable support for Swift Package Manager as well.</p>
<p>Navigate to your project’s root in Terminal and follow the instructions presented on point 1 of this article.</p>
<p>Once you have done that and you have the base for your package, you just need to link the existing code to your <code>Sources/MySwiftPackage</code> folder.</p>
<p>To do this you will need to use the Terminal once again where you will be able to link your existing functionality by running the following command for all the files containing functionality in your framework:</p>
<pre><code class="hljs language-shell"><span class="hljs-meta prompt_">$ </span><span class="bash">ln -s <span class="hljs-built_in">source</span> target`</span>

</code></pre>
<p>So if for example our package contains only one file <code>DataFormatter.swift</code> that is in charge of formatting some data for our application, the source will be replaced with the path of the file and the target will be replace with <code>Sources/MySwiftPackage</code>.</p>
<p>Now to release your package, you will need to push it to GitHub and create a release.</p>
<h2>Final notes</h2>
<p>Swift Package Manage represents an alternative to CocoaPods and Carthage when it comes to distrubuting your packages and with it now being part of Xcode 11, we expect it's popularity to increase and to see more are more packages distributed with it in the near future.</p>
<p>For further reading make sure to read Apple's Swift Package Manger documentation <a href="https://swift.org/package-manager/">here</a>.</p>
<p>Hope to see you next time!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Serverless Server-side Swift using Google Cloud Run]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/05/27/serverless-serverside-swift-using-google-cloud-run</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/05/27/serverless-serverside-swift-using-google-cloud-run</guid>
            <pubDate>Mon, 27 May 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Learn about Google's new service launched recently – Google Cloud Run – and how it allows you to run <strong>server-side</strong> swift <strong>server-less</strong> 😱.
In part 1 we will cover the basics and deploy a Vapor application in less than 10 minutes.</p>
<h2>Part 1: Deploying a Server-side Swift application</h2>
<p><img src="/assets/img/articles/2019-05-27-serverless-serverside-swift-using-google-cloud-run/gcr-post-logo.webp" alt=""></p>
<h3>Serverless</h3>
<p>Serverless is there to make our developer's life easier and costs more manageable. By offering us infrastructure and platforms as a service on a per-use basis we don’t have to deal with configuring or monitoring servers as well as thinking too much about scaling. Often these services are offered by cloud providers as FaaS (Function As A Service), e.g. AWS Lambda, Azure Cloud Functions etc.</p>
<p>Apart from the <strong>usual drawbacks</strong> when going for a serverless architecture (see more below) you are often limited to a small amount of supported programming languages such as python, ruby, nodejs and go (<a href="https://www.ibm.com/cloud/blog/performant-swift-serverless-actions">IBM supports serverside swift functions</a>).</p>
<h4>Google Cloud Run</h4>
<p>Well, Google introduced a new solution <strong>Google Cloud Run</strong> that provides you with a fully managed serverless execution environment for <strong>containerized apps</strong>. This means you can run any container — with the <strong>language of your choice</strong> — on their platform.</p>
<p>Google Cloud Run Overview</p>
<blockquote>
<p>As of writing this you can try out Google Cloud Platform for free, getting $300 free credit to spend over 12 months. <a href="https://cloud.google.com/free/">https://cloud.google.com/free/</a></p>
</blockquote>
<h3>Running Server-side Swift on Google Cloud Run</h3>
<p>I’m choosing <a href="https://vapor.codes">Vapor</a> as my weapon of choice when it comes to server-side Swift, but this should also work with alternative frameworks such as <a href="https://kitura.io">Kitura</a> or <a href="https://perfect.org">Perfect</a>.</p>
<p>The following steps are required:</p>
<ul>
<li>Setup an account on Google Cloud platform (not covered in this tutorial)</li>
<li>Create a project on Google Cloud platform</li>
<li>Install Google Cloud toolbox (CLI)</li>
<li>Setup your Vapor project</li>
<li>Create and configure the Dockerfile</li>
<li>Build your project and push the image to Google Cloud Container Registry</li>
<li>Deploy the image to Google Cloud Run</li>
</ul>
<p>Okay, let’s get our hands dirty! 🙌</p>
<h4>Create project on GCR (or use an existing one)</h4>
<p>For testing out things it is always nice to create a new project that you can easily delete later (and with it, all its resources).</p>
<ol>
<li>Go to <a href="https://console.cloud.google.com/projectcreate">https://console.cloud.google.com/projectcreate</a></li>
<li>Enter a name for your project, create the project</li>
<li>Remember the project id, e.g. <code>gcr-blog-example</code></li>
</ol>
<h4>Install Google Cloud toolbox</h4>
<p>I’m assuming you are on a Mac and have <a href="https://docs.brew.sh/Installation">homebrew installed</a>.</p>
<ol>
<li>
<p>Install the Google Cloud SDK command line interface:</p>
<pre><code class="hljs language-bash">$ brew install homebrew/cask/google-cloud-sdk
</code></pre>
</li>
<li>
<p>Initialize <code>gcloud</code>, providing access to your Google Cloud account and select your project:</p>
<pre><code class="hljs language-bash">$ gcloud init
</code></pre>
</li>
<li>
<p>Install the beta components needed for Google Cloud Run:</p>
<pre><code class="hljs language-bash">$ gcloud components install beta
$ gcloud components update
</code></pre>
</li>
</ol>
<h4>Setup your Vapor project</h4>
<p>I’m assuming you are already having the Vapor toolbox installed. If not, please follow the <a href="https://docs.vapor.codes/3.0/install/macos/">instructions here</a> (macOS).</p>
<blockquote>
<p><strong>Note: you can get the source code on Github:</strong> <a href="https://github.com/cweinberger/gcr-example-vapor">https://github.com/cweinberger/gcr-example-vapor</a></p>
</blockquote>
<ol>
<li>
<p>Create a new Vapor project (using the default api template):</p>
<pre><code class="hljs language-bash">$ vapor new gcr-example-vapor
</code></pre>
</li>
<li>
<p>Generate the Xcode project and open it:</p>
<pre><code class="hljs language-bash">$ vapor xcode -y
</code></pre>
</li>
<li>
<p>Let's collect some information about the container we're currently on. Change your <code>boot.swift</code> to:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> Vapor

<span class="hljs-keyword">internal</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">ContainerInfo</span>: <span class="hljs-title class_">Content</span> {

    <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> date: <span class="hljs-type">Date</span> <span class="hljs-operator">=</span> <span class="hljs-type">Date</span>()
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> containerID: <span class="hljs-type">Int</span> <span class="hljs-operator">=</span> <span class="hljs-type">Int</span>.random(in: <span class="hljs-number">0</span><span class="hljs-operator">...</span><span class="hljs-type">Int</span>.max)

    <span class="hljs-keyword">let</span> creationDate <span class="hljs-operator">=</span> <span class="hljs-type">ContainerInfo</span>.date
    <span class="hljs-keyword">let</span> currentDate <span class="hljs-operator">=</span> <span class="hljs-type">Date</span>()
    <span class="hljs-keyword">let</span> uuid <span class="hljs-operator">=</span> <span class="hljs-type">ContainerInfo</span>.containerID
}

<span class="hljs-comment">/// Called after your application has initialized.</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">boot</span>(\<span class="hljs-keyword">_</span> <span class="hljs-params">app</span>: <span class="hljs-type">Application</span>) <span class="hljs-keyword">throws</span> {
    <span class="hljs-comment">// Your code here</span>
    <span class="hljs-keyword">_</span> <span class="hljs-operator">=</span> <span class="hljs-type">ContainerInfo</span>()
}
</code></pre>
</li>
<li>
<p>In the bottom of <code>routes.swift</code> add this route:</p>
<pre><code class="hljs language-swift">router.get(<span class="hljs-string">"gcr-example"</span>) { req <span class="hljs-keyword">in</span>
    <span class="hljs-keyword">return</span> <span class="hljs-type">ContainerInfo</span>()
}
</code></pre>
</li>
<li>
<p>Run the project locally (CMD+R) — make sure to select the <code>Run</code> target.</p>
</li>
<li>
<p>Try our new endpoint! In terminal enter:</p>
<pre><code class="hljs language-bash">$ curl localhost:8080/gcr-example
</code></pre>
</li>
<li>
<p>Result:</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
  <span class="hljs-attr">"currentDate"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2019-05-22T04:26:49Z"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"uuid"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"1939D9E0-A67A-412D-9C4F-55722EFA1751"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"creationDate"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2019-05-22T04:17:18Z"</span>
<span class="hljs-punctuation">}</span>
</code></pre>
</li>
</ol>
<p><em>⚠️ Note that the default template from Vapor uses <strong>SQLite</strong> as Fluent driver which <strong>doesn’t make a lot of sense when working with horizontally scaled systems</strong>. If you need to persist your data (across nodes) you should use a different solution, e.g. Google Cloud SQL, or any separate database server.</em></p>
<h4>Create and configure the Dockerfile</h4>
<p>The default Vapor template already includes a Dockerfile 🎉. Let’s take this one as a basis and adjust to fit our (Google’s) needs:</p>
<ol>
<li>
<p>Create a copy of the Dockerfile shipped with the example: <code>$ cp web.Dockerfile Dockerfile</code></p>
</li>
<li>
<p>Change the <code>ENV</code> in line 30 to <code>development</code></p>
</li>
<li>
<p>Google Cloud Run expects containers to listen on port <code>8080</code>, therefore change the the port from <code>80</code> to <code>8080</code> in line 32</p>
<pre><code class="hljs language-bash"><span class="hljs-comment"># You can set the Swift version to what you need for your app. Versions can be found here: https://hub.docker.com/_/swift</span>
FROM swift:5.0 as builder
<span class="hljs-comment"># For local build, add `--build-arg env=docker`</span>
<span class="hljs-comment"># In your application, you can use `Environment.custom(name: "docker")` to check if you're in this env</span>
ARG env
RUN apt-get -qq update &#x26;&#x26; apt-get install -y \
  libssl-dev zlib1g-dev \
  &#x26;&#x26; rm -r /var/lib/apt/lists/*
WORKDIR /app
COPY . .
RUN mkdir -p /build/lib &#x26;&#x26; cp -R /usr/lib/swift/linux/*.so* /build/lib
RUN swift build -c release &#x26;&#x26; mv `swift build -c release --show-bin-path` /build/bin
<span class="hljs-comment"># Production image</span>
FROM ubuntu:18.04
ARG env
<span class="hljs-comment"># DEBIAN_FRONTEND=noninteractive for automatic UTC configuration in tzdata</span>
RUN apt-get -qq update &#x26;&#x26; DEBIAN_FRONTEND=noninteractive apt-get install -y \
  libatomic1 libicu60 libxml2 libcurl4 libz-dev libbsd0 tzdata \
  &#x26;&#x26; rm -r /var/lib/apt/lists/*
WORKDIR /app
COPY --from=builder /build/bin/Run .
COPY --from=builder /build/lib/* /usr/lib/
<span class="hljs-comment"># Uncomment the next line if you need to load resources from the `Public` directory</span>
<span class="hljs-comment">#COPY --from=builder /app/Public ./Public</span>
<span class="hljs-comment"># Uncomment the next line if you are using Leaf</span>
<span class="hljs-comment">#COPY --from=builder /app/Resources ./Resources</span>
ENV ENVIRONMENT=development
ENTRYPOINT ./Run serve --env <span class="hljs-variable">$ENVIRONMENT</span> --hostname 0.0.0.0 --port 8080
</code></pre>
</li>
<li>
<p>(Optional) If you have Docker installed, you can test locally now by building and running it:</p>
<pre><code class="hljs language-bash">$ docker build -t gcr-example-vapor .
$ docker run -it -p 8080:8080 gcr-example-vapor
</code></pre>
</li>
</ol>
<p>This will make our endpoint available at <code>localhost:8080/gcr-example</code>. <em>You can skip this step if you don’t have Docker.</em></p>
<h4>Build your image &#x26; deploy to Google Cloud Run</h4>
<p>Now we are almost done! We are using the CLI tool <code>gcloud</code> that we installed earlier to build and upload our image to Google Cloud Registry. Then we will deploy it using Google Cloud Run.</p>
<ol>
<li>
<p>Submit the build:</p>
<pre><code class="hljs language-bash">$ gcloud builds submit --tag gcr.io/gcr-blog-example/gcr-example-vapor
</code></pre>
</li>
<li>
<p>Deploy and run it on Google Cloud Run:</p>
<pre><code class="hljs language-bash">$ gcloud beta run deploy --image gcr.io/gcr-blog-example/gcr-example-vapor
</code></pre>
<p>(replace <code>gcr-blog-example</code> with your <strong>project id</strong> and <code>gcr-example-vapor</code> with a <strong>container name</strong> of your choice)</p>
</li>
<li>
<p>In the output you will see the URL, e.g. <em><a href="https://gcr-example-vapor-%7Bsome-id%7D-uc.a.run.app">https://gcr-example-vapor-{some-id}-uc.a.run.app</a></em>.</p>
</li>
</ol>
<p>We are done now, <strong>you can try it out</strong> with curl or in the browser of your choice:</p>
<p><img src="/assets/img/articles/2019-05-27-serverless-serverside-swift-using-google-cloud-run/screenshot-result.webp" alt="">
<em>Interesting: <strong>creationDate</strong> and <strong>uuid</strong> never change 🤔. That’s something for the next blog posts 🕵️‍♂️.</em></p>
<h3>Up next</h3>
<p>This was as easy as it should be when dealing with serverless applications. In <strong>less than 10 minutes</strong> we were able to build &#x26; configure a project and deploy it into the wild using Google Cloud Run.</p>
<p>In the next articles we will be focussing on <strong>advantages and disadvantages</strong> of this architecture, have a deep look into <strong>performance</strong> and create more <strong>elaborate examples</strong>.</p>
<p>Thanks for reading!<br>
– Christian</p>
<h3>Links</h3>
<ul>
<li>Github repository with all sources: <a href="https://github.com/cweinberger/gcr-example-vapor">https://github.com/cweinberger/gcr-example-vapor</a></li>
<li>Nodes Engineering Blog: <a href="https://engineering.nodesagency.com">https://engineering.nodesagency.com</a></li>
<li>Vapor: <a href="https://vapor.codes">https://vapor.codes</a></li>
<li>Google Cloud Platform: <a href="https://cloud.google.com/">https://cloud.google.com/</a></li>
<li>Google Cloud Run: <a href="https://cloud.google.com/run/">https://cloud.google.com/run/</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Automate Debugging and Testing Workflows using ADB]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/04/29/automate-debugging-and-testing-workflows-using-adb</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/04/29/automate-debugging-and-testing-workflows-using-adb</guid>
            <pubDate>Mon, 29 Apr 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>While developing and testing we usually come across repetitive tasks involving manual efforts.
Navigating to a certain part of the app, filling out sign up forms or simply taking screenshots are all time-consuming tasks.
In this blog post we look at how we can utilize <a href="https://developer.android.com/studio/command-line/adb">ADB</a> (Android Debug Bridge) to automate and speed up mundane workflows.</p>
<h2>Basics</h2>
<p>To get an overview of all your connected devices run following command:</p>
<pre><code class="hljs language-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># List all connected devices</span>
adb devices
</code></pre>
<p>You can wake up a device by simulating a power-button press like:</p>
<pre><code class="hljs language-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Wake up device</span>
adb shell input keyevent 26
</code></pre>
<p>If you have more then one connected device you need to specify which device you want to target:</p>
<pre><code class="hljs language-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Wake up a specific device</span>
adb -s emulator-5554 shell input keyevent 26
</code></pre>
<p>At Nodes all our test devices come locked with a pin code. So why not have a script for waking up and unlocking a device so we don't have to manually enter a pin every time we want to interact with it:</p>
<pre><code class="hljs language-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Unlocks a pin (1234) locked device</span>

<span class="hljs-comment"># Wake up</span>
adb shell input keyevent 26

<span class="hljs-comment"># Unlock screen</span>
adb shell input keyevent 82

<span class="hljs-comment"># Enter and confirm pin</span>
adb shell input text 1234
adb shell input keyevent 66
</code></pre>
<p>Let's open our app. We simply provide our package name and have <a href="https://developer.android.com/studio/test/monkey">Monkey</a> trigger a LAUNCHER intent for us:</p>
<pre><code class="hljs language-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Open application</span>
adb shell monkey -p com.package.name -c android.intent.category.LAUNCHER 1
</code></pre>
<p>If you need to install your app first, generate your APK and install it with:</p>
<pre><code class="hljs language-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Install an APK</span>
adb install -r name.apk
</code></pre>
<p>To uninstall an app use:</p>
<pre><code class="hljs language-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Uninstall an app</span>
adb uninstall com.package.name
</code></pre>
<p>Sometimes you might want to clear your app's data:</p>
<pre><code class="hljs language-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Clear app data</span>
adb shell pm clear com.package.name
</code></pre>
<p>Or navigate to app settings on an older Samsung phone:</p>
<pre><code class="hljs language-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Open app settings</span>
adb shell am start -a android.settings.APPLICATION_DETAILS_SETTINGS package:com.package.name
</code></pre>
<h2>Permissions</h2>
<p>You can revoke all permissions running:</p>
<pre><code class="hljs language-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Reset all permissions</span>
adb shell pm reset-permissions
</code></pre>
<p>Or just a single one using:</p>
<pre><code class="hljs language-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Revoke WRITE_EXTERNAL_STORAGEl permission</span>
adb shell pm revoke com.package.name android.permission.WRITE_EXTERNAL_STORAGE
</code></pre>
<p>To grant a permission run:</p>
<pre><code class="hljs language-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Grant WRITE_EXTERNAL_STORAGE permission</span>
adb shell pm grant com.package.name android.permission.WRITE_EXTERNAL_STORAGE
</code></pre>
<h2>Screenshots</h2>
<p>If we want to take a nice screenshot from our app without clutter in the status bar we can combine following commands:</p>
<pre><code class="hljs language-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Cleans up the status bar and takes a screenshot</span>

<span class="hljs-comment"># Start demo mode</span>
adb shell settings put global sysui_demo_allowed 1

<span class="hljs-comment"># Display time 10:10</span>
adb shell am broadcast -a com.android.systemui.demo -e <span class="hljs-built_in">command</span> clock -e hhmm 1010

<span class="hljs-comment"># Display mobile data level 4 without type</span>
adb shell am broadcast -a com.android.systemui.demo -e <span class="hljs-built_in">command</span> network -e mobile show -e level 4 -e datatype <span class="hljs-literal">false</span>

<span class="hljs-comment"># Display wifi level 4</span>
adb shell am broadcast -a com.android.systemui.demo -e <span class="hljs-built_in">command</span> network -e mobile show -e level 4

<span class="hljs-comment"># Show battery level 100 not charging</span>
adb shell am broadcast -a com.android.systemui.demo -e <span class="hljs-built_in">command</span> battery -e plugged <span class="hljs-literal">false</span> -e level 100

<span class="hljs-comment"># Take screenshot</span>
adb shell screencap /sdcard/screenshot_file_name.png

<span class="hljs-comment"># Exit demo mode</span>
adb shell am broadcast -a com.android.systemui.demo -e <span class="hljs-built_in">command</span> <span class="hljs-built_in">exit</span>
</code></pre>
<p>You can find all possible demo mode commands in a link at the end of this article.</p>
<p>If you want to record the screen instead replace the screencap command with:</p>
<pre><code class="hljs language-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Records a device's screen</span>
adb shell screenrecord /sdcard/video_file_name.mp4
</code></pre>
<p>To get a hold of the screenshot or video you took run:</p>
<pre><code class="hljs language-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Downloads screenshot to computer</span>
adb pull /sdcard/screenshot_file_name.png
</code></pre>
<h2>Deep-Links</h2>
<p>Let's say we have a deep-link for verifying a user's email address. Instead of setting up our email account to access the mail on a test device we can simply run this script:</p>
<pre><code class="hljs language-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Opens a deep-link for verifying a user's email address</span>

<span class="hljs-built_in">echo</span> Enter your email verification code:
<span class="hljs-built_in">read</span> code

adb shell am start -W -a android.intent.action.VIEW -d <span class="hljs-string">"appname://user/activate?verificationcode=<span class="hljs-variable">$code</span>"</span>
</code></pre>
<p>The script above prompts you to enter the verification code, builds the deeplink and launches the application.</p>
<h2>Autofill Input Forms</h2>
<p>Most apps offer some sort of sign-up/sign-in flow. For sign-in we usually have to fill out our email and password. Sign-up screens on the other hand can require much more details like confirming a password or accepting a privacy policy. Wouldn't it be nice to have a script filling out all these forms for us?</p>
<pre><code class="hljs language-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Autofill script for user sign-in form. Navigate to UserSignInFragment before executing.</span>

<span class="hljs-built_in">echo</span> Enter your nodes user name:
<span class="hljs-built_in">read</span> username

<span class="hljs-comment"># Enter email</span>
adb shell input keyevent 61
adb shell input text <span class="hljs-string">"<span class="hljs-variable">$username</span>+appname@nodesagency.com"</span>

<span class="hljs-comment"># Enter password</span>
adb shell input keyevent 61
adb shell input text <span class="hljs-string">"SuperSecret123\!"</span>
</code></pre>
<p>Having Gmail as our default email service comes in handy here since one account can offer multiple test accounts. Simply extend your email with <code>+</code> and you have a new address for testing that points to your inbox. To avoid the need of adjusting the email in the script we simply prompt for a user name. This way every developer at Nodes can run the script right away.</p>
<p>Keep in mind that relying on key events for navigating our app requires a proper setup of focusable views. You can learn more about that under developer.android.com/training/keyboard-input/navigation. It's always good practice to support keyboard navigation, Chromebook users will appreciate it as well!</p>
<p>You can find all key constants in a link at the end of this article. For most cases the Tab key (61) will be sufficient.</p>
<h2>SharedPreferences</h2>
<p>I already showed how to clear all local data of an app, though what we really want most of the time is to change a specific key. <a href="https://github.com/jfsso/PreferencesEditor">PreferencesEditor</a> allows you to easily add, edit and remove keys from your shared preferences files.</p>
<p>After adding PreferencesEditor as a dependency to our debug builds we can change a value simply by:</p>
<pre><code class="hljs language-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Sets a value in default shared preferences</span>
adb shell <span class="hljs-string">'am broadcast -a com.package.name.sp.PUT --es key key_name --es value "Hello world!"'</span>
</code></pre>
<p>Or remove a value by running:</p>
<pre><code class="hljs language-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Removes a value in default shared preferences</span>
adb shell <span class="hljs-string">'am broadcast -a com.package.name.sp.REMOVE --es key key_name'</span>
</code></pre>
<h2>Conclusion</h2>
<p>I hope this blog post gave you some ideas on how to save time during debugging and testing. If you find yourself doing something over and over again try to automate it!</p>
<p>All the links to get going:</p>
<p>Android Debug Bridge: <a href="https://developer.android.com/studio/command-line/adb">https://developer.android.com/studio/command-line/adb</a></p>
<p>Android Key Constants: <a href="https://developer.android.com/reference/android/view/KeyEvent.html">https://developer.android.com/reference/android/view/KeyEvent.html</a></p>
<p>Android Keyboard Navigation: <a href="https://developer.android.com/training/keyboard-input/navigation">https://developer.android.com/training/keyboard-input/navigation</a></p>
<p>Android Demo Mode: <a href="https://android.googlesource.com/platform/frameworks/base/+/android-6.0.0_r1/packages/SystemUI/docs/demo_mode.md">https://android.googlesource.com/platform/frameworks/base/+/android-6.0.0_r1/packages/SystemUI/docs/demo_mode.md</a></p>
<p>PreferencesEditor: <a href="https://github.com/jfsso/PreferencesEditor">https://github.com/jfsso/PreferencesEditor</a></p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/U3sOwViXhkY">Franck V.</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Is Flutter ready for production?]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/04/16/is-flutter-ready-for-production</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/04/16/is-flutter-ready-for-production</guid>
            <pubDate>Tue, 16 Apr 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>After hearing about Flutter from different sources and developers with all kinds of background we decided to try to rebuild an existing Android app in Flutter and see the the state of the Mobile framework.</p>
<p>We tried to reproduce as many features as possible from original Android app while learning Dart and researching the best way to architect our Flutter project. After some reading we ended up using the <a href="https://medium.com/flutterpub/architecting-your-flutter-project-bd04e144a8f1">BloC pattern</a>.
BloC is a a simple pattern for defining the business logic of the app. BloC makes it easier to write Android, iOS and AngularDart apps where almost all business logic is shared. In our case we chose to use <a href="https://pub.dartlang.org/packages/flutter_bloc">flutter_bloc</a> library which is an implementation of the BloC pattern.</p>
<h2>Development setup</h2>
<p>I decided to use Intellij Idea for Flutter development because it is updated more frequently than Android Studio and is more stable. Visual Studio Code is also a very popular code editor which has great support for Flutter.
Initial setup requires installing Dart and Flutter plugins with related sdks. Dart Plugin provides Dart language support and the static Analyzer which evaluates Dart code, checking for errors and warnings that are specified in the Dart Language Specification (which can be configured).</p>
<p>Flutter plugin provides tools like Flutter Outline, Inspector, Performance.</p>
<ul>
<li>Outline tool window shows the dart file structure with all the field, methods and widgets. It provides some quick widget actions as well.</li>
<li>Inspector tool window shows the whole widget tree of app current state with the ability to quickly jump to widget source code inside the file and line number it is actually created which is really useful when the app layout gets complex or in case of an issue. Because the layouts in Flutter are built composing different widgets the whole layout tree can get big fast so the Inspector is a great tool to assist in building of UIs.</li>
<li>The Inspector also provides some additional tools which can sometimes save time debugging: Select widget mode, Debug paint. <em>Select widget mode</em> connects the widget shown in the emulator with the widget's source code so clicking anywhere in the emulator will show the exact place in code where the widget is built.</li>
<li>Flutter performance is usually used when there are problems with frame drops or any other unexpected behavior.</li>
</ul>
<p>Compared to Kotlin, Dart support is currently not as good and sometimes the IDE just stops working and needs a restart. Also many code quick fixes and auto completion which we are familiar with when working with Kotlin are not supported Dart yet. Also sometimes the IDE code completions and other features broke, when we had an error in some unrelated file.</p>
<p><img src="/assets/img/articles/2019-04-16-is-flutter-ready-for-production/flutter_tools.webp" alt="Flutter in production"></p>
<h2>Talking to the iOS / Android layer</h2>
<p>Flutter’s platform-specific API support relies on a <a href="https://flutter.dev/docs/development/platform-integration/platform-channels">flexible message passing style</a>:</p>
<ol>
<li>the Flutter part of the app sends a message to the Native part (iOS/ Android) over a platform channel.</li>
<li>The native part listens on the platform channel, receives the message, performs some platform specific actions (native) and sends back a message to the Flutter part.</li>
</ol>
<p>This way we can integrate any platform specific services and third-party libraries.</p>
<h2>Adding Google maps support</h2>
<p>We found some difficulties integrating the Google Maps map with large groups of markers which should be clustered into single markers. Google Maps can be integrated as a library which would become a simple widget, but the library is still considered "Developers Preview" so we can expect bugs and features missing from original library, and:</p>
<blockquote>
<p><em>The API exposed by this plugin is not yet stable, and we expect some breaking changes to land soon.</em></p>
</blockquote>
<p>For example there is no way of quickly enabling the clustering feature because in Android Native it's provided by the Google Maps Android API utility library which is not yet ported to Dart. The library can't be rewritten quickly to Dart either because it contains some platform specific code. We can't build a bridge to the native library because in that case we'd have only an native implentation for Android and not for iOS. Trying to port the library we found out it was more difficult than expected because of some Dart language limitations which required massive refactoring.</p>
<h2>Build toolchain</h2>
<p>Flutter build system uses a pubspec.yaml file that’s written in the YAML language. <strong>flutter</strong> tool which is included with the Flutter sdk provides multiple commands like:</p>
<ul>
<li><em>run</em> (Runs your Flutter app on an attached device)</li>
<li><em>doctor</em> shows information and health about the installed tooling</li>
</ul>
<p><strong>pub</strong> tool which is included with Dart sdk which provides <em>deps</em> (prints package dependencies) <em>global</em> (work with global packages). This is different from Gradle build system because we can't write scripts directly into the YAML file but only write the app package specification (dependencies, required versions, etc). In case we need to run some scripts we can use the <strong>pub</strong> tool. To make a dart script executable it should be included in a package and listed under the <em>executables</em> tag.</p>
<h2>Getting a build on a device</h2>
<p>Android was pretty easy to get running. Debug builds was handled automatically and pushed to a device via adb like you would expect. Signing is configured via Gradle and <a href="https://flutter.dev/docs/deployment/android">the flutter docs</a> describes a way to hide signing info from git.</p>
<p>iOS required some configuration in Xcode. Debug build just required me to configure my team and development provisioning profile. Signing a distribution build was normal iOS procedure, so no headaches there (besides the normal stuff).</p>
<h2>Dart vs Kotlin</h2>
<p>Looking from the code style perspective Kotlin is an elegant language. It's easy to get yourself into trying to improve Kotlin code, make it shorter, more beautiful and readable. Dart, on the other hand, gives an impression of a simple language, which is just enough to get things done. Kotlin has features which are very easy to get used to. While Dart does provide some syntatic sugar in order to make some things easier, it doesn't come close to Kotlin. When switching from Kotlin to Dart it may feel like a step backwards.</p>
<p>Missed features in particular:</p>
<ul>
<li>Nullable types/optionals and all the features related to that</li>
<li>Data classes</li>
<li>Sealed classes</li>
<li>Extension methods</li>
</ul>
<p>Dart has a cool feature though; mixins. Dart allows to add behaviour from one class into another without inheritance. Similar to extension protocols in Swift, but still missing in Kotlin.</p>
<h2>Live code reload - gimmick or for real?</h2>
<p>Developing for Android is not as productive because of long build times and the need of restarting the app every time changes are made. For me, this results in an unpleasant workflow:</p>
<ol>
<li>Made as many changes as I can without running the app</li>
<li>Press run</li>
<li>Wait 1-5 minutes for the build to complete</li>
<li>Navigate to the screen I am working on</li>
<li>Test as many things as I can</li>
<li><code>goto 1</code></li>
</ol>
<p>A live reload would certainly make the things better. Does it work with Flutter? Almost. Most of times it works and it does save a lot of time. It's possible to make adjustments, reload the screen in 1-2 seconds and see the results immedeatly. Finally, I can spend almost all the time actually developing instead of waiting for the build to complete.</p>
<p>I did encounter a couple of issues with code reload.</p>
<p>At first I was reloading the code with VSCode. I would press <code>F5</code> at start, make some changes and after saving the files VSCode would do the reload. It was great, until one day VSCode got updated and the reload on save stopped working. Reload still works in terminal, after running <code>flutter run</code> pressing <code>r</code> will hot reload the app and pressing <code>R</code> will do a hot restart. I am sure there is a way to fix realoding in VSCode, but while working on an app, fixing the tools is the last thing I want to do.</p>
<p>Another issue with reload are bugs which don't make any sense. Sometimes after a number of reloads, modifications don't get applied quite right and this results in weird behaviour which dissappears after restarting the app completly. This makes debugging the app worse, because when encountering a weird bug there is always a thought "Did I mess something up or is it that weird reload thing?".</p>
<p>Another thing worth mentioning is that app needs to be restarted after adding assets or packages. It's not very often you need to add an asset or package to the project, so it's not a big deal. Overall, while live reload has some minor problems, it works quite well and it does save a lot of time.</p>
<h2>Building UI</h2>
<p>The UI is built by composing different widgets so the UI tree can get pretty complex. Usually extracting parts of UI in separate widgets (when widgets get too nested) and files is a good idea to simplify the development and get a better understanding of the UI.</p>
<p>Building UI is faster with flutter because custom widgets are much easier to build and hot reload display the preview without loosing state. Compared to Android's <em>Layout preview</em> tool the Device UI preview is dynamic and displays the actual data which simplifies testing and guarantees pixel perfect elements.</p>
<p>A small bonus to dev mode is in case of overdrawing widgets, Flutter tools will show a warning with the value of pixels which are being overdrawn.</p>
<h2>Final words</h2>
<p>Re-implementing an existing app proved to have some difficulties. Google Maps integration proved to have some pain points and I ended up with keyboard issues due to a <a href="https://github.com/flutter/flutter/issues/17098">navigation stack bug</a>. This could probably be solved by some refactoring, but still acted as a showstopper due to the limited time I had.</p>
<p>Since Flutter isn't translating to any UIKit or Android widgets, platform differences are minimal. Getting the first build up and running on iOS and comparing it to Android was a surprising positive experience. There wasn't any breaking quirks or differences!</p>
<p>Overall, though, I still think Flutter feels like a 0.5 version. The development flow feels really fluent and getting screens implemented is fast, but the ecosystem still feels a bit immature. If your project scope doesn't include many platform plugins, you could probably get away with doing it in Flutter, but you start getting the beta feel pretty fast once you dive in.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Does your application waste memory?]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/04/01/ApplicationMemoryPressure</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/04/01/ApplicationMemoryPressure</guid>
            <pubDate>Mon, 01 Apr 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Have you ever thought about what is inside your application causing high RAM usage in Task Manager? Take a moment and check out your program memory usage right now. Does it look good/healthy/fit/expected/normal? Do you have an idea about what the top three application types consuming the most RAM are? What is your guess how much megabytes they conquer?</p>
<h3>How is memory expected to be used?</h3>
<p>Memory is consumed by objects that carry data required for an application to do its job.</p>
<p>Does it mean the more data is there, the more memory is inevitably needed?</p>
<p>Is there anything that can be done to break the loop?</p>
<p>Let's start with finding top memory consumers and figure out if anything can be done there.</p>
<h2>How to get per type statistics?</h2>
<p><a href="https://github.com/mitikov/KeepSitecoreSimple/wiki/Myth-busting-Memory-Dumps">A memory snapshot of the live process</a> is a gold mine of information and can be collected in 4 clicks:</p>
<ol>
<li>Task Manager</li>
<li>Details</li>
<li>Right click on target process (w3wp in case of IIS-hosted app)</li>
<li>Create dump file</li>
</ol>
<p>Importing the dump file into <a href="https://www.jetbrains.com/dotmemory">dotMemory</a> highlights the largest size taken by <a href="https://en.wikipedia.org/wiki/Immutable_object">immutable</a> <a href="https://docs.microsoft.com/en-us/dotnet/api/system.string?view=netframework-4.7.2">string</a> and <em>Sitecore.Data.ID</em> (reference version of <a href="https://docs.microsoft.com/en-us/dotnet/api/system.guid?view=netframework-4.7.2">Guid</a>) types:</p>
<p>![heap-stats](/assets/img/articles/2019-04-01-ApplicationMemoryPressure/dotMemory-heap-stats.webp</p>
<p>Sitecore reads data from the database and caches it inside the process to avoid further expensive database calls. In other words - it seem to be expected that text data and its IDs would be in top 3.</p>
<p>How does that align with your prediction? Did you expect <strong>strings</strong> to consume half of the total heap size?</p>
<h3>Reduce memory pressure</h3>
<p>Despite some Sitecore fields (like <strong>created by</strong> <code>sitecore\admin</code>) having repeatable values, each read from the database will turn text into a new string object instance and cached by the application shortly after.</p>
<p>.NET already supports the concept of <a href="https://docs.microsoft.com/en-us/dotnet/api/system.string.intern?view=netframework-4.7.2">re-using immutable object instances</a> by using an implementation of the <a href="https://en.wikipedia.org/wiki/Object_pool_pattern">Object pool pattern</a>.</p>
<p>Instead of caching the value straightaway, Sitecore can intern it first (either by grabbing a reference to the interned object, or by adding it to the intern pool) and thereby lowering the number of objects carrying duplicate data.</p>
<p>All there is left for a developer is to locate repeatable data in fields, which can be done by using this simple SQL script:</p>
<pre><code class="hljs language-sql"><span class="hljs-keyword">WITH</span> DuplicatedFieldValues <span class="hljs-keyword">AS</span>  (
<span class="hljs-keyword">SELECT</span>
	v.FieldId,
	FieldDefinitionRow.[Name],
	<span class="hljs-keyword">CONVERT</span>(NVARCHAR(<span class="hljs-number">250</span>),v.[<span class="hljs-keyword">Value</span>]) <span class="hljs-keyword">AS</span> [Field <span class="hljs-keyword">Value</span>],
	<span class="hljs-built_in">COUNT</span>(<span class="hljs-number">1</span>) <span class="hljs-keyword">AS</span> [Hits]
<span class="hljs-keyword">FROM</span>
	[VersionedFields] v
<span class="hljs-keyword">JOIN</span>
	[Items] FieldDefinitionRow <span class="hljs-keyword">ON</span> FieldDefinitionRow.ID <span class="hljs-operator">=</span> v.fieldID
<span class="hljs-keyword">WHERE</span>
	v.FieldId <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">IN</span>
	(
		<span class="hljs-comment">/* Fields already interned OOB by Sitecore.Interning.config */</span>
		<span class="hljs-string">'BADD9CF9-53E0-4D0C-BCC0-2D784C282F6A'</span> <span class="hljs-comment">/* updated by */</span>,
		<span class="hljs-string">'5DD74568-4D4B-44C1-B513-0AF5F4CDA34F'</span> <span class="hljs-comment">/* created by */</span>,
		<span class="hljs-string">'52807595-0F8F-4B20-8D2A-CB71D28C6103'</span> <span class="hljs-comment">/* owner */</span>,
		<span class="hljs-string">'3E431DE1-525E-47A3-B6B0-1CCBEC3A8C98'</span> <span class="hljs-comment">/* workflow state */</span>
	)
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span>
	FieldId, [Name], <span class="hljs-keyword">CONVERT</span>(NVARCHAR(<span class="hljs-number">250</span>),[<span class="hljs-keyword">Value</span>])
<span class="hljs-keyword">HAVING</span>
	<span class="hljs-built_in">COUNT</span>(<span class="hljs-operator">*</span>) <span class="hljs-operator">></span> <span class="hljs-number">500</span> <span class="hljs-comment">/* How many same field values must be met to be shown */</span>)

<span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">FROM</span> DuplicatedFieldValues
<span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> [Hits] <span class="hljs-keyword">DESC</span>

<span class="hljs-keyword">SELECT</span>
	REPLACE(CONCAT(<span class="hljs-string">'&#x3C;'</span>, [Name], <span class="hljs-string">'>'</span>), <span class="hljs-string">' '</span>,<span class="hljs-string">'_'</span>) <span class="hljs-keyword">AS</span> [BeginTag],
	CONCAT(<span class="hljs-string">'{'</span>,FieldId,<span class="hljs-string">'}'</span>) <span class="hljs-keyword">AS</span> [FieldId],
	REPLACE(CONCAT(<span class="hljs-string">'&#x3C;/'</span>, [Name], <span class="hljs-string">'>'</span>), <span class="hljs-string">' '</span>,<span class="hljs-string">'_'</span>) <span class="hljs-keyword">AS</span> [EndTag],
	<span class="hljs-built_in">SUM</span>(Hits) <span class="hljs-keyword">AS</span> [Duplicates],
	<span class="hljs-built_in">COUNT</span>(<span class="hljs-number">1</span>) <span class="hljs-keyword">AS</span> [<span class="hljs-keyword">Unique</span> <span class="hljs-keyword">Values</span>]
<span class="hljs-keyword">FROM</span>
	DuplicatedFieldValues
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span>
	[FieldId], [Name]
<span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span>
	[Duplicates] <span class="hljs-keyword">DESC</span>
</code></pre>
<p>Located fieldIDs are to be added into the Sitecore.Interning.config, <strong>fieldIdsToIntern</strong> section to let Sitecore apply interning logic for them.</p>
<h3>Verify it works phase</h3>
<p>The real gain is to be measured with ON/OFF interning:</p>
<ol>
<li>Kill existing w3wp.exe process (since applications do not like to give away conquered memory)</li>
<li>Start Sitecore application with unlimited cache sizes</li>
<li>Capture a full memory snapshot after a big pile of content has been loaded and full garbage collection has occurred. Some sample code:</li>
</ol>
<pre><code class="hljs language-csharp"><span class="hljs-keyword">var</span> db = Sitecore.Configuration.Factory.GetDatabase(<span class="hljs-string">"master"</span>);
<span class="hljs-keyword">using</span> (<span class="hljs-keyword">new</span> SecurityDisabler())
{
	<span class="hljs-keyword">var</span> root = db.GetItem(<span class="hljs-keyword">new</span> ID(<span class="hljs-string">"{110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9}"</span>));
	<span class="hljs-keyword">var</span> descendants = root.Axes.GetDescendants();
	MainUtil.Nop(descendants);
}
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, <span class="hljs-literal">true</span>, <span class="hljs-literal">true</span>);
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, <span class="hljs-literal">true</span>, <span class="hljs-literal">true</span>);
</code></pre>
<p>Repeat the steps with enabled/disabled interning (<strong>setting name="Interning.Enabled"</strong> in <strong>Website\App_Config\Include\Sitecore.Interning.config</strong>) to get the base lines.</p>
<p>Let's make one more prediction before getting dotMemory into the game - what is the impact of configuring interning?</p>
<h2>Results</h2>
<p>The local results shows that 30% less objects stays in the heap and one fourth of the RAM usage has been removed.</p>
<p>In simple words - the application can consume less than 6 GB instead of more than 8 GB as previously:</p>
<p><img src="/assets/img/articles/2019-04-01-ApplicationMemoryPressure/proof_less_memory_consumed_in_reality.webp" alt="interning-results"></p>
<p>Did you predict that big performance win?</p>
<h2>Lesson learnt</h2>
<p><a href="https://en.wikipedia.org/wiki/Object_pool_pattern">Object pool</a> and <a href="https://en.wikipedia.org/wiki/Immutable_object">Immutable object</a> design patterns allow noticeably reducing the application memory pressure without altering any functionality!</p>
<p>The cost (extra operation to pool object) is much lower than gain - not only tons of saved memory, but also a huge boost in comparing <strong>pooled</strong> objects - fast by-reference equality in certain cases instead of by-value comparing.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A dive into the Observer Pattern in iOS applications]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/03/19/ObserverPatterniOS</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/03/19/ObserverPatterniOS</guid>
            <pubDate>Tue, 19 Mar 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Often when developing iOS applications with more complex requirements we may encounter the need to react to, and perform UI updates on, not so easily accessible UI components. Another example could be that we must update an object outside our currently focused scene, perhaps created by a scene a few levels back in our view hierarchy.</p>
<p>A common way to solve this is by adapting our code to a reactive approach and using a reactive framework, such as <a href="https://github.com/ReactiveX/RxSwift">RxSwift</a>.
But what happens when we don't need everything that these frameworks provide to us, or we just aren't comfortable with using a reactive framework?</p>
<p>A solution to this can be created by implementing the <em>Observer Pattern</em>.</p>
<h3>About the Observer Pattern</h3>
<p>Observer is a behavioral design pattern. It specifies communication between objects: observable and observers. An observable is an object which notifies observers about the changes in its state. For example, in a shopping app, we need to always be notified when the user adds or removes a product from the cart. The user action in this case will be what changes the state of the upcoming purchase, which again causes the cart to be notified.</p>
<p>Today we will be going over how to implement this pattern in a simple shopping iOS application.</p>
<h2>Let's Get Started</h2>
<p>Before we get started with the actual code implementation of this pattern, lets see what our requirements are.</p>
<p>We will need to be able to:</p>
<ul>
<li>Register an Observer</li>
<li>Notify/Update the Observer when an event happens</li>
<li>Remove the Observer and discard it when we don't need it anymore</li>
</ul>
<p>As well I have created a demo project that can be downloaded <a href="https://github.com/nodes-ios/ObserverPattern-Demo/tree/start-project">here</a>. This will allow us to focus strictly on implementing the patter and the functionality related to it.</p>
<h3>Creating a Disposable</h3>
<p>Because we want our Observer to auto deinitialize when we remove any scene in our app holding a reference to it, we are going to start by creating a <code>Disposable</code>. In your project go ahead and create a <code>Disposable.swift</code> file and add the following code:</p>
<pre><code class="hljs language-swift">
<span class="hljs-keyword">import</span> Foundation

<span class="hljs-keyword">class</span> <span class="hljs-title class_">Disposable</span> {

    <span class="hljs-comment">// callback called automatically on deinit</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> dispose: () -> ()

    <span class="hljs-comment">// on init create a callback that we will use to remove the observer when the disposable will deinitialize</span>
    <span class="hljs-keyword">init</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">dispose</span>: <span class="hljs-keyword">@escaping</span> () -> ()) {
        <span class="hljs-keyword">self</span>.dispose <span class="hljs-operator">=</span> dispose
    }

    <span class="hljs-keyword">deinit</span> {
        dispose()
    }

    <span class="hljs-comment">// call add when having multiple Observables in the same class</span>
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">add</span>(<span class="hljs-params">to</span> <span class="hljs-params">disposable</span>: <span class="hljs-keyword">inout</span> [<span class="hljs-type">Disposable</span>]) {
        disposable.append(<span class="hljs-keyword">self</span>)
    }
}

</code></pre>
<p>We will be using the <code>Disposable</code> to return as a reference when observing an event and as well to remove the observer when the disposable will be deinitialised.</p>
<h3>Creating the Oberver</h3>
<p>For our app to be able to observe changes we need a way to register an observer and to do so we need to create it first. So let's get started.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Observer</span>&#x3C;<span class="hljs-title class_">ObserverValue</span>> {

    <span class="hljs-comment">// the associated value for the observer</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> value: <span class="hljs-type">ObserverValue</span>

    <span class="hljs-keyword">init</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">value</span>: <span class="hljs-type">ObserverValue</span>) {
        <span class="hljs-keyword">self</span>.value <span class="hljs-operator">=</span> value
    }
}
</code></pre>
<p>In the above snippet we have layed out the base for our observer. Here we are declaring the generic class <code>Observer</code> and create its initialiser that takes a generic value.</p>
<p>Now that we can create an observer, let's see how we can subsribe to a change event.</p>
<pre><code class="hljs language-swift">
<span class="hljs-comment">// typealias for our observer id</span>
<span class="hljs-keyword">typealias</span> <span class="hljs-type">ObserverId</span> <span class="hljs-operator">=</span> <span class="hljs-type">UInt</span>

<span class="hljs-comment">// typealias for our observe callback</span>
<span class="hljs-keyword">typealias</span> <span class="hljs-type">ObservableCallback</span> <span class="hljs-operator">=</span> ((<span class="hljs-type">ObserverValue</span>) -> <span class="hljs-type">Void</span>)

<span class="hljs-comment">// lock will prevent the simultanious registration of observers from multiple threads.</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> lock <span class="hljs-operator">=</span> <span class="hljs-type">NSLock</span>()

<span class="hljs-comment">// an dictionary of all the registered observers</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> observers: [<span class="hljs-type">ObserverId</span>: <span class="hljs-type">ObservableCallback</span>] <span class="hljs-operator">=</span> [:]

<span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> sequentialId: <span class="hljs-type">ObserverId</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>

<span class="hljs-keyword">func</span> <span class="hljs-title function_">observe</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">values</span>: <span class="hljs-keyword">@escaping</span> <span class="hljs-type">ObservableCallback</span>) -> <span class="hljs-type">Disposable</span> {
    <span class="hljs-comment">// aquire a lock</span>
    lock.lock()
    <span class="hljs-keyword">defer</span> {
        <span class="hljs-comment">// increase sequentialId to guarantee an unique identifiers for our observers</span>
        sequentialId <span class="hljs-operator">+=</span> <span class="hljs-number">1</span>
        <span class="hljs-comment">// relinquishes a previously acquired lock.</span>
        lock.unlock()
    }

    <span class="hljs-comment">// add the observer to our observers dictionary and assign it the current value</span>
    <span class="hljs-keyword">let</span> id <span class="hljs-operator">=</span> sequentialId
    observers[id] <span class="hljs-operator">=</span> values
    values(value)

    <span class="hljs-keyword">let</span> disposable <span class="hljs-operator">=</span> <span class="hljs-type">Disposable</span> { [<span class="hljs-keyword">weak</span> <span class="hljs-keyword">self</span>] <span class="hljs-keyword">in</span>
        <span class="hljs-comment">// remove observer on deinit</span>
        <span class="hljs-keyword">self</span><span class="hljs-operator">?</span>.observers[id] <span class="hljs-operator">=</span> <span class="hljs-literal">nil</span>
    }

    <span class="hljs-keyword">return</span> id
}

</code></pre>
<p>Above we have created <code>func observe(_ values: @escaping ObservableCallback) -> Disposable</code>. This function will allow us to safely register an observer, that in the following step we will notify when an update to our value happens. The following <code>func update(_ value: ObserverValue)</code> we will call to update the current value of our observer. When an update events happens we then will notify all our registered observers about the update with the help of <code>func notify()</code>.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">update</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">value</span>: <span class="hljs-type">ObserverValue</span>) {
    <span class="hljs-keyword">self</span>.value <span class="hljs-operator">=</span> value
    notify()
}

<span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">notify</span>() {
    <span class="hljs-keyword">for</span> observer <span class="hljs-keyword">in</span> observers.values {
        observer(value)
    }
}
</code></pre>
<p>Once our observer is created we can now use it to subscribe to an update event. In our app we will use this to ease the way we add the items selected by the user to the cart.</p>
<h3>Add the Observer to Our Project</h3>
<p>Now in our project we can start including the observer. We are going to create an observer for the user selected products in our scenes root, to be able to pass it around via initialisers to our <code>UIViewControllers</code>.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> productsObserver: <span class="hljs-type">Observer</span>&#x3C;[<span class="hljs-type">Product</span>]> <span class="hljs-operator">=</span> <span class="hljs-type">Observer</span>([])
</code></pre>
<p>With the observer created we now need a way to pass it to our <code>UIViewControllers</code>, so go ahead an modify the <code>class func instantiate(productsObserver: Observer&#x3C;[Product]>)</code> in our <code>UIViewControllers</code> to include the <code>productsObserver</code> as a parameter, then declare the <code>var productsObserver: Observer&#x3C;[Product]>!</code> in the <code>UIViewController</code> as well. Your instantiate function for <code>ProductCategoriesViewController</code> should look similar to this now.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> productsObserver: <span class="hljs-type">Observer</span>&#x3C;[<span class="hljs-type">Product</span>]>!

<span class="hljs-comment">// MARK: - Init</span>

<span class="hljs-keyword">class</span> <span class="hljs-title class_">func</span> <span class="hljs-title class_">instantiate</span>(<span class="hljs-title class_">productsObserver</span>: <span class="hljs-title class_">Observer</span>&#x3C;[<span class="hljs-title class_">Product</span>]>) -> <span class="hljs-title class_">ProductCategoriesViewController</span> {
    <span class="hljs-keyword">let</span> name <span class="hljs-operator">=</span> <span class="hljs-string">"<span class="hljs-subst">\(ProductCategoriesViewController.<span class="hljs-keyword">self</span>)</span>"</span>
    <span class="hljs-keyword">let</span> storyboard <span class="hljs-operator">=</span> <span class="hljs-type">UIStoryboard</span>(name: name, bundle: <span class="hljs-literal">nil</span>)
    <span class="hljs-keyword">let</span> vc <span class="hljs-operator">=</span> storyboard.instantiateViewController(withIdentifier: name) <span class="hljs-keyword">as!</span> <span class="hljs-type">ProductCategoriesViewController</span>
    vc.productsObserver <span class="hljs-operator">=</span> productsObserver
    <span class="hljs-keyword">return</span> vc
}
</code></pre>
<h3>Subscribe to an Event</h3>
<p>For our cart to update in real time, we will need to subscribe to a change event on our observer. In order to do so we need to call the <code>observe</code> function on our observer in our <code>SmallCartViewController</code>.</p>
<p>In the <code>viewDidLoad</code> go ahead and add the following snippet:</p>
<pre><code class="hljs language-swift">disposable <span class="hljs-operator">=</span> productsObserver.observe { (products) <span class="hljs-keyword">in</span>
    <span class="hljs-keyword">self</span>.productsLabel.text <span class="hljs-operator">=</span> <span class="hljs-string">"<span class="hljs-subst">\(products.count)</span> items"</span>
    <span class="hljs-keyword">var</span> price <span class="hljs-operator">=</span> <span class="hljs-number">0.0</span>
    products.forEach({ price <span class="hljs-operator">+=</span> <span class="hljs-variable">$0</span>.price })
    <span class="hljs-keyword">self</span>.priceLabel.text <span class="hljs-operator">=</span> <span class="hljs-type">Current</span>.priceFormatter.string(from: price <span class="hljs-keyword">as</span> <span class="hljs-type">NSNumber</span>)<span class="hljs-operator">!</span>
}
</code></pre>
<p>The observe function will notify us via it's callback whenever a change in the associated value of our observer happens. As well the function returns a value of type <code>Int</code> that we will use to hold a reference to our observable.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> disposable: <span class="hljs-type">Disposable</span>!
</code></pre>
<h3>Update Observers Associated Value</h3>
<p>Now that we are listening for events and we have the observer declared in our app, we need a way to add the user selected products to the current value of our observer. In order to do so we would need to get the current array of products and append a new product to it. Currently we don't have a way to get the current value of our observer, but by adding the following function in our <code>Observer</code> class we can do so:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">getValue</span>() -> <span class="hljs-type">ObserverValue</span> {
    <span class="hljs-keyword">return</span> value
}
</code></pre>
<p>The <code>func getValue() -> ObserverValue</code> will return the current associated value with our observer.</p>
<p>Now in our <code>ProductsViewController</code> in the <code>didSelectRowAt</code> delegate function for our <code>UITableView</code> we can update the associated value:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">tableView</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">tableView</span>: <span class="hljs-type">UITableView</span>, <span class="hljs-params">didSelectRowAt</span> <span class="hljs-params">indexPath</span>: <span class="hljs-type">IndexPath</span>) {
    tableView.deselectRow(at: indexPath, animated: <span class="hljs-literal">false</span>)
    <span class="hljs-comment">// get the selected product</span>
    <span class="hljs-keyword">let</span> product <span class="hljs-operator">=</span> products[indexPath.row]
    <span class="hljs-comment">// get the current products in cart</span>
    <span class="hljs-keyword">var</span> productsInCart <span class="hljs-operator">=</span> productsObserver.getValue() <span class="hljs-keyword">as</span> [<span class="hljs-type">Product</span>]
    <span class="hljs-comment">// add new selected product to the current products</span>
    productsInCart.append(product)
    <span class="hljs-comment">// update the associated value</span>
    productsObserver.update(productsInCart)
}
</code></pre>
<p>By calling <code>productsObserver.update(productsInCart)</code> we will automatically update the associated value and notify all the registered observers.</p>
<p>Go ahead an run your code now. Navigate to our products and select one or more of them, you will see how the cart will update with the number of selected products and the current total price for the products.</p>
<h2>Final Notes</h2>
<p>You have now made it all the way to the end of this post and hopefully you will have a clear picture of how to implement the Observer pattern.</p>
<p>Don't forget to download our final project and compare our results. :)</p>
<p><a href="https://github.com/nodes-ios/ObserverPattern-Demo/tree/develop">Final Project</a></p>
<p>Hope to see you next time!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Bugsnag for Vapor 3]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/01/31/Bugsnag</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/01/31/Bugsnag</guid>
            <pubDate>Thu, 31 Jan 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>At Nodes we have been working hard on a Vapor 3 provider for <a href="https://www.bugsnag.com">Bugsnag</a>. Bugsnag is a reporting tool that we use extensively to help catch and fix bugs.</p>
<p>In the new version of the library, we've been able to remove a lot of dead code, simplify the API and add support for breadcrumbs.</p>
<h2>Migration to Vapor 3</h2>
<p>The migration from Vapor 2 to Vapor 3 was a relatively easy one for Bugsnag. The major changes were the use of <code>Codable</code> and the introduction of <code>Future</code>s.</p>
<p>One of the double-edged-swords of Vapor 3 is the asynchronous design. The asynchronous nature of the framework makes stack traces virtually useless. Sadly, we had to remove support for <a href="https://github.com/nodes-vapor/stack">Stack</a> and stack traces.</p>
<p>Registration has seen an update as well. It now uses the <code>Service</code> system.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> reporter <span class="hljs-operator">=</span> <span class="hljs-type">BugsnagReporter</span>(
    apiKey: <span class="hljs-string">"&#x3C;YOUR BUGSNAG API KEY>"</span>,
    releaseStage: environment.name,
    shouldReport: environment.name <span class="hljs-operator">!=</span> <span class="hljs-string">"local"</span>
    debug: <span class="hljs-literal">false</span>
)
</code></pre>
<p>With a newly simplified API, we introduced support for severities.</p>
<pre><code class="hljs language-swift">reporter.info(<span class="hljs-operator">...</span>)
reporter.warning(<span class="hljs-operator">...</span>)
reporter.error(<span class="hljs-operator">...</span>)
</code></pre>
<p>After the migration to Vapor 3, we updated the Bugsnag payload version from 2 to 4. This allows us to support newer features as well as silence warnings about deprecated APIs.</p>
<h2>Breadcrumbs</h2>
<p>After many feature requests, we have now integrated breadcrumbs into the latest version. Breadcrumb is a feature that helps ease the debugging process by enabling a user to attach data to the life-cycle of a request. To drop a breadcrumb just use the convenience function on <code>Request</code>.</p>
<pre><code class="hljs language-swift"> req.breadcrumb(
     name: <span class="hljs-string">"Something happened!"</span>,
     type: .manual,
     metadata: [<span class="hljs-string">"foo"</span>: <span class="hljs-string">"bar"</span>]
 )
</code></pre>
<p>If you are curious about Bugsnag and would like to know more or try it out, be sure to checkout the <a href="https://github.com/nodes-vapor/bugsnag">repo on Github</a>.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/cckf4TsHAuw">Andrew Neel</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A Guide to Google Home Integrations]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2019/01/31/Google-Home</link>
            <guid>https://engineering.monstar-lab.com/en/post/2019/01/31/Google-Home</guid>
            <pubDate>Thu, 31 Jan 2019 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Within the trend of IoT, Google Home is amongst the most popular. Thankfully, Google has made the development of actions quite approachable. To get an overview of the process, let's create a weather integration together.</p>
<h2>Actions on Google</h2>
<p>Creating your Google Home integration starts with Actions on Google (AoG). Quoting Google:</p>
<blockquote>
<p>Actions on Google is the platform for developers to extend the Google Assistant. Join this emerging ecosystem by developing actions to engage users on Google Home, Pixel, and many other surfaces where the Google Assistant will be available.</p>
</blockquote>
<p>Now, it's time to create your action. Under the <em>Build</em> section, click <em>ADD ACTION</em> and finally select <em>Custom intent</em>. After creating the action, Google will redirect you to their <a href="http://dialogflow.com">Dialogflow</a> service.</p>
<h2>Dialogflow</h2>
<p>Dialogflow is Google's intuitive natural language processing (NLP) tool. Here is where you train the AI to recognize intents. This is by far the smoothest part of creating a Google Home integration. Create an intent and just type in a few example phrases to train the AI.</p>
<ul>
<li>How's the weather today?</li>
<li>What's the weather?</li>
<li>How's the weather in <code>Copenhagen</code>?</li>
</ul>
<p>After configuring the training phrases, make sure to check the box <em>Enable webhook call for this intent</em>. Doing so allows you to add fulfillments programmatically.</p>
<h3>Fulfillment</h3>
<p>A fulfillment is a response to an intent. Programmatic fulfillments allow you to do many things: present a card with information, prompt a user for more information or show an error. Fulfillments use the webhook REST API and can be served through cloud functions or self hosting.</p>
<h4>Cloud Functions</h4>
<p>In the <em>Fulfillment</em> tab on Dialogflow you have the option to edit a fulfillment and deploy it to Google Cloud Functions all within the inline editor. If you're short on time or your project isn't that large, this is by far the simpler option of the two.</p>
<h4>Self-hosting</h4>
<p>If you're working on a larger project or cloud functions aren't your thing, you can host your server and configure Dialogflow to fulfill all requests using your server. As most major programming languages have a fulfillment library, you have a much wider choice in what to develop your fulfillment in.</p>
<h2>Testing</h2>
<p>After developing and deploying your fulfillment, it's time to test. On the Dialogflow website, go to the <em>Integrations</em> section and then click on <em>INTEGRATION SETTINGS</em>. At the bottom of the dialog, you should see the button <em>TEST</em>. If clicking the button gives you the error <code>there was an error completing your request</code> make sure Google selected the correct user in the upper-right corner.</p>
<p>You should now be in the <code>Simulator</code> where you can test all of your actions and intents. As a bonus, all supported Google devices linked to the same email will be able to test your actions.</p>
<p>Try it out!</p>
<blockquote>
<p>Ok Google, ask My Test App about the weather</p>
</blockquote>
<h3>Conclusion</h3>
<p>Creating a Google Home integration and fulfillment is an intuitive and relatively easy process. There is some friction with the integration between the different Google services, but overall everything works nicely.</p>
<h4>Key takeaways</h4>
<ul>
<li>Setting up a Google Action is very easy</li>
<li>Intents have to be trained manually for every single language your application supports</li>
<li>Localization of fulfillments will take a bit of effort</li>
<li>NodesJS and Go seem to be the most supported languages</li>
<li>You can only address your app explicitly. Ex. “Okay Google, ask … about the weather”</li>
</ul>
<p><em>Article Photo by <a href="https://unsplash.com/photos/aW9_Lo2AiGg">Dan Farrell</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Swift Alps]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2018/12/05/swift-alps-2018</link>
            <guid>https://engineering.monstar-lab.com/en/post/2018/12/05/swift-alps-2018</guid>
            <pubDate>Wed, 05 Dec 2018 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><a href="https://theswiftalps.com">Swift Alps</a> - as the name might imply - is a conference revolving around anything Swift related. As the name might also imply, the conference takes place in the Swiss Alps, in Crans-Montana more specifically.</p>
<p>What sets this conference apart from many of the other conferences out there is the format.</p>
<p>Instead of the "traditional" presenter/audience format, Swift Alps is centered around workshops where a mentor runs the workshop and a limited number of participants will pair up and work together learning new material. Each workshop takes 2 hours and you can attend a max number of 3 workshops each day.</p>
<p>This year the topics range from Accessibility to ARKit and reverse engineering to name a few.</p>
<p>2018 is the third year the conference takes place and first year with attendance from Nodes.</p>
<p><a href="https://twitter.com/chriscombs76">Chris</a> and <a href="https://twitter.com/pbodsk">Peter</a> attended this years conference on the 29th and 30th of November, and this is a debrief from our Swift adventures in the Swiss Alps.</p>
<h2>Thursday</h2>
<p>After waking up to this
<img src="/assets/img/articles/2018-12-05-swift-alps-2018/goodmorning.webp" alt="Good morning indeed"></p>
<p>It was time to head to the conference to get registered.</p>
<p>After registration, caffeine level adjustments, getting access to the wifi and a brief introduction of their workshop from each of the mentors, it was time to get the show rolling.</p>
<p>The workshops lined up for Thursday was:</p>
<ul>
<li><em>New generation networking with Swift-NIO</em> by <a href="https://twitter.com/fpillet">Florent Pillet</a></li>
<li><em>Level Up Your Debugging Skills</em> by <a href="https://twitter.com/_Caro_N">Carola Nitz</a></li>
<li><em>An "Intents" Guide to Custom Siri Shortcuts</em> by <a href="https://twitter.com/a2">Alexsander Akers</a></li>
<li><em>Reverse Engineering</em> by <a href="https://twitter.com/_inside">Guilherme Rambo</a></li>
</ul>
<p>and the sad news was that you could only pick three out of these four interesting topics.</p>
<h2>New generation networking with Swift-NIO</h2>
<p>I attended the workshop focusing on <a href="https://github.com/apple/swift-nio">Swift NIO</a> (introduced at this years <a href="https://www.tryswift.co/events/2018/tokyo/en/">try! Swift Tokyo</a>) and the Network.framework (<a href="https://developer.apple.com/videos/play/wwdc2018/715/">introduced by Apple at this years WWDC</a>).</p>
<p>The goal was to continue the work started by Florent on either an iOS chat client using Network.framework, or a macOS chat server using Swift NIO, and my session buddy and I chose to start out with the client app.</p>
<p>In my normal, day to day work, networking work is something you set up at the start of a project - typically by copying some code you were satisfied with in a previous project, and then its just there, calling services, translating the returned JSON into model objects that you can use for your "real" work.</p>
<p>Therefore it was really fun and challenging to try and wrap your head around implementing a chat client from scratch. Starting out with making a connection (deciding whether to use TCP or UDP), actually connecting and watch the connection go through different stages before - hopefully - being established, and finally being able to send data over the network.</p>
<p>Apples new <a href="https://developer.apple.com/documentation/network">Network.framework</a> was pleasant to work with, it felt very swifty with various handler closures and enums with associated values.</p>
<p>An interesting feature in Network.framework is the <a href="https://developer.apple.com/documentation/network/nwpath">NWPath</a> and <a href="https://developer.apple.com/documentation/network/nwpathmonitor">NWPathMonitor</a> which you can use to monitor the connection and react if it changes...or completely goes away. Super interesting when writing a network monitor for instance.</p>
<p>All in all a very good workshop, good work Florent</p>
<h2>An "Intents" Guide to Custom Siri Shortcuts</h2>
<p>Siri shortcuts were one of the bigger announcements to come out of WWDC 2018. There's a lot of talk about powerful workflows you can add to your app to make your user experience better for both casual and power users alike. In Aleksander's workshop, we learned about how Siri shortcuts work, or specifically how the Intents framework works, and then implemented them in existing apps we had. We soon found out two important facts about developing Siri shortcuts.</p>
<ol>
<li>
<p>Creating a new shortcut is super simple. All you need to do is create a new intent shortcut, provide it some info about what data it is expecting, what it should listen for, and what kind of action it is (voice call, ordering food, playing music, etc). Build it and run and you should be able to get to communicate with your app right away.</p>
</li>
<li>
<p>Structuring your app to perform useful tasks with the data input from Siri is a huge complex problem. Since the intent is in its own extension, it does not have access to the rest of your codebase in the traditional sense. You need to modularize your code in a way that splits logic into separate services, which can then be imported into the intent extension. If your app isn't already set up like this, it can be a huge refactor effort.</p>
</li>
</ol>
<p>We spent maybe 20 minutes setting up the intent and getting Siri to interact with our app. The next hour or so was spent refactoring the existing app so that the right code could be accessed by the Siri intent. All in all, it was a great exercise and shed light on what to expect when developing a Siri shortcut.</p>
<h2>Level Up Your Debugging Skills</h2>
<p>I admit it, I like to debug, I actually do! It gives me a good idea about the flow of a program, I can see values of variables in a loop for instance and it is a great tool in my daily work.</p>
<p>And since we're doing confessions now, I'll also admit that my debugging work in Xcode normally resolves to:</p>
<ul>
<li>place a breakpoint</li>
<li><code>po interesting object</code></li>
<li>step</li>
<li>continue</li>
</ul>
<p>and that is about it. Sure, I had my mind blown by the <a href="https://developer.apple.com/videos/play/wwdc2018/412/">Advanced Debugging</a>
video from WWDC 2018 and that also led to one conditional breakpoint in the days after, but that was about it.</p>
<p>So clearly there was potential for levelling up my debugging skills!</p>
<p>Carola had made kind of a treasure hunt which introduced us to various features in lldb directly from the terminal. We learned to have the debugger wait for a process to be launched, how to set a breakpoint directly in the terminal, how to use the repl to execute new code while your "real" code was paused and the final trick, how to update the UI of your app with help from the debugger.</p>
<p>By now lldb veterans may be rolling their eyes over this idiot discovering blatantly obvious things, but it was really interesting for me to be "forced" into using those features. Now I just need to keep integrating the more advanced lldb features into my daily workflow.</p>
<p>Thank you to Carola for a very fine workshop.</p>
<h2>Reverse Engineering</h2>
<p>Final course on the menu Thursday was a real treat. An introduction to fooling around with the private frameworks in the iOS simulator. The mentor was no other than Guilherme Rambo, amongst many other things writer, at <a href="https://9to5mac.com">9to5mac</a> who <a href="https://9to5mac.com/author/guirambobr/">describes him as</a></p>
<blockquote>
<p>a Mac and iOS developer based in Brazil. Known for discovering Apple’s secrets and analyzing leaks</p>
</blockquote>
<p>By using the tool <a href="http://stevenygard.com/projects/class-dump/">class-dump</a> we were able to find interesting ViewControllers in Apple's private frameworks and then try to instantiate them from our own apps.</p>
<p>The interesting thing was, that for this to really succeed, we had to use good old Objective C, since this language - beeing more dynamic in its nature - allows you to more easily load private frameworks dynamically instead of using the linker.</p>
<p>Writing Objective C again was kind of depressing, not because of the language (I really liked Objective C back then), but because you became aware of how rusty you were in that language. Four years ago I was writing Objective C like there was no tomorrow, and now I was fighting the compiler over missing semicolons...how the times have changed.</p>
<p>We managed to get an example project running that could load the PencilView you normally use in Apples Notes app, it wasn't pretty but really fun.</p>
<p>Later on, I managed to get an example working with the FUFlightViewController, and was then able to track our flight back home via Paris</p>
<p><img src="/assets/img/articles/2018-12-05-swift-alps-2018/flight.webp" alt="take me home"></p>
<p>This session was great fun and it was interesting to see how you are able to get access to some of the secret frameworks that lies hidden in your simulator.</p>
<h2>Friday</h2>
<p>The schedule for Friday looked as follows:</p>
<ul>
<li><em>Swift Design Patterns</em> by <a href="https://twitter.com/twostraws">Paul Hudson</a></li>
<li><em>Better Developer Tooling by writing your own Mac Apps with Cocoa Bindings</em> by <a href="https://twitter.com/terhechte">Benedikt Terhechte</a></li>
<li><em>Accessibility on iOS</em> by <a href="https://twitter.com/_ms_monika">Monika Mścichowska</a></li>
<li><em>ARKit - from basics to advance</em> by <a href="https://twitter.com/gridnaka">Kateryna Gridina</a></li>
</ul>
<p>Again, hard choices were to be made!</p>
<h2>ARKit - from basics to advance</h2>
<p>First on the agenda for me was an introduction to ARKit. We've already used ARKit on projects in Nodes so this was a really interesting topic for me to look into.</p>
<p>Kate helped us through a list of examples, starting with plane detection, loading a SpriteKit with simple elements scene from a .sks file, composing your own SpriteKit scene of objets and finally adding a "real" 3D model to a SpriteKit scene and have it render properly.</p>
<p><img src="/assets/img/articles/2018-12-05-swift-alps-2018/arapple.webp" alt="ar apple"></p>
<p>ARKit is still a relatively young framework and to me it still has elements of black magic attached to it, so it was nice to have the time and a mentor to help understand what actually goes on when ARKit starts tracking the world through your camera.</p>
<h2>Swift Design Patterns</h2>
<p>Design Patterns, we all know and love them right?</p>
<p>They give us developers a shared vocabulary. And at the same time it allows us to compose sentences containing seemingly normal words which - when spoken - makes <em>absolutely</em> no sense to non-developers</p>
<p>"Yeah well, I think I would go for dependency injection here, but if you wanna do a singleton then sure!"</p>
<p>Paul Hudson's workshop was a series of design patterns examples, which could be applied to an existing app. The patterns was ranged in increasing complexity ranging from MVC to Coordinators (as famed by <a href="http://khanlou.com/2015/01/the-coordinator/">Soroush Khanlou</a>) and you could mix and match as you saw fit.</p>
<p>It was - yet another -interesting workshop and few things beats the joy of trying to wrap your head around a design pattern, applying it to your code base and afterwards be able to delete line after line of no longer needed code.</p>
<h2>Accessibility on iOS</h2>
<p>When most people think of accessibility in app development, they think of.. well they usually don't think about it. To be honest, most of the apps we develop at Nodes have very little effort put into making them accessible for users with disabilities. This workshop was really refreshing and showed another world of user experience.</p>
<p>We began by putting our phones in voice-over mode. This is a feature for sight-impaired users, which reads aloud the various elements on screen. You need to use a lot of obscure gestures to navigate between UI elements and discover actions. Even having your phone in this mode for 5 minutes is enough to make you realize that making this experience nice is a huge benefit.</p>
<p>We then took over a small sample app that was just a list of recipes, where tapping on one would open a detail view. We found that using this app with voice-over enabled was pretty cumbersome, so we applied some nice accessibility features such as element grouping, custom actions, and better accessibility labels.</p>
<p>Apple has been making a huge push for better accessibility lately and I believe accessibility will become a huge focus for app development in the near term future.</p>
<h2>Better Developer Tooling by writing your own Mac Apps with Cocoa Bindings</h2>
<p>Many of us are iOS developers, which also means that we use (and love) a Mac for our daily work every day.</p>
<p>But how many of us has actually tried writing a macOS app?</p>
<p>Benedikt has, and he was here to tell us about the joy of using bindings.</p>
<p><a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaBindings/Concepts/WhatAreBindings.html">Bindings</a> - in broad strokes - enables you to very quickly wire up your model and UI code, thereby reducing the amount of code you have to write to update your model object when ever a user taps a button or the other way around: update your UI when the model updates.</p>
<p>They are an old technology (in development terms at least), powerful but also hard to debug, which is why you maybe shouldn't rely on them too heavily when writing production apps.</p>
<p>But for internal "tooling" apps, bindings is perfect and allows you to ship internal tooling apps quite fast.</p>
<p>We were working our way through a series of examples and was able to make several apps with a lot of functionality and relatively few lines of code.</p>
<p>Bindings are interesting in several ways. You can do a lot with few lines of code, but make one typo and you are left looking at "interesting" error messages, in some cases you don't even have to make the errors yourself, Interface Builder contains errors that you need to know your way around. At the same time you get a sense that this framework has been around for quite some time. The amount of options and possibilities are staggering, but sometimes well hidden and sparsely documented. An elegant weapon...for a more civilized age if you will.</p>
<p>I think many of us left that workshop with a sudden urge to start writing macOS apps instead of the "normal" shell script for internal tool projects, so...mission accomplished Benedikt!</p>
<h2>Game Over</h2>
<p>So there you have it! Two interesting days flew by way too quick and now we're back in a rainy and dark Denmark armed with a ton of new tricks up our sleeves.</p>
<p>It was an absolute joy meeting likeminded developers who loves the iOS platform as much as we do and learn how they attack the challenges we all face from time to time.</p>
<p>The concept of working together with a complete stranger on each workshop is really great, it means that you get to "know" a lot of the other conference goers really quick and makes it easier to talk over coffee afterwards.</p>
<h2>Should I go?</h2>
<p>So, big question, should you go to Swift Alps next year?</p>
<p>As we hope the above post might illustrate, we highly recommend it, both for the high quality content and for the social networking involved.</p>
<p>Swift Alps organizers, mentors and participants, thank you so much for two great days, we hope to see you next year.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Augmented reality is here to disrupt the industry - Insights from Web Summit 2018]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2018/11/23/ar-web-summit</link>
            <guid>https://engineering.monstar-lab.com/en/post/2018/11/23/ar-web-summit</guid>
            <pubDate>Fri, 23 Nov 2018 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>It has been two weeks since <a href="https://websummit.com">Web Summit</a> took place in Lisbon, Portugal and we can still feel the energetic vibes. 3 days full of inspirational speakers, current tech trends, coffee astronauts, robots (have you seen the new social robot Furhat revealed at the summit?), diversity in tech and much more. But out of all, I was personally most excited about the interest that big companies are putting more and more into AR, VR and MR technologies.</p>
<p>Augmented reality still seems to be trivial for some, but when industry leaders like Google, Porsche and AliBaba are heavily investing in this technology, we know it will soon become a commodity in our daily lives. There were many tech talks, startups and partner exhibitors that were putting focus on it and here are some highlights:</p>
<hr>
<h3>Eyes on the road: How AR will change the car industry?</h3>
<p><img src="/assets/img/articles/2018-11-23-ar-web-summit/wayray.webp" alt="Way Ray Image">
<em>Image source: <a href="https://wayray.com/pressarea/presskit/sdk">WayRay Presskit</a></em></p>
<p><a href="https://wayray.com/">WayRay</a>’s Vitaly Ponomarev and <a href="https://www.porsche.com">Porsche</a>’s Christian Knörle joined forces to talk about the opportunities and challenges of AR in the car industry.</p>
<p>WayRay is a Swiss startup that develops holographic AR technologies for connected cars and managed to raise more than 100$M in investments since they launched in 2012. Today the main application is to assist drivers with the navigation, but with the self driving cars of tomorrow, it could extend to much more.</p>
<p>Ponomarev got the idea of the business after an accident he got into because he was distracted by the GPS navigator. This is a great example of how AR can be used for improving our daily lives, not just for entertainment purposes. He also has a very interesting point of view regarding self-driving cars, in a sense that AR can be a powerful tool to build the trust between passengers and car. By projecting what the car is seeing and thinking, the passenger can understand that the car makes the right decisions. The biggest challenge of WayRay now is to industrialise the technology.</p>
<p>Their business plan to develop the hardware and software, together with a <a href="https://wayray.com/sdk">True AR SDK</a> for 3rd party developers which brings endless possibilities. WayRay also had a <a href="https://wayray.com/sdk/challenge">True AR Challenge hackathon</a> this year and it's interesting to look at the projects to get some inspiration and more understanding of this technology.</p>
<p>Knörle compares that adopting AR in our daily lives would be similar to the experience of getting prescription glasses when you have vision problems. Looking forward to that!</p>
<hr>
<h3>Magic Leap: from concept to augmented reality</h3>
<p><img src="/assets/img/articles/2018-11-23-ar-web-summit/magic-leap.webp" alt="Magic Leap Image"></p>
<p>CMO of <a href="https://www.magicleap.com/">Magic Leap</a> Leap Brenda Freeman gave us more insights on one of the most anticipated piece of technology in the AR world. With 5 cameras in the headset, 3 world cameras that mesh the room and 2 cameras for eye tracking, Magic Leap goes a bit further than augmented reality into <a href="https://en.wikipedia.org/wiki/Mixed_reality">mixed reality</a>.</p>
<p>Virtual reality (VR) is a completely immersive virtual experience, augmented reality (AR) overlays virtual objects on the real-work environment, and then there’s mixed reality (MR) which is the merging of real and virtual worlds to produce new environments and visualisations where physical and digital objects co-exist and interact in real time.</p>
<p>Those technologies will have great applications on communication, entertainment, gaming, e-commerce and many more and that is why early adopters are already developing and investing in them. An example of early experimentation is the collaboration between Magic Leap, H&#x26;M and Warpin Media for the new Moschino collection (<a href="https://wwd.com/fashion-news/fashion-scoops/hm-creates-augmented-reality-experience-for-moschino-collab-1202891622/">video</a>).</p>
<p>Right now their focus is to get developers and creators to build and test everyday use cases on the platform and then later they will be able to sell the headset to consumers. They recently had a developers conference to showcase what is possible with Magic Leap and inspire new ideas.</p>
<p>When asked about the future of those new technologies, 5-10 years down the road, Freeman said:</p>
<blockquote>
<p>We believe this will be the next disruptive computing platform in the generation to come.</p>
</blockquote>
<p>Another great take-away from her talk was:</p>
<blockquote>
<p>We talk so much about future forward. Future is now! If you aren’t thinking of how to disrupt your respective industry then you’re behind.</p>
</blockquote>
<h3>Our augmented future: What’s next for AR?</h3>
<p>Ambarish Mitra from <a href="https://www.blippar.com/">Blippar</a>, a leading augmented reality and computer vision company, came to the Creatiff stage to talk about the evolution and future of augmented reality.</p>
<p>The computing interface started with the <strong>touch</strong> experience though keyboards and touchscreens, then it evolved into the area of <strong>natural language</strong> with AI assistants like Siri and Alexa, and now into the final frontier which is <strong>vision</strong> with augmented reality. Mitra also explained that even though AR and AI were positioned as separate industries, they are very deeply linked as AR cannot properly scale until computers can understand reality with the help of artificial intelligence.</p>
<p>When looking at the business opportunities of AR, the CEO of Blippar says that AR can be a very profitable industry, way higher than VR. The <a href="https://www.gartner.com/en/research/methodologies/gartner-hype-cycle">Gartner Hype Cycle</a> for Emerging Technologies 2018, shows <strong>AR will reach mainstream adoption in 5-10 years</strong>:</p>
<p><img src="https://blogs.gartner.com/smarterwithgartner/files/2018/08/PR_490866_5_Trends_in_the_Emerging_Tech_Hype_Cycle_2018_Hype_Cycle.png" alt="Gartner Hype Cycle of Technologies 2018"></p>
<p>Mitra showcased some examples of how AR is already impacting different industries. For example, retailers are using AR for magic mirrors to virtually try on clothing or connecting offline content like a coat in a store with online content like reviews and other recommended items (<a href="https://www.youtube.com/watch?v=M_eCrueFGHY">video</a>). Blippar has a lot more great inspirational videos of AR business ideas on their <a href="https://www.youtube.com/user/blippar1/videos">Youtube channel</a>.</p>
<p>On the second part of his presentation, Mitra gave a few demos of AR applications: a retail environment where you can engage with the products (using the Magic leap headset), a life on Mars experiment, a fitness scanning app and audience coloured butterflies that "come to life".</p>
<hr>
<p>Besides those amazing talks, we’ve seen artists using AR and VR as a new way of storytelling, startups with disruptive ideas and partner’s stepping up their exhibition’s game with AR capabilities. A great example was <a href="https://www.siemens.com">Siemens</a> who used AR to tell employees stories with a new twist.</p>
<p><img src="/assets/img/articles/2018-11-23-ar-web-summit/siemens.webp" alt="Ar Image"></p>
<hr>
<p><strong>Fun fact</strong>: There were <strong>69.304 attendees</strong> which on average walk 13.5 km at Web Summit = 935.604 km. <strong>Together we reached the moon. Twice!</strong></p>
<hr>
<h4>Resources</h4>
<ul>
<li>You can checkout many of the talks on the <a href="https://www.youtube.com/channel/UCJtkHqH4Qof97TSx7BzE5IQ">Web Summit’s Youtube Channel</a></li>
<li>You can also checkout <a href="https://www.instagram.com/stories/highlights/17990784427005924/">Nodes' Instagram story</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[JWT auth in Go]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2018/11/22/JWT-auth-in-Go</link>
            <guid>https://engineering.monstar-lab.com/en/post/2018/11/22/JWT-auth-in-Go</guid>
            <pubDate>Thu, 22 Nov 2018 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Authentication is the most fundamental building block of any application. Whenever we start building a new app, we consider how users authenticate in the very beginning. In the olden days, we used to implement session based authentication and transmitted the data using cookies. We had to store the session data somewhere. That used to make our apps stateful. This also came with a problem. We had to make database calls whenever we wanted to authenticate someone. This causes huge overhead and does not scale well. It can also create a bottleneck in our application.
Using JSON Web Tokens can mitigate this issue. We don’t have to store any session data in our database or anywhere because JWTs can carry information with them in JSON format. Although they can be encrypted, we will be focusing on signed tokens which carry the authenticated user’s information. As you will see later in this article, we don’t have to query the database for the requesting user’s information for making restricted api calls. Signed tokens cannot be tampered or else the signature will not match.</p>
<p>I highly recommend going through the following writing to learn more about the structure of JWTs:</p>
<p><a href="https://jwt.io/introduction/" title="https://jwt.io/introduction/"><strong>JWT.IO - JSON Web Tokens Introduction</strong><br>
JSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties. Learn…</a><a href="https://jwt.io/introduction/"></a></p>
<blockquote>
<p>Go is an open source programming language that makes it easy to build simple, reliable, and efficient software. — <a href="https://golang.org">https://golang.org</a></p>
</blockquote>
<p>If you are new to Go, check out the Golang official blog:</p>
<p><a href="https://blog.golang.org" title="https://blog.golang.org"><strong>The Go Programming Language Blog</strong><br>
Today marks the ninth anniversary of the day we open-sourced our initial sketch of Go. On each anniversary we like to…</a><a href="https://blog.golang.org"></a></p>
<h4>What you will learn in this article</h4>
<ul>
<li>Set up a basic Golang application using the Echo framework.</li>
<li>Generate signed JWTs with expiration.</li>
<li>Set claims in the JWTs to be able to identify the user.</li>
<li>An overview of JWT structure and how to use them.</li>
<li>Create new or use framework provided middlewares to validate tokens and restrict apis.</li>
</ul>
<p>To get started, we need to create a new Go application:</p>
<p><em>Make the path appropriate for your workspace.</em></p>
<p>mkdir -p go/src/github.com/war1oc/jwt-auth</p>
<p>I use <a href="https://github.com/golang/dep">Dep</a> for dependency management.</p>
<pre><code class="hljs language-bash">dep init
</code></pre>
<p>I will be using the <a href="https://echo.labstack.com/">Echo Framework</a>. It’s a very minimalist framework which has the essentials baked in. Let’s create the main.go file by taking the code from the <a href="https://echo.labstack.com/guide">Echo Guide</a>. This will be our starting point.</p>
<p><em>main.go:</em></p>
<pre><code class="hljs language-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"net/http"</span>
    <span class="hljs-string">"github.com/labstack/echo"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    e := echo.New()
    e.GET(<span class="hljs-string">"/"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(c echo.Context)</span></span> <span class="hljs-type">error</span> {
        <span class="hljs-keyword">return</span> c.String(http.StatusOK, <span class="hljs-string">"Hello, World!"</span>)
    })
    e.Logger.Fatal(e.Start(<span class="hljs-string">":1323"</span>))}
</code></pre>
<p>Run <code>dep ensure</code> to install the newly added dependency (“github.com/labstack/echo”).</p>
<p>If you run the application now, echo fires up a server and listens on the <code>:1323</code> port. A basic hello world application.</p>
<p>Let’s create the login handler:</p>
<p><em>handler.go:</em></p>
<pre><code class="hljs language-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"net/http"</span>
    <span class="hljs-string">"time"</span>

    <span class="hljs-string">"github.com/dgrijalva/jwt-go"</span>
    <span class="hljs-string">"github.com/labstack/echo"</span>
)

<span class="hljs-keyword">type</span> handler <span class="hljs-keyword">struct</span>{}

<span class="hljs-comment">// Most of the code is taken from the echo guide</span>
<span class="hljs-comment">// [https://echo.labstack.com/cookbook/jwt](https://echo.labstack.com/cookbook/jwt)</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(h *handler)</span></span> login(c echo.Context) <span class="hljs-type">error</span> {
    username := c.FormValue(<span class="hljs-string">"username"</span>)
    password := c.FormValue(<span class="hljs-string">"password"</span>)

    <span class="hljs-comment">// Check in your db if the user exists or not</span>
    <span class="hljs-keyword">if</span> username == <span class="hljs-string">"jon"</span> &#x26;&#x26; password == <span class="hljs-string">"password"</span> {
        <span class="hljs-comment">// Create token</span>
        token := jwt.New(jwt.SigningMethodHS256)

        <span class="hljs-comment">// Set claims</span>
        <span class="hljs-comment">// This is the information which frontend can use</span>
        <span class="hljs-comment">// The backend can also decode the token and get admin etc.</span>
        claims := token.Claims.(jwt.MapClaims)
        claims[<span class="hljs-string">"name"</span>] = <span class="hljs-string">"Jon Doe"</span>
        claims[<span class="hljs-string">"admin"</span>] = <span class="hljs-literal">true</span>
        claims[<span class="hljs-string">"exp"</span>] = time.Now().Add(time.Hour * <span class="hljs-number">72</span>).Unix()

        <span class="hljs-comment">// Generate encoded token and send it as response.</span>
        <span class="hljs-comment">// The signing string should be secret (a generated UUID          works too)</span>
        t, err := token.SignedString([]<span class="hljs-type">byte</span>(<span class="hljs-string">"secret"</span>))
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            <span class="hljs-keyword">return</span> err
        }
        <span class="hljs-keyword">return</span> c.JSON(http.StatusOK, <span class="hljs-keyword">map</span>[<span class="hljs-type">string</span>]<span class="hljs-type">string</span>{
            <span class="hljs-string">"token"</span>: t,
        })
    }

    <span class="hljs-keyword">return</span> echo.ErrUnauthorized
}
</code></pre>
<p>Since database connection and querying is not in the scope of this article, I checked the username and password this way.</p>
<p>This is a very minimal application describing the core of JWT in Go.</p>
<p>We need to add a route for login:</p>
<p><em>main.go:</em></p>
<pre><code class="hljs language-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"net/http"</span>

    <span class="hljs-string">"github.com/labstack/echo"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    e := echo.New()
    e.GET(<span class="hljs-string">"/"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(c echo.Context)</span></span> <span class="hljs-type">error</span> {
        <span class="hljs-keyword">return</span> c.String(http.StatusOK, <span class="hljs-string">"Hello, World!"</span>)
    })

    h := &#x26;handler{}
    e.POST(<span class="hljs-string">"/login"</span>, h.login)

    e.Logger.Fatal(e.Start(<span class="hljs-string">":1323"</span>))
}
</code></pre>
<p>Run the app <code>go run *.go</code></p>
<pre><code class="hljs language-bash">curl -X POST localhost:1323/login -d <span class="hljs-string">"username=jon&#x26;password=password"</span>

{<span class="hljs-string">"token"</span>:<span class="hljs-string">"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6dHJ1ZSwiZXhwIjoxNTQyNjkwMjQ3LCJuYW1lIjoiSm9uIERvZSJ9.OqsaJ76nYhiaiVPcAr13\_vMPyTfRcv6eKFm06O3n8fE"</span>}
</code></pre>
<p>You should get the token in the response when you hit the api with the correct username and password.</p>
<p>Incorrect username and password will throw an unauthorised error.</p>
<pre><code class="hljs language-bash">curl -X POST localhost:1323/login -d <span class="hljs-string">"username=jon&#x26;password=nope"</span>

{<span class="hljs-string">"message"</span>:<span class="hljs-string">"Unauthorized"</span>}
</code></pre>
<p>Since the token is being generated using <em>exp</em> (expiration), it will be unique everytime.</p>
<p>Let’s inspect how our token looks when decoded, head over to <a href="https://jwt.io">jwt.io</a> and paste the token:</p>
<p><em>Header:</em></p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
  <span class="hljs-attr">"alg"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"HS256"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"typ"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"JWT"</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<p><em>Payload:</em></p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
  <span class="hljs-attr">"admin"</span><span class="hljs-punctuation">:</span> <span class="hljs-keyword">true</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"exp"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1542690247</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Jon Doe"</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<p>Both front and backend can use the payload to identify the user.</p>
<p>Now we can restrict apis and require users to be logged in. This can be accomplished by middlewares. We can also restrict by claims such as if a user is an admin or not.</p>
<p>Take a look at echo’s JWT middleware:</p>
<p><a href="https://echo.labstack.com/middleware/jwt" title="https://echo.labstack.com/middleware/jwt"><strong>JWT Middleware - Echo - High performance, minimalist Go web framework</strong><br>
JWT middleware for Echo - Echo is a high performance, extensible, minimalist web framework for Go (Golang).</a><a href="https://echo.labstack.com/middleware/jwt"></a></p>
<p>We create a middleware of our own which is basically just the echo jwt middleware, but we can now use it with our handlers.</p>
<p><em>middleware.go:</em></p>
<pre><code class="hljs language-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"github.com/labstack/echo/middleware"</span>
)

<span class="hljs-keyword">var</span> IsLoggedIn = middleware.JWTWithConfig(middleware.JWTConfig{
    SigningKey: []<span class="hljs-type">byte</span>(<span class="hljs-string">"secret"</span>),
})
</code></pre>
<p>The signing key has to match the signing key used when issuing the token. It’s better to keep it in a configuration file or environment variable.</p>
<p>Let’s create a restricted api:</p>
<p>In <em>main.go:</em></p>
<pre><code class="hljs language-go">e.GET(<span class="hljs-string">"/private"</span>, h.private, isLoggedIn)
</code></pre>
<p>In <em>handler.go:</em></p>
<pre><code class="hljs language-go"><span class="hljs-comment">// Most of the code is taken from the echo guide</span>
<span class="hljs-comment">// [https://echo.labstack.com/cookbook/jwt](https://echo.labstack.com/cookbook/jwt)</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(h \*handler)</span></span> private(c echo.Context) <span class="hljs-type">error</span> {
    user := c.Get(<span class="hljs-string">"user"</span>).(\*jwt.Token)
    claims := user.Claims.(jwt.MapClaims)
    name := claims[<span class="hljs-string">"name"</span>].(<span class="hljs-type">string</span>)
    <span class="hljs-keyword">return</span> c.String(http.StatusOK, <span class="hljs-string">"Welcome "</span>+name+<span class="hljs-string">"!"</span>)
}
</code></pre>
<p>Run the app <code>go run *.go</code></p>
<p>If we now hit the private api without any token we get an error:</p>
<pre><code class="hljs language-bash">curl localhost:1323/private

{<span class="hljs-string">"message"</span>:<span class="hljs-string">"missing or malformed jwt"</span>}
</code></pre>
<p>With token:</p>
<pre><code class="hljs language-bash">curl -H <span class="hljs-string">"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6dHJ1ZSwiZXhwIjoxNTQyNjkwMjQ3LCJuYW1lIjoiSm9uIERvZSJ9.OqsaJ76nYhiaiVPcAr13\_vMPyTfRcv6eKFm06O3n8fE"</span> localhost:1323/private

Welcome Jon Doe!
</code></pre>
<p>And there you have it! Authentication with JWT. As you may have already noticed, we did not make a database call but got the user’s name from the token itself. This has tremendous performance advantages over making db query for every api call the client makes. We can extract necessary information such as user ID and role from the token to decide whether we want to allow the user access to the api or not.</p>
<p>Now that we have a basic jwt auth system running, let’s go a step further and create a middleware for admin (isAdmin).</p>
<p><em>middleware.go:</em></p>
<pre><code class="hljs language-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">isAdmin</span><span class="hljs-params">(next echo.HandlerFunc)</span></span> echo.HandlerFunc {
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(c echo.Context)</span></span> <span class="hljs-type">error</span> {
        user := c.Get(<span class="hljs-string">"user"</span>).(*jwt.Token)
        claims := user.Claims.(jwt.MapClaims)
        isAdmin := claims[<span class="hljs-string">"admin"</span>].(<span class="hljs-type">bool</span>)

        <span class="hljs-keyword">if</span> isAdmin == <span class="hljs-literal">false</span> {
            <span class="hljs-keyword">return</span> echo.ErrUnauthorized
        }

        <span class="hljs-keyword">return</span> next(c)
    }
}
</code></pre>
<p><em>main.go:</em></p>
<pre><code class="hljs language-go">e.GET(<span class="hljs-string">"/admin"</span>, h.private, isLoggedIn, isAdmin)
</code></pre>
<p>Middlewares are also like handler functions, and you can add as many as you want. In this case, the <em>isLoggedIn</em> middleware sets the user to the context and thus we can use it in our <em>isAdmin</em> middleware which only checks whether the user is an admin or not.</p>
<p>Change the claim admin to false in the login handler:</p>
<pre><code class="hljs language-go">claims[<span class="hljs-string">"admin"</span>] = <span class="hljs-literal">false</span>
</code></pre>
<p>Log in again and using the new token try hitting the /admin api:</p>
<pre><code class="hljs language-bash">curl -H <span class="hljs-string">"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6ZmFsc2UsImV4cCI6MTU0MjgwMjIwMywibmFtZSI6IkpvbiBEb2UifQ.kuypz\_fyVlnvMOweU6izkjuKTKOjPlJTYV-q3iT3pHg"</span> localhost:1323/admin

{<span class="hljs-string">"message"</span>:<span class="hljs-string">"Unauthorized"</span>}
</code></pre>
<p>However, the private api still works fine:</p>
<pre><code class="hljs language-go">curl -H <span class="hljs-string">"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6ZmFsc2UsImV4cCI6MTU0MjgwMjIwMywibmFtZSI6IkpvbiBEb2UifQ.kuypz\_fyVlnvMOweU6izkjuKTKOjPlJTYV-q3iT3pHg"</span> localhost:<span class="hljs-number">1323</span>/private

Welcome Jon Doe!
</code></pre>
<p>Changing the claim to insert admin as true, you will be able to access the /admin api:</p>
<pre><code class="hljs language-go">curl -H <span class="hljs-string">"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6dHJ1ZSwiZXhwIjoxNTQyODAyMzk5LCJuYW1lIjoiSm9uIERvZSJ9.K-uDZN\_XX0J9PaUJXeRGjHmtfDwLr9StGZG1FOIa5Hc"</span> localhost:<span class="hljs-number">1323</span>/admin

Welcome Jon Doe!
</code></pre>
<p>You can create middlewares which satisfy your business use cases and give access to users accordingly.</p>
<h4>Where to go from here</h4>
<p>This was a very primitive implementation of JWT in Go. In a production environment you will definitely have to be more thoughtful.</p>
<p>Check out the full source code here:</p>
<p><a href="https://github.com/war1oc/jwt-auth" title="https://github.com/war1oc/jwt-auth"><strong>war1oc/jwt-auth</strong><br>
A tutorial for implementing JWT authentication in Golang - war1oc/jwt-auth</a><a href="https://github.com/war1oc/jwt-auth"></a></p>
<p>There are a lot more to learn on this topic. I will list down a few for giving you guys a head start in your search:</p>
<ul>
<li>Refresh tokens (access tokens should be short lived and refresh tokens long lived; refresh tokens are used to issue new access tokens for valid users)</li>
<li>How to have a global logout/panic button (logs out all users) in case of an emergency or breach</li>
<li>Using JWTs in microservices</li>
<li>Using App keys for microservices to talk to each other</li>
</ul>
<p>These are just a few things you might want to check out to build a more robust and secure application. While wading through these dark waters you may find a lot more clues what to study next. Good luck!</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/SSAbwzqz2Kc">Markus Spiske</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Going to ServerSide.swift 2018]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2018/10/29/Going-to-ServerSide-swift-2018</link>
            <guid>https://engineering.monstar-lab.com/en/post/2018/10/29/Going-to-ServerSide-swift-2018</guid>
            <pubDate>Mon, 29 Oct 2018 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>The first conference in the world to focus on server-side Swift</h2>
<p>We (the Vapor team at Nodes) were excited when we heard that a conference focusing on server-side Swift was announced. We did get the news a bit early since the conference is run by <a href="https://twitter.com/0xtim">Tim Condon</a>, <a href="https://twitter.com/martinlasek">Martin Lasek</a> and <a href="https://twitter.com/steffendsommer">Steffen D. Sommer</a> where Martin and Steffen both work at Nodes. Before this conference, we would look at conferences focused around Swift or more traditional "backend" conferences which most likely would focus on another language than Swift. Being able to go to a conference that mixes backend together with our programming language of choice is ideal to us.</p>
<p>The whole Vapor team at Nodes met up in Berlin to participate in the conference and with this blog post we wanted to share a couple of highlights from the conference as well as give some feedback on the conference in case any readers are considering to go next year.</p>
<h2>Quick facts</h2>
<p>Some quick facts about ServerSide.swift 2018:</p>
<ul>
<li>September 12-14 in Berlin (Adlershof)</li>
<li>One day of workshops. Four half-day workshops to choose from, with two running in parallel before lunch and two more running in parallel after lunch. This meant that you would be able to attend two at most.</li>
<li>Two days of talks. 8 talks per day and 18 speakers in total.</li>
<li>September 13 had a panel discussion after the last talk with five people from the community in the panel.</li>
<li>Website: <a href="http://serversideswift.info">http://serversideswift.info</a> - videos should be available soon at the time of writing this post.</li>
</ul>
<h2>Talk highlights</h2>
<p>With 16 talks in total there has been a lot of knowledge to digest. Here are some highlights from the different talks we heard:</p>
<h3>Vapor State Of The Union (Tanner Nelson)</h3>
<p>Since we at Nodes spend a lot of time using Vapor we were of course interested in the state of the union talk by Vapor's founder, Tanner Nelson. The talk went through the biggest changes from Vapor 2 to Vapor 3, being 1) how we deal with configs (read: no more JSON files), 2) how Codable removes a lot of boiler plate code for us and 3) how Vapor is now built on top of SwiftNIO, making it possible for the Vapor team to delete a lot of code. The talk also gave a quick overview of the different packages that exist within the Vapor ecosystem and it provided a sneak peek into what can be expected from Vapor Cloud 2 which <a href="https://medium.com/vapor-cloud/vapor-cloud-2-public-alpha-2d9ee14d0688">launched in alpha recently</a>. Lastly, the talk also touched upon the <a href="https://swift.org/server/">Swift Server Work Group</a> and the <a href="https://forums.swift.org/c/server/43">Swift Forums</a>.</p>
<h3>From Idea to Production at ING (Ian Partridge and Pim Stolk)</h3>
<p>It is always a good idea to have the finger on the pulse regarding alternative tools, and this split venture talk from Pim from <a href="https://www.ing.com">ING</a> (Dutch bank) and Ian from <a href="https://www.kitura.io">IBM and Kitura</a> provided just that.</p>
<p>ING is a bank in The Netherlands where Kitura (an alternative framework to Vapor) is playing a big role in their tech stack. The first part of the talk was given by Pim from ING and, besides presenting ING to the crowd, it revolved around the thoughts behind choosing Kitura, bringing innovative solutions into the banking world.</p>
<p>Ian's talk focused on Kitura and presented how the latest version has evolved from <code>2.0</code> to current <code>2.5</code>. A key takeaway was how they managed to integrate <a href="https://www.openapis.org/">OpenAPI</a> in Kitura by automating their API documentation using auto-generated Swagger documentation and examples. This is a feature that would be welcomed by any API developer.</p>
<h3>Microservices (Ralph Küpper and Marcin Kliks)</h3>
<p>There were two talks about microservices and the topic was mentioned several more times in other talks and in the hallways. This approach of splitting up backends into several dedicated subsystems that each have a very specific role is a hot topic in serverland in general and server-side Swift is no exception.</p>
<p>In his talk, Ralph Küpper explored microservices from an architectural approach and gave examples of how he is using it in his company <a href="https://www.skelpo.com">Skelpo</a> to create apps for their clients. He provided a balanced view, listing both advantages (e.g. being able to tailor approaches to the setup of each component in terms of language and framework and advantages in terms of team allocation) and disadvantages (it is hard to do right and there is an overhead in terms of time and money).</p>
<p>Marcin Kliks works for <a href="https://allegro.pl"><em>allegro</em></a>, an e-commerce platform in Poland, and he gave an example of a specific microservice that he had developed that took care of one thing: compressing images. Their site is based heavily around images and they were experiencing slow response times. In order to deal with the influx of new images they needed a fast system that was also memory-efficient due to the already high memory load inherent to image optimization. He demonstrated how he was able to call into MozJPEG's C API from Swift and could debug both languages in Xcode at the same time.</p>
<h3>Microservers (AKA SwiftNIO on Raspberry Pi by Helge Heß)</h3>
<p>As one of the last talks of the conference, this talk was full of surprises, both in terms of content and as well in terms of delivery. Watching Helge demonstrate the powers of the small and efficient but fast device in action running SwiftNIO was both highly informative and entertaining.
He showed step by step how to get a simple server up and running using a small wrapper around SwiftNIO, some Xcode templates, and a Docker deploy script. A <a href="https://www.uraimo.com">community-maintained</a> port of Swift for ARM processors and a small Docker hosting <a href="http://hypriot.com">operating system</a> were used to get it all working.
Helge also demonstrated a more complex setup where the Raspberry Pi hosted both an HTTP + WebSocket and an IRC server making it possible to send commands to the little powerhouse over a web-based IRC client that caused messages to display on a small attached text-display. It served as a great demonstration for the flexibility of the Raspberry Pi and its potential to run Swift thanks to its speed and low memory consumption.
All this potential for cheap and plentiful dedicated cores has some real world uses as well. <a href="https://www.scaleway.com">Scaleway</a> is a hosting platform that offers ARM based servers. Due to the compactness and low heat they are able to get around 8000 cores in a single server rack.</p>
<h2>Last remarks</h2>
<p>In spite of coming from different backgrounds and using different frameworks, the attendees shared an enthusiasm for Swift on the server and it was great to experience the sense of community in real life, outside the chat servers. Bridges were built between the subcommunities, and knowledge and experiences were shared.</p>
<p>On Thursday there was an open bar (sponsored by <a href="https://swiftengine.io">Swiftengine</a>) which allowed us to mingle and meet speakers and fellow attendees in a relaxed atmosphere. Among the attendees were two people from the SwiftNIO team (<a href="https://twitter.com/Lukasaoz">Cory Benfield</a> and <a href="https://twitter.com/johannesweiss">Johannes Weiss</a>). They are very approachable and active not just in their support on the Vapor Discord server (and other frameworks), but equally so in real life and this conference provided a great opportunity to meet some of the minds behind the awesome project that is SwiftNIO.</p>
<p>There were over 80 participants and surely many more would have liked to come but couldn't because of travel or lack of time. It was the first conference focused on server-side Swift but it clearly will not be the last since this topic is very popular. The conference was well-organized - even though the organizers were new at it - and the quality of the talks was very high so we're looking forward to next year's edition!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Android WorkManager]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2018/08/06/Android-WorkManager</link>
            <guid>https://engineering.monstar-lab.com/en/post/2018/08/06/Android-WorkManager</guid>
            <pubDate>Mon, 06 Aug 2018 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><code>WorkManager</code> is a new API in Android Architecture Components introduced in the Google I/O 2018. It simplifies and makes it much easier to do work on background threads. The <code>WorkManager</code> schedules tasks as instances of the <code>Worker</code> class and can schedule these workers based on certain conditions which you can set by using the provided The <code>Constraints</code> class. Examples of conditions you can set from the <code>Constraints</code> class, can be things like available internet/wifi connection or if a charger is connected. The <code>WorkManager</code> can also schedule all <code>Worker</code> instances to launch in any order and we can also pass input data into a <code>Worker</code> and get the output data.</p>
<p>Also a very important note about <a href="https://developer.android.com/topic/libraries/architecture/workmanager"><code>WorkManager</code></a>: “WorkManager is intended for tasks that require a guarantee that the system will run them even if the app exits...”*</p>
<h2>How to use it</h2>
<p>First you have to add the dependency in your <code>build.gradle</code> file:
<code>implementation "android.arch.work:work-runtime:1.0.0-alpha02"</code></p>
<p>We will look at the following:</p>
<ul>
<li>How to create a Worker</li>
<li>How to create Constraints</li>
<li>How to give a Worker instance an input and get its output</li>
<li>How to schedule all Worker's to be executed in a particular order</li>
</ul>
<h3>User story</h3>
<p>To demonstrate how to use <code>WorkManager</code> we will start by establishing a made-up user story.
In our imaginary app QuickSnapper, we press a shutter button and the app will take a picture and apply some stickers on it and upload it all in one automatic process thanks to the WorkManager.</p>
<p>So let's split up the user story in 3 use cases:</p>
<ul>
<li>1 The user takes a photo (We want to compress it here)</li>
<li>2 The app adds weather and location information on the picture after step 1 (GPS and Internet must be available)</li>
<li>3 The app uploads the image immediately after step 1-2 (Internet must be available)</li>
</ul>
<p>For each use case we will make a <code>Worker</code> class. To do that we need to make a class and extend <code>Worker</code> which requires us to implement a <code>doWork()</code> method with a return type of a <code>WorkerResult</code> that can either be <code>WorkerResult.SUCCESS</code> or <code>WorkerResult.FAILURE</code></p>
<h3>Creating Workers</h3>
<p>The first Worker compress our Bitmap into a smaller size, convert the Bitmap to <code>ByteArray</code> passes it in the WorkManager's <code>outputData</code> object and then we return <code>WorkerResult.SUCCESS</code> or <code>WorkerResult.FAILURE</code>.</p>
<pre><code class="hljs language-kotlin">    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ImageCompressionTask</span></span>(<span class="hljs-keyword">val</span> bitmap: Bitmap?) : Worker() {
        <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">doWork</span><span class="hljs-params">()</span></span>: WorkerResult {
            <span class="hljs-keyword">val</span> newBitmap: Bitmap?
            <span class="hljs-keyword">try</span> {
                <span class="hljs-comment">//Create a compressed bitmap</span>
                newBitmap = Bitmap.createScaledBitmap(bitmap, <span class="hljs-number">500</span>, <span class="hljs-number">500</span>, <span class="hljs-literal">false</span>)
                <span class="hljs-keyword">return</span> WorkerResult.SUCCESS
            } <span class="hljs-keyword">catch</span> (e: IllegalArgumentException) {
                <span class="hljs-keyword">return</span> WorkerResult.FAILURE
            }
        }
    }
</code></pre>
<p>In the second Worker we retrieve the Bitmap from the Workers <code>inputData</code> object and adds some weather and location stickers on the image</p>
<pre><code class="hljs language-kotlin">    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AddStickersTask</span> : <span class="hljs-type">Worker</span></span>() {
        <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">doWork</span><span class="hljs-params">()</span></span>: WorkerResult {
            <span class="hljs-keyword">try</span> {
                <span class="hljs-comment">//Adding stickers on the bitmap...</span>
                <span class="hljs-keyword">return</span> WorkerResult.SUCCESS
            } <span class="hljs-keyword">catch</span> (e: Exception) {
                <span class="hljs-keyword">return</span> WorkerResult.FAILURE
            }
        }
    }
</code></pre>
<p>In the last Worker we just upload our Bitmap to our server</p>
<pre><code class="hljs language-kotlin">    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UploadImageTask</span> : <span class="hljs-type">Worker</span></span>() {
        <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">doWork</span><span class="hljs-params">()</span></span>: WorkerResult {
            <span class="hljs-comment">//Retrieve bitmap and upload work here</span>
            <span class="hljs-keyword">return</span> WorkerResult.SUCCESS
        }
    }
</code></pre>
<h2>Creating Constraints for workers</h2>
<p>Now we have created 3 <code>Worker</code> classes and can chain them together so they run when each previous <code>Worker</code> has returned <code>WorkerResult.SUCCESS</code>. The <code>WorkManager</code> won't proceed if any of the <code>Worker</code> instances returns <code>WorkerResult.FAILURE</code>.</p>
<p>But first we have to make our <code>Constraints</code> for our <code>Worker</code> instances, so the <code>Worker</code> only runs if the conditions we have set in the <code>Constraints</code> class is met.</p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">val</span> constraint = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
</code></pre>
<p>GPS requirement is not yet supported in the <code>Constraints</code> class but we will instead check for enabled GPS in the <code>AddStickersTask</code> and if it’s not enabled we will return <code>FAILURE</code> and the next <code>WorkManager</code> won’t proceed to the next <code>Worker</code></p>
<h4>Now lets use our Worker classes</h4>
<p>Here we set our <code>Constraints</code> for each <code>Worker</code> and <code>build()</code> will return an instance of <code>OneTimeWorkRequest</code></p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">val</span> imageCompressionTask = OneTimeWorkRequest.Builder(ImageCompressionTask::<span class="hljs-keyword">class</span>.java).build()
<span class="hljs-keyword">val</span> addStickersTask = OneTimeWorkRequest.Builder(AddStickersTask::<span class="hljs-keyword">class</span>.java).setConstraints(constraint).build()
<span class="hljs-keyword">val</span> uploadImageTask = OneTimeWorkRequest.Builder(UploadImageTask::<span class="hljs-keyword">class</span>.java).setConstraints(constraint).build()
</code></pre>
<p>We make them as <code>OneTimeWorkRequest</code> because we only want these <code>Worker</code> to execute once. <code>PeriodicWorkRequest</code> can be used in cases where you want a <code>Worker</code> for some repetitive work which can run in intervals you can set.</p>
<h2>Input data and output data</h2>
<h4>Input data</h4>
<p>As it is right now, our <code>ImageCompressionTask</code> doesn't have any Bitmap to compress. So we have to give it a <code>Bitmap</code> before it begins its work. We can pass a <code>Bitmap</code> or any type of data to our <code>Worker</code> classes by sending it an instance of a <code>Data</code> object from the <code>WorkerManger</code> API.</p>
<p>The <code>Data</code> class dosn't support <code>Bitmap</code> but it does support <code>ByteArray</code>, so we can convert our <code>Bitmap</code> to a <code>ByteArray</code>
by using this static method to create a new intance of <code>Data</code> containg our <code>ByteArray</code></p>
<pre><code class="hljs language-kotlin"><span class="hljs-keyword">val</span> compressionData = Data.fromByteArray(getBitmapByteArray())
</code></pre>
<p>Now we just add a <code>String</code> tag to identify and retrieve our <code>Worker</code> later and we give it the <code>compressionData</code> like this:</p>
<pre><code class="hljs language-kotlin"> <span class="hljs-keyword">val</span> imageCompressionTask = OneTimeWorkRequest.Builder(ImageCompressionTask::<span class="hljs-keyword">class</span>.java).addTag(TAG_WORKER_1).setInputData(compressionData).build()
</code></pre>
<h4>Getting the input data in the Worker class</h4>
<p>Now when we have given our <code>ImageCompressionTask</code> some input data, we can extrat that by just calling the <code>inputData</code> object provided from the <code>Worker</code> class and when we are finished with our data, we can make it put in the <code>outputData</code> object so we can retrieve it outside of the <code>ImageCompressionTask</code> class</p>
<pre><code class="hljs language-kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ImageCompressionTask</span></span>() : Worker() {

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">doWork</span><span class="hljs-params">()</span></span>: WorkerResult {
        <span class="hljs-keyword">try</span> {
             <span class="hljs-comment">//get the input data</span>
            <span class="hljs-keyword">val</span> bitmapByteArray = Data.toByteArray(inputData)
            <span class="hljs-keyword">val</span> bitmap = BitmapFactory.decodeStream(ByteArrayInputStream(bitmapByteArray))

            <span class="hljs-keyword">val</span> newBitmap = Bitmap.createScaledBitmap(bitmap, <span class="hljs-number">500</span>, <span class="hljs-number">500</span>, <span class="hljs-literal">false</span>)

            <span class="hljs-comment">//Save the bitmap back to the outputData</span>
            outputData = Data.fromByteArray(getBitmapByteArrayHelper(newBitmap))
            <span class="hljs-keyword">return</span> WorkerResult.SUCCESS
        } <span class="hljs-keyword">catch</span> (e: IllegalArgumentException) {
            <span class="hljs-keyword">return</span> WorkerResult.FAILURE
        }
    }
</code></pre>
<h4>Output data</h4>
<p>Usually we want to get the output data from a <code>Worker</code> when it have finished its work. To do that we can listen on a specific <code>Worker</code> by retrieving the <code>Worker</code> by its tag:</p>
<pre><code class="hljs language-kotlin">     WorkManager.getInstance().getStatusesByTag(TAG_WORKER_1).observe(<span class="hljs-keyword">this</span>, Observer { workerStatusList ->
            <span class="hljs-keyword">val</span> workstatus = workerStatusList?.<span class="hljs-keyword">get</span>(<span class="hljs-number">0</span>)
            workstatus?.let {
                <span class="hljs-keyword">if</span> (it.state.isFinished) {
                    <span class="hljs-keyword">val</span> outputData = it.outputData
                }
            }
        })
</code></pre>
<h2>Putting everything together</h2>
<p>Now we just feed our <code>WorkManager</code> with our <code>Worker</code>'s in the order as described in our user story and we done!</p>
<pre><code class="hljs language-kotlin">WorkManager.getInstance().beginWith(imageCompressionTask).then(addStickersTask).then(uploadImageTask).enqueue()
</code></pre>
<h2>When should you use it?</h2>
<p>The <code>WorkManager</code> is very useful for tasks running in background threads and for tasks which need to fulfill certain conditions before they can run or automated tasks running in a certain order.</p>
<h4>Some example of when WorkManager also can be really useful</h4>
<ul>
<li>Uploading data</li>
<li>Download data</li>
<li>Bitmap Compression work</li>
<li>GPS location logging.</li>
<li>Chat apps</li>
<li>Playlists apps</li>
<li>Repetitive work that needs to run on background threads</li>
</ul>
<h2>Links</h2>
<p>A more detailed and advanced tutorial on how to work with WorkManager from Google:
<a href="https://codelabs.developers.google.com/codelabs/android-workmanager/#0">https://codelabs.developers.google.com/codelabs/android-workmanager/#0</a></p>
<p>More about WorkManager:
<a href="https://developer.android.com/topic/libraries/architecture/workmanager">https://developer.android.com/topic/libraries/architecture/workmanager</a></p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/QckxruozjRg">Annie Spratt</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Composition over Inheritance]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2018/07/23/Composition-over-Inheritance</link>
            <guid>https://engineering.monstar-lab.com/en/post/2018/07/23/Composition-over-Inheritance</guid>
            <pubDate>Mon, 23 Jul 2018 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>You are tasked with building an application that deals with cars. You are off designing the classes for cars unaware of the looming dangers of inheritance.</p>
<p>Every car has a drive functionality, the driver floors the accelerator and the car moves.</p>
<p>So, you design a base class called Car which has a function drive().</p>
<p>![Car base class](/assets/img/articles/2018-07-23-Composition-over-Inheritance/car-base-class.webp</p>
<p>You implement the Drive() method in this base class so that all the sub-car classes inheriting from this base Car class get it as it is common functionality. You feel very good about yourself for writing reusable code.</p>
<p>The client wants a car that can drive with petrol and another on electricity.</p>
<p>You create two classes for these:</p>
<p>![Petrol and Electric car classes](/assets/img/articles/2018-07-23-Composition-over-Inheritance/petrol-and-electric-car-classes.webp</p>
<p>The petrol driven car needs to be refueled and the electric car needs to be charged, hence the two methods in each class.</p>
<p>Your application works as expected and the client is happy which in turn makes you and your boss happy.</p>
<p>A couple of months go by and the client comes back to you with guess what? A new requirement. They need a hybrid car.</p>
<p>The hybrid needs to run on both petrol and electricity. Suddenly, you have a problem. You find three ways to fit this into your existing model.</p>
<p>Either inherit from the Petrol Car class and duplicate the code for Charge().</p>
<p>![Hybrid car class inherits petrol car class](/assets/img/articles/2018-07-23-Composition-over-Inheritance/hybrid-car-class-inherits-petrol-car-class.webp</p>
<p>Or, you can inherit from the Electric Car class and duplicate the code for Refuel().</p>
<p>![Hybrid car class inherits electric car class](/assets/img/articles/2018-07-23-Composition-over-Inheritance/hybrid-car-class-inherits-electric-car-class.webp</p>
<p>The third option you see, and by far the most popular choice, is to inherit from the Car class and duplicate the code for both Refuel() and Charge().</p>
<p>![Hybrid car class inherits car class directly](/assets/img/articles/2018-07-23-Composition-over-Inheritance/hybrid-car-class-inherits-car-class-directly.webp</p>
<p>This decision makes the most sense among all three in my opinion. The Hybrid car is not exclusively an electric car that it should be derived from the Electric Car class; the same applies for the Petrol Driven Car class. However, there is still a lot of code duplication going on. Changes in refueling logic needs to be implemented in multiple places now, this will prove to be troublesome during maintenance. You accept the risks thinking there won’t be much changes in the future. Right when you were feeling good about your work, the client approaches you and tells you they need a Toy Car now. The Toy Car is special. It will not have the Drive functionality. And just like that, the model you had created, collapses. The Drive() method will now have to be duplicated across all the car classes except Toy Car class. You can see how we are now heading down a slippery slope right?</p>
<p>What we are seeing is a pattern of IS-A relations; and electric car IS A car. This is the root of the problem here. We should get rid of so many IS-A relations and compose a car object with HAS-A relations. HAS-A relations can be interpreted as a car HAS A drive behaviour. We can create separate interfaces for each behaviour:</p>
<p>![Behaviour interfaces](/assets/img/articles/2018-07-23-Composition-over-Inheritance/behaviour-interfaces.webp</p>
<p>We can now compose the Car Class with these behaviours:</p>
<p>![Car behaviour composition](/assets/img/articles/2018-07-23-Composition-over-Inheritance/car-behaviour-composition.webp</p>
<p>What this means is that a car object is composed of the above functionality. They are not concrete implementation, rather, they are abstractions to allow any behaviour to be plugged in depending on the need. There can be any number of concrete implementations of the behaviours:</p>
<p>![Refuel behaviour implementation](/assets/img/articles/2018-07-23-Composition-over-Inheritance/refuel-behaviour-implementation.webp</p>
<p>Every Refuel Behaviour class must have a Refuel method. The NoRefuelBehaviour class is also a Refuel Behaviour in the sense that it states the absence of this behaviour in the object.</p>
<p>We take a concrete implementation of the interfaces we have in the Car class and inject them to compose cars with mixed functionalities.</p>
<p>The composition can look like this:</p>
<p>Petrol Car = SimpleDriveBehaviour + RefuelPetrolBehaviour + NoChargeBehaviour</p>
<p>Electric Car = SimpleDriveBehaviour + NoRefuelBehaviour + FastChargeBehaviour</p>
<p>Hybrid Car = SimpleDriveBehaviour + RefuelDieselBehaviour + SimpleChargeBehaviour</p>
<p>Toy Car = NoDriveBehaviour + NoRefuelBehaviour + NoChargeBehaviour</p>
<p>As you can see, this way the software becomes much more robust; the model supports pluggable interface implementations and has no code duplication. Now when the client comes up with a new car type, you can just compose it to match the requirement; no inheritance hierarchies to manage.</p>
<h2>Conclusion</h2>
<p>We have looked at a very simple example in this article. Even in this simple model, the inheritance tree broke and we looked into how to fix it using composition.</p>
<p>You might be thinking, but Tanveer, isn’t inheritance one of the pillars of object oriented programming, are you telling us not to use it?</p>
<p>All I’m saying, like many other developers, is to favor composition over inheritance. Currently I’m working with Golang most of the time, and it does not support inheritance. It forces developers to use composition instead. This leads to a much better software design. I believe it wouldn’t be going too far to suggest that we don’t even need inheritance.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/JRVxgAkzIsM">Kelly Sikkema</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Book a test drive with Siri Shortcuts (a Siri Shortcuts intro)]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2018/07/16/SiriShortcuts</link>
            <guid>https://engineering.monstar-lab.com/en/post/2018/07/16/SiriShortcuts</guid>
            <pubDate>Mon, 16 Jul 2018 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Earlier this summer at WWDC 2018, Apple announced <a href="https://developer.apple.com/sirikit/">Siri Shortcuts</a>, a new Siri feature that will allow you to extend your app's capabilities by building custom voice actions.</p>
<p>Siri Shortcuts will be available with the release of iOS 12 later this year.</p>
<h2>Let's get started</h2>
<p>This tutorial will focus on understanding and developing a basic application, which will allow our users to book a test drive with their local car showroom through Siri Shortcuts.</p>
<p>To speed up the development process and solely focus on understanding Siri Shortcuts, I have prepared for you a sample project that can be downloaded <a href="https://github.com/nodes-ios/SiriShortcuts-Demo/tree/starter">here</a></p>
<p>The demo project currently contains 2 ViewControllers, a CatalogViewController for our car list, a BookingConfirmationViewController for our test drive booking confirmation and a DataManager.</p>
<h3>Part 1: Enabling Siri Capabilities and adding Inters Extension</h3>
<p>In order to be able to start developing our Siri Shortcuts feature, we need to enable Siri Capabilities in our app. You can do this by opening your project navigator in the "Capabilities" tab. Here you can scroll down to and turn "Siri" on.</p>
<p align="center"><img src="https://raw.githubusercontent.com/kjoneandrei/blog/iOS-siri-shortcuts-post/source/_posts-images/2018-07-16-SiriShortcuts/1.png"></p>
<p>Now that we have enabled Siri, we will need to add the <code>IntentExtension</code> and <code>IntentExtensionUI</code> targets to our app. To do so, click "File" in the Xcode menu, select "New" -> "Target" and then select the "Intent Extension" target. Name your extension <code>SiriIntentExtension</code> and make sure you have checked "Include Intent Extension UI". This will include <code>SiriIntentExtensionUI</code> automatically to our app's targets.</p>
<p align="center"><img src="https://raw.githubusercontent.com/kjoneandrei/blog/iOS-siri-shortcuts-post/source/_posts-images/2018-07-16-SiriShortcuts/2.png"></p>
<h3>Part 2: Request Siri permissions</h3>
<p>Include the NSSiriUsageDescription key in your iOS app’s Info.plist file and describes what our users will be allowing "Siri" interaction for.</p>
<p>Then in our <code>CatalogViewController</code> import <code>IntentsUI</code> and add the following functions and call <code>requestSiriAuthorization</code> in <code>viewDidLoad</code>. The <code>requestSiriAuthorization</code> function will ask our users on the first app open to allow access to Siri. Because our app's main purpose is to demo Siri's functionality, we can add <code>allowSiriAlert</code> function, that will be called if the user hasn't allowed Siri permissions yet.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">//if user hasn't already allowed Siri permisions, we request the user to authorize Siri</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">requestSiriAuthorization</span>() {
    <span class="hljs-keyword">guard</span> <span class="hljs-type">INPreferences</span>.siriAuthorizationStatus() <span class="hljs-operator">!=</span> .authorized <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> }

    <span class="hljs-type">INPreferences</span>.requestSiriAuthorization { (status) <span class="hljs-keyword">in</span>
        <span class="hljs-keyword">switch</span> status {
        <span class="hljs-keyword">case</span> .authorized, .notDetermined, .restricted:
            <span class="hljs-keyword">self</span>.makeCatalogActivity()
        <span class="hljs-keyword">case</span> .denied:
            <span class="hljs-keyword">self</span>.allowSiriAlert()
        }
    }
}

<span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">allowSiriAlert</span>() {
    <span class="hljs-keyword">let</span> alert <span class="hljs-operator">=</span> <span class="hljs-type">UIAlertController</span>(title: <span class="hljs-string">"Siri permision needed"</span>, message: <span class="hljs-literal">nil</span>, preferredStyle: .alert)
    <span class="hljs-keyword">let</span> allow <span class="hljs-operator">=</span> <span class="hljs-type">UIAlertAction</span>(title: <span class="hljs-string">"Allow"</span>, style: .default) { <span class="hljs-keyword">_</span> <span class="hljs-keyword">in</span>
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> settingsUrl <span class="hljs-operator">=</span> <span class="hljs-type">URL</span>(string: <span class="hljs-type">UIApplication</span>.openSettingsURLString) <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> }
        <span class="hljs-keyword">if</span> <span class="hljs-type">UIApplication</span>.shared.canOpenURL(settingsUrl) {
            <span class="hljs-type">UIApplication</span>.shared.open(settingsUrl, options: [:], completionHandler: <span class="hljs-literal">nil</span>)
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-built_in">print</span>(<span class="hljs-string">"can't open path"</span>)
        }
    }

    <span class="hljs-keyword">let</span> cancel <span class="hljs-operator">=</span> <span class="hljs-type">UIAlertAction</span>(title: <span class="hljs-string">"Cancel"</span>, style: .default) { <span class="hljs-keyword">_</span> <span class="hljs-keyword">in</span>
        alert.dismiss(animated: <span class="hljs-literal">true</span>, completion: <span class="hljs-literal">nil</span>)
    }

    alert.addAction(allow)
    alert.addAction(cancel)

    present(alert, animated: <span class="hljs-literal">true</span>, completion: <span class="hljs-literal">nil</span>)
}
</code></pre>
<h3>Part 3: Donate Shortcuts</h3>
<p>Before Siri can suggest shortcuts for our app, we need to tell Siri about the shortcuts through donations. An app should make donations each time a user performs an action in the app.</p>
<p>Currently there are two types of donations possible:</p>
<ul>
<li>Donate a User Activity</li>
<li>Donate an Interaction</li>
</ul>
<p>More information about this can be found <a href="https://developer.apple.com/documentation/sirikit/donating_shortcuts">here</a>.</p>
<h3>Part 4: Donating a User Activity</h3>
<p>As suggested by Apple <code>NSUserActivity</code> provides a lightweight approach for making a donation that also integrates with other Apple features such as Handoff and Spotlight search. In our project we will use an <code>NSUserActivity</code> to allow our users to find the car catalog from Siri.</p>
<p>To get started define the activity as a type in the <code>NSUserActivityTypes</code> array in our <code>Info.plist</code>. The activity should have a reversed domain name which is unique within the list.</p>
<p>Now in our project, go ahead and create a new <code>Swift</code> file, called <code>NSUserActivity+CatalogActivity</code>. Here we will extend <code>NSUserActivity</code> to create our catalog activity.</p>
<p>To make it easier for future implementation of other custom activities we will create another <code>NSUserActivity</code> extension, where we will hold our identifiers needed for further development.</p>
<p>Your extension should look like this:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">extension</span> <span class="hljs-title class_">NSUserActivity</span> {
    <span class="hljs-keyword">struct</span> <span class="hljs-title class_">ActivityKeys</span> {
        <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> catalog <span class="hljs-operator">=</span> <span class="hljs-string">"catalogActivity"</span>
    }

    <span class="hljs-keyword">struct</span> <span class="hljs-title class_">ActivityTypes</span> {
        <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> catalog <span class="hljs-operator">=</span> <span class="hljs-string">"com.nodes.siriSugestions.demo.catalogActivity"</span>
    }

    <span class="hljs-keyword">struct</span> <span class="hljs-title class_">ActivityTitles</span> {
        <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> catalog <span class="hljs-operator">=</span> <span class="hljs-string">"Car Showroom Catalog"</span>
    }

    <span class="hljs-keyword">struct</span> <span class="hljs-title class_">SearchableKeywords</span> {
        <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> catalog <span class="hljs-operator">=</span> [<span class="hljs-string">"CAR"</span>, <span class="hljs-string">"SHOWROOM"</span>, <span class="hljs-string">"CATALOG"</span>, <span class="hljs-string">"CARS"</span>]
    }

    <span class="hljs-keyword">struct</span> <span class="hljs-title class_">SearchableName</span> {
        <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> catalog <span class="hljs-operator">=</span> <span class="hljs-string">"Car Showroom Catalog"</span>
    }

    <span class="hljs-keyword">struct</span> <span class="hljs-title class_">SearchableDescription</span> {
        <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> catalog <span class="hljs-operator">=</span> <span class="hljs-string">"Find the best cars for your testdrive"</span>
    }

    <span class="hljs-keyword">struct</span> <span class="hljs-title class_">InvocationPhrase</span> {
        <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> catalog <span class="hljs-operator">=</span> <span class="hljs-string">"Show Catalog"</span>
    }
}
</code></pre>
<p>Switch back to <code>NSUserActivity+CatalogActivity</code> and start by extending <code>NSUserActivity</code> and import <code>UIKit</code> and <code>CoreSpotlight</code>.
Now add the following static variable:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> CoreSpotlight
<span class="hljs-keyword">import</span> UIKit

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">NSUserActivity</span> {

    <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> catalogActivity: <span class="hljs-type">NSUserActivity</span> {
    <span class="hljs-keyword">let</span> activity <span class="hljs-operator">=</span> <span class="hljs-type">NSUserActivity</span>(activityType: <span class="hljs-type">NSUserActivity</span>.<span class="hljs-type">ActivityTypes</span>.catalog)
    activity.title <span class="hljs-operator">=</span> <span class="hljs-type">NSUserActivity</span>.<span class="hljs-type">ActivityTitles</span>.catalog
    activity.isEligibleForSearch <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>
    activity.isEligibleForPrediction <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>
    activity.persistentIdentifier <span class="hljs-operator">=</span> <span class="hljs-type">NSUserActivityPersistentIdentifier</span>(<span class="hljs-type">NSUserActivity</span>.<span class="hljs-type">ActivityKeys</span>.catalog)
    activity.suggestedInvocationPhrase <span class="hljs-operator">=</span> <span class="hljs-type">NSUserActivity</span>.<span class="hljs-type">InvocationPhrase</span>.catalog

    <span class="hljs-keyword">#if</span> os(iOS)
    <span class="hljs-keyword">let</span> attributes <span class="hljs-operator">=</span> <span class="hljs-type">CSSearchableItemAttributeSet</span>(itemContentType: <span class="hljs-type">NSUserActivity</span>.<span class="hljs-type">ActivityTypes</span>.catalog)
    attributes.keywords <span class="hljs-operator">=</span> <span class="hljs-type">NSUserActivity</span>.<span class="hljs-type">SearchableKeywords</span>.catalog
    attributes.displayName <span class="hljs-operator">=</span> <span class="hljs-type">NSUserActivity</span>.<span class="hljs-type">SearchableName</span>.catalog
    attributes.contentDescription <span class="hljs-operator">=</span> <span class="hljs-type">NSUserActivity</span>.<span class="hljs-type">SearchableDescription</span>.catalog

    activity.contentAttributeSet <span class="hljs-operator">=</span> attributes
    <span class="hljs-keyword">#endif</span>

    <span class="hljs-keyword">return</span> activity
    }

}
</code></pre>
<p>We have now created a custom <code>NSUserActivity</code> which is both eligible for search and prediction, with a custom <code>suggestedInvocationPhrase</code> to be suggested later for our users when creating a <code>Voice Shortcut</code>. As well we have added a <code>contentAttributeSet</code> to our activity. This will allow our users to find the activity as well via <code>Core Spotlight</code>.</p>
<p>We are now ready to donate this activity to Siri. For this switch back to our <code>CatalogViewController</code> and in our <code>viewDidLoad</code> call <code>userActivity = NSUserActivity.catalogActivity</code>. By simply assigning our custom activity to the <code>ViewController</code> we have now donated it to Siri.</p>
<p>Because the <code>Shortcuts</code> app is not yet released at the time of this tutorial, our users can create a voice command by going to the Settings app and selecting Siri. Here we will be able to see the new shortcut created and create a voice phrase to invoke it.</p>
<h3>Part 5: Donating an Interaction</h3>
<p>The other way to make a donation is to use an <a href="https://developer.apple.com/documentation/sirikit/ininteraction"><code>INInteraction</code> object</a>. This involves a bit more work, but gives you more control over defining and handling the action.</p>
<p>Firstly we need to add our project and <code>Siri Intent Definition File</code>. You can do this by going to "File" -> "New" -> "File" and select <code>Siri Intent Definition File</code>. Make sure that you have selected as targets for this all the three targets of our project.</p>
<p>Select the newly created <code>Intents.intentdefinition</code>, here in the bottom left corner select the <code>+</code> icon and then <code>New intent</code>. Now that we have created an <code>Intent</code> let's name it <code>TestDrive</code>, select the category as <code>Book</code> and add a Title and a Description. As well extra information for this intent can be added. You can add an Image to be shown for the intent and select the option.</p>
<p>Parameters can be defined for the intent to define the shortcut types. A shortcut type has title, subtitle and a set of parameter combinations such as:</p>
<ul>
<li>car and duration</li>
<li>car and options</li>
<li>car, duration and options</li>
</ul>
<p>These types define the schema that Siri uses to identify requests the user makes; for example, “Book a test drive for 60 minutes.” The parameter combination for this example is: car and duration.</p>
<p>Your final intent should look like this:</p>
<p align="center"><img src="https://raw.githubusercontent.com/kjoneandrei/blog/iOS-siri-shortcuts-post/source/_posts-images/2018-07-16-SiriShortcuts/3.png"></p>
<p>Now that we have created our intent, we must inform our <code>Intent Extensions</code> that our app will support this custom type.</p>
<p>Inside <code>SiriIntentExtensionUI</code> -> <code>Info.plist</code> select <code>NSExtension</code> expand <code>NSExtensionAttributes</code> expand <code>IntentsSupported</code> and add a new item of type <code>String</code> with the value <code>TestDriveIntent</code>.
As well inside <code>SiriIntentExtension</code> -> <code>Info.plist</code> and repeat the previous step.</p>
<p>Now that we have configured our app and our extensions to know about the intent, we can now start focusing on handling it.</p>
<h4>Handle the intent</h4>
<p>Inside our <code>SiriIntentExtension</code>, select default created <code>IntentHandler</code> and add the following lines of code there:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">override</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">handler</span>(<span class="hljs-params">for</span> <span class="hljs-params">intent</span>: <span class="hljs-type">INIntent</span>) -> <span class="hljs-keyword">Any</span> {
<span class="hljs-keyword">guard</span> intent <span class="hljs-keyword">is</span> <span class="hljs-type">TestDriveIntent</span> <span class="hljs-keyword">else</span> {
    <span class="hljs-built_in">fatalError</span>(<span class="hljs-string">"Intent type not recognised <span class="hljs-subst">\(intent)</span>"</span>)
    }

    <span class="hljs-keyword">return</span> <span class="hljs-type">TestDriveIntentHandler</span>()
}
</code></pre>
<p>The above function will handle our custom intent. It will first check if the intent is one of our custom intents and then return a new instance of <code>TestDriveIntentHandler</code>. At this moment this will throw an error at compile time, since <code>TestDriveIntentHandler</code> is not yet defined, so lets go ahead and create it now.</p>
<p>Inside your main target create a new <code>Swift</code> file and name it <code>TestDriveIntentHandler</code> (a protocol generated for us from the <code>Intents Definition File</code>) . Select all 3 <code>Target memberships</code> for it (this way our extensions and our app can use the same custom handler) and let's start coding!</p>
<p>Go ahead and create <code>TestDriveIntentHandler</code> as a class conforming to <code>NSObject</code> and <code>TestDriveIntentHandling</code>. Then add the following functions <code>public func confirm(intent: TestDriveIntent, completion: @escaping (TestDriveIntentResponse) -> Void)</code> which will allow us to perform some final validation on the intent parameters and <code>public func handle(intent: TestDriveIntent, completion: @escaping (TestDriveIntentResponse) -> Void)</code> which will allow us to complete the booking.</p>
<p>Your final class should look like this:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> UIKit
<span class="hljs-keyword">import</span> Intents

<span class="hljs-keyword">class</span> <span class="hljs-title class_">TestDriveIntentHandler</span>: <span class="hljs-title class_">NSObject</span>, <span class="hljs-title class_">TestDriveIntentHandling</span> {
    <span class="hljs-comment">/// - Tag: confirm_intent</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">confirm</span>(<span class="hljs-params">intent</span>: <span class="hljs-type">TestDriveIntent</span>, <span class="hljs-params">completion</span>: <span class="hljs-keyword">@escaping</span> (<span class="hljs-type">TestDriveIntentResponse</span>) -> <span class="hljs-type">Void</span>) {

    <span class="hljs-comment">/*
    The confirm phase provides an opportunity for you to perform any final validation of the intent parameters and to
    verify that any needed services are available. You might confirm that you can communicate with your company’s server
    */</span>
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> car <span class="hljs-operator">=</span> intent.car,
            <span class="hljs-keyword">let</span> modelId <span class="hljs-operator">=</span> car.identifier,
            <span class="hljs-keyword">let</span> <span class="hljs-keyword">_</span> <span class="hljs-operator">=</span> <span class="hljs-type">DataManager</span>.shared.findCar(<span class="hljs-type">Int</span>(modelId)<span class="hljs-operator">!</span>) <span class="hljs-keyword">else</span> {
            completion(<span class="hljs-type">TestDriveIntentResponse</span>(code: .failure, userActivity: <span class="hljs-literal">nil</span>))
            <span class="hljs-keyword">return</span>
        }

        <span class="hljs-comment">// Once the intent is validated, indicate that the intent is ready to handle.</span>
        completion(<span class="hljs-type">TestDriveIntentResponse</span>(code: .ready, userActivity: <span class="hljs-literal">nil</span>))
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">handle</span>(<span class="hljs-params">intent</span>: <span class="hljs-type">TestDriveIntent</span>, <span class="hljs-params">completion</span>: <span class="hljs-keyword">@escaping</span> (<span class="hljs-type">TestDriveIntentResponse</span>) -> <span class="hljs-type">Void</span>) {

        <span class="hljs-keyword">guard</span>
            <span class="hljs-keyword">let</span> order <span class="hljs-operator">=</span> <span class="hljs-type">TestDrive</span>(from: intent)
            <span class="hljs-keyword">else</span> {
                completion(<span class="hljs-type">TestDriveIntentResponse</span>(code: .failure, userActivity: <span class="hljs-literal">nil</span>))
                <span class="hljs-keyword">return</span>
        }

        <span class="hljs-comment">//  The handle method is also an appropriate place to handle payment via Apple Pay.</span>
        <span class="hljs-comment">//  A declined payment is another example of a failure case that could take advantage of a custom response.</span>

        <span class="hljs-type">TestDriveManager</span>.shared.bookTestDrive(order.car, duration: order.duration)

        <span class="hljs-comment">//  For the success case, we want to indicate a wait time to the user so that they know when their booking order will be ready.</span>
        <span class="hljs-comment">//  Ths sample uses a hardcoded value, but your implementation could use a time interval returned by your server.</span>
        completion(<span class="hljs-type">TestDriveIntentResponse</span>(code: .success, userActivity: <span class="hljs-literal">nil</span>))
    }
}
</code></pre>
<h4>Donating the intent</h4>
<p>Now that our app knows how to handle the intent, we are now ready to donate it to Siri, when the user takes the specific action. Siri will then suggest interactions to the user based on how often the user performs the actions and the user will later be able to access the shortcut himself through the shortcuts app or from Siri Settings.</p>
<p>In our <code>TestDriveManager</code> go ahead and declare <code>private func donateInteraction(for testDrive: TestDrive)</code>. We will use this function to donate the Interaction and we will call it before the <code>return</code> of our <code>bookTestDrive</code> function.</p>
<p>In order to donate the interaction we need to be able to generate an <code>INInteraction</code> from our <code>TestDrive</code>. To do so we must create a variable called <code>intent</code> in our <code>TestDrive</code> class.</p>
<p>Go ahead and add the following to our <code>TestDrive.swift</code>:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">extension</span> <span class="hljs-title class_">TestDrive</span> {
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> intent: <span class="hljs-type">TestDriveIntent</span> {
        <span class="hljs-keyword">let</span> intent <span class="hljs-operator">=</span> <span class="hljs-type">TestDriveIntent</span>()
        intent.car <span class="hljs-operator">=</span> <span class="hljs-type">INObject</span>(identifier: car.modelId.description, display: car.brand <span class="hljs-operator">+</span> <span class="hljs-string">" "</span> <span class="hljs-operator">+</span> car.modelName)
        intent.duration <span class="hljs-operator">=</span> duration <span class="hljs-keyword">as</span> <span class="hljs-type">NSNumber</span>

        <span class="hljs-keyword">let</span> options: [<span class="hljs-type">String</span>: <span class="hljs-type">String</span>] <span class="hljs-operator">=</span> [<span class="hljs-string">"fabricationYear"</span>: car.fabricationYear.description]
        <span class="hljs-keyword">var</span> intentOptions: [<span class="hljs-type">INObject</span>] <span class="hljs-operator">=</span> []
        options.keys.forEach { (key) <span class="hljs-keyword">in</span>
            <span class="hljs-keyword">let</span> value <span class="hljs-operator">=</span> options[key]
            intentOptions.append(<span class="hljs-type">INObject</span>(identifier: key, display: value<span class="hljs-operator">!</span>))
        }

        intent.options <span class="hljs-operator">=</span> intentOptions
        intent.suggestedInvocationPhrase <span class="hljs-operator">=</span> <span class="hljs-string">"Book a test drive for <span class="hljs-subst">\(car.brand)</span> <span class="hljs-subst">\(car.modelName)</span>"</span>

        <span class="hljs-keyword">return</span> intent
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">init?</span>(<span class="hljs-params">from</span> <span class="hljs-params">intent</span>: <span class="hljs-type">TestDriveIntent</span>) {
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> modelId <span class="hljs-operator">=</span> intent.car<span class="hljs-operator">?</span>.identifier,
            <span class="hljs-keyword">let</span> car <span class="hljs-operator">=</span> <span class="hljs-type">DataManager</span>.shared.findCar(<span class="hljs-type">Int</span>(modelId)<span class="hljs-operator">!</span>),
            <span class="hljs-keyword">let</span> duration <span class="hljs-operator">=</span> intent.duration
            <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span> }

        <span class="hljs-keyword">self</span>.<span class="hljs-keyword">init</span>(car: car, duration: <span class="hljs-type">Int</span>(truncating: duration))
    }
}

</code></pre>
<p>Our <code>public var intent: TestDriveIntent</code> is for us a computed property that will create a new intent of previously declared type <code>public var intent: TestDriveIntent</code> based on the information existing already in the <code>TestDrive</code> object. As well we have created a custom <code>init</code> for our class, so that we can convert the received <code>TestDriveIntent</code> to our <code>TestDrive</code> object at a later point.</p>
<p>Every time the user will book a test drive, we will donate an <code>INInteraction</code> to Siri so that she can suggest it to our user.</p>
<p>Now so that we can call our shortcut, we need to go to <code>Settings</code> -> <code>Siri &#x26; Search</code>. Here you will be able to find the donated shortcuts at the top of the view. When you tap our donated shortcut, Siri will invite you to create a custom phrase for it, suggesting as a reference point, our intent's <code>suggestedInvocationPhrase</code>. Record a custom phrase and voila, Siri is now ready for you.</p>
<p>Go ahead and ask Siri to book a test drive for you. This is how it should look like when you ask Siri to book a test drive for you.</p>
<p align="center"><img src="https://raw.githubusercontent.com/kjoneandrei/blog/iOS-siri-shortcuts-post/source/_posts-images/2018-07-16-SiriShortcuts/4.png"></p>
<p>Something doesn't look quite right here, and that is because our <code>SiriIntentExtensionUI</code> does not know to display anything to the user at the moment. For that to change we need to edit <code>IntentViewController</code>'s <code>func configureView(for parameters: Set&#x3C;INParameter>, of interaction: INInteraction, interactiveBehavior: INUIInteractiveBehavior, context: INUIHostedViewContext, completion: @escaping (Bool, Set&#x3C;INParameter>, CGSize) -> Void)</code> that will prepare the interaction to handle and display the corresponding UI.</p>
<p>In the <code>SiriIntentExtensionUI</code>'s <code>MainInterface.storyboard</code> you should be able to find <code>TestDriveOverview</code> and <code>TestDriveBookingCompleted</code>, two custom views I have already created and added for you to speed up the integration process. We will need to add these custom views as well in <code>TestDriveBookingCompleted</code>.</p>
<p>Now in <code>IntentViewController</code> add the following functions that will help us display the UI.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">/// - Returns: Desired size of the view</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">displayOverview</span>(<span class="hljs-params">for</span> <span class="hljs-params">testDrive</span>: <span class="hljs-type">TestDrive</span>, <span class="hljs-params">from</span> <span class="hljs-params">intent</span>: <span class="hljs-type">TestDriveIntent</span>) -> <span class="hljs-type">CGSize</span> {
    overviewView.configure(testDrive)

    view.addSubview(overviewView)

    <span class="hljs-keyword">let</span> width <span class="hljs-operator">=</span> <span class="hljs-keyword">self</span>.extensionContext<span class="hljs-operator">?</span>.hostedViewMaximumAllowedSize.width <span class="hljs-operator">??</span> <span class="hljs-number">320</span>
    <span class="hljs-keyword">let</span> frame <span class="hljs-operator">=</span> <span class="hljs-type">CGRect</span>(origin: .zero, size: <span class="hljs-type">CGSize</span>(width: width, height: <span class="hljs-number">100</span>))
    overviewView.frame <span class="hljs-operator">=</span> frame

    <span class="hljs-keyword">return</span> frame.size
}

<span class="hljs-comment">/// - Returns: Desired size of the view</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">displayOrderConfirmation</span>(<span class="hljs-params">for</span> <span class="hljs-params">testDrive</span>: <span class="hljs-type">TestDrive</span>, <span class="hljs-params">from</span> <span class="hljs-params">intent</span>: <span class="hljs-type">TestDriveIntent</span>, <span class="hljs-params">with</span> <span class="hljs-params">response</span>:<span class="hljs-type">TestDriveIntentResponse</span>) -> <span class="hljs-type">CGSize</span> {
    completedView.configure(testDrive)

    view.addSubview(completedView)

    <span class="hljs-keyword">let</span> width <span class="hljs-operator">=</span> <span class="hljs-keyword">self</span>.extensionContext<span class="hljs-operator">?</span>.hostedViewMaximumAllowedSize.width <span class="hljs-operator">??</span> <span class="hljs-number">320</span>
    <span class="hljs-keyword">let</span> frame <span class="hljs-operator">=</span> <span class="hljs-type">CGRect</span>(origin: .zero, size: <span class="hljs-type">CGSize</span>(width: width, height: <span class="hljs-number">150</span>))
    completedView.frame <span class="hljs-operator">=</span> frame

    <span class="hljs-keyword">return</span> frame.size
}
</code></pre>
<p>We are now ready do display our custom UI for the interaction. Inside our <code>configureView</code> function add the following <code>switch</code> just before the <code>completion</code> call. This will check the status of our intent and display the matching UI.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Different UIs can be displayed depending if the intent is in the confirmation phase or the handle phase.</span>
<span class="hljs-keyword">var</span> desiredSize <span class="hljs-operator">=</span> <span class="hljs-type">CGSize</span>.zero
<span class="hljs-keyword">switch</span> interaction.intentHandlingStatus {
    <span class="hljs-keyword">case</span> .ready:
        desiredSize <span class="hljs-operator">=</span> displayOverview(for: testDrive, from: intent)
    <span class="hljs-keyword">case</span> .success:
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> response <span class="hljs-operator">=</span> interaction.intentResponse <span class="hljs-keyword">as?</span> <span class="hljs-type">TestDriveIntentResponse</span> {
            desiredSize <span class="hljs-operator">=</span> displayOrderConfirmation(for: testDrive, from: intent, with: response)
        }
    <span class="hljs-keyword">default</span>:
        <span class="hljs-keyword">break</span>
}
</code></pre>
<p>Bring Siri up on screen again and call your custom phrase again. Now you should be able to see the custom UI which looks something like this:</p>
<p align="center"><img src="https://raw.githubusercontent.com/kjoneandrei/blog/iOS-siri-shortcuts-post/source/_posts-images/2018-07-16-SiriShortcuts/5.png"></p>
<p>The last thing for us to do before we have completely integrated shortcuts into our app is to handle the intent inside our app as well.</p>
<p>Inside your <code>AppDelegate.swift</code>, go ahead and edit the <code>func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool</code> as follows:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">application</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">application</span>: <span class="hljs-type">UIApplication</span>,
<span class="hljs-params">continue</span> <span class="hljs-params">userActivity</span>: <span class="hljs-type">NSUserActivity</span>,
<span class="hljs-params">restorationHandler</span>: <span class="hljs-keyword">@escaping</span> ([<span class="hljs-type">UIUserActivityRestoring</span>]<span class="hljs-operator">?</span>) -> <span class="hljs-type">Void</span>) -> <span class="hljs-type">Bool</span> {
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> intent <span class="hljs-operator">=</span> userActivity.interaction<span class="hljs-operator">?</span>.intent <span class="hljs-keyword">as?</span> <span class="hljs-type">TestDriveIntent</span> {
    <span class="hljs-comment">//handle intent</span>
        handle(intent)
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> userActivity.activityType <span class="hljs-operator">==</span> <span class="hljs-type">NSUserActivity</span>.<span class="hljs-type">ActivityTypes</span>.catalog {
    <span class="hljs-comment">//handle activity</span>
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
}

<span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">handle</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">intent</span>: <span class="hljs-type">TestDriveIntent</span>) {
    <span class="hljs-keyword">let</span> handler <span class="hljs-operator">=</span> <span class="hljs-type">TestDriveIntentHandler</span>()
    handler.handle(intent: intent) { (response) <span class="hljs-keyword">in</span>

    <span class="hljs-keyword">if</span> response.code <span class="hljs-operator">!=</span> .success {
        <span class="hljs-built_in">print</span>(<span class="hljs-string">"failed handling intent"</span>)
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> window <span class="hljs-operator">=</span> <span class="hljs-keyword">self</span>.window,
            <span class="hljs-keyword">let</span> rootViewController <span class="hljs-operator">=</span> window.rootViewController <span class="hljs-keyword">as?</span> <span class="hljs-type">UINavigationController</span>,
            <span class="hljs-keyword">let</span> vc <span class="hljs-operator">=</span> rootViewController.viewControllers.first <span class="hljs-keyword">as?</span> <span class="hljs-type">ViewController</span> <span class="hljs-keyword">else</span> {
                <span class="hljs-keyword">return</span>
            }
        <span class="hljs-keyword">let</span> storyboard <span class="hljs-operator">=</span> <span class="hljs-type">UIStoryboard</span>(name: <span class="hljs-string">"Main"</span>, bundle: .main)
        <span class="hljs-keyword">let</span> confirmationVC <span class="hljs-operator">=</span> storyboard.instantiateViewController(withIdentifier: <span class="hljs-string">"BookingConfirmationViewController"</span>) <span class="hljs-keyword">as!</span> <span class="hljs-type">BookingConfirmationViewController</span>
        confirmationVC.testDrive <span class="hljs-operator">=</span> <span class="hljs-type">TestDrive</span>(from: intent)
        vc.navigationController<span class="hljs-operator">?</span>.pushViewController(confirmationVC, animated: <span class="hljs-literal">true</span>)
        }
    }
}
</code></pre>
<p>We have added the <code>handle</code> function that will present to our user <code>BookingConfirmationViewController</code> if we have received a successful intent of type <code>TestDriveIntentHandler</code>.</p>
<h3>Part 6: Add Phrases to Siri</h3>
<p>Another feature to add to our app, is the option to add phrases to Siri directly from our app. This will make the experience for the user a better one.</p>
<p>For this to happen we will need to create a <code>VoiceShortcutsManager</code> that will have as purpose to update and add voice shortcuts to our intents.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> Foundation
<span class="hljs-keyword">import</span> Intents

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">VoiceShortcutsManager</span> {

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> voiceShortcuts: [<span class="hljs-type">INVoiceShortcut</span>] <span class="hljs-operator">=</span> []

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">init</span>() {
        updateVoiceShortcuts(completion: <span class="hljs-literal">nil</span>)
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">voiceShortcut</span>(<span class="hljs-params">for</span> <span class="hljs-params">order</span>: <span class="hljs-type">TestDrive</span>) -> <span class="hljs-type">INVoiceShortcut</span>? {
        <span class="hljs-keyword">let</span> voiceShorcut <span class="hljs-operator">=</span> voiceShortcuts.first { (voiceShortcut) -> <span class="hljs-type">Bool</span> <span class="hljs-keyword">in</span>
            <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> intent <span class="hljs-operator">=</span> voiceShortcut.__shortcut.intent <span class="hljs-keyword">as?</span> <span class="hljs-type">TestDriveIntent</span>,
                <span class="hljs-keyword">let</span> testDrive <span class="hljs-operator">=</span> <span class="hljs-type">TestDrive</span>(from: intent) <span class="hljs-keyword">else</span> {
                <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
            }
            <span class="hljs-keyword">return</span> order <span class="hljs-operator">==</span> testDrive
        }
        <span class="hljs-keyword">return</span> voiceShorcut
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">updateVoiceShortcuts</span>(<span class="hljs-params">completion</span>: (() -> <span class="hljs-type">Void</span>)<span class="hljs-operator">?</span>) {
        <span class="hljs-type">INVoiceShortcutCenter</span>.shared.getAllVoiceShortcuts { (voiceShortcutsFromCenter, error) <span class="hljs-keyword">in</span>
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> voiceShortcutsFromCenter <span class="hljs-operator">=</span> voiceShortcutsFromCenter <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> error <span class="hljs-operator">=</span> error {
                <span class="hljs-built_in">print</span>(<span class="hljs-string">"Failed to fetch voice shortcuts with error: <span class="hljs-subst">\(error.localizedDescription)</span>"</span>)
            }
            <span class="hljs-keyword">return</span>
        }
            <span class="hljs-keyword">self</span>.voiceShortcuts <span class="hljs-operator">=</span> voiceShortcutsFromCenter
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> completion <span class="hljs-operator">=</span> completion {
                completion()
            }
        }
    }
}
</code></pre>
<p>In our <code>CatalogViewController</code> go ahead and declare a <code>private lazy var voiceShortcutManager = VoiceShortcutsManager.init()</code>. Now let's allow the user set or edit a voice shortcut everytime it clicks on one of our catalog items. This is not an amazing UX but will make it easier for us to play.</p>
<p>In <code>didSelectRowAt</code> go ahead and add the following block:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> shortcut <span class="hljs-operator">=</span> voiceShortcutManager.voiceShortcut(for: testDrive) {
    <span class="hljs-keyword">let</span> editVoiceShortcutViewController <span class="hljs-operator">=</span> <span class="hljs-type">INUIEditVoiceShortcutViewController</span>(voiceShortcut: shortcut)
    editVoiceShortcutViewController.delegate <span class="hljs-operator">=</span> <span class="hljs-keyword">self</span>
    present(editVoiceShortcutViewController, animated: <span class="hljs-literal">true</span>, completion: <span class="hljs-literal">nil</span>)
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> shortcut <span class="hljs-operator">=</span> <span class="hljs-type">INShortcut</span>(intent: testDrive.intent) {
    <span class="hljs-keyword">let</span> addVoiceShortcutVC <span class="hljs-operator">=</span> <span class="hljs-type">INUIAddVoiceShortcutViewController</span>(shortcut: shortcut)
    addVoiceShortcutVC.delegate <span class="hljs-operator">=</span> <span class="hljs-keyword">self</span>
    present(addVoiceShortcutVC, animated: <span class="hljs-literal">true</span>, completion: <span class="hljs-literal">nil</span>)
}
</code></pre>
<p>Here we check if the <code>voiceShortcutManager</code> knows of a shortcut for our intent and allow the user to create or update the shortcut. As well we need to conform to the delegate methods for <code>INUIAddVoiceShortcutViewControllerDelegate</code> and <code>INUIEditVoiceShortcutViewControllerDelegate</code>, by adding the following:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">updateVoiceShortcuts</span>() {
    voiceShortcutManager.updateVoiceShortcuts(completion: <span class="hljs-literal">nil</span>)
    dismiss(animated: <span class="hljs-literal">true</span>, completion: <span class="hljs-literal">nil</span>)
}
</code></pre>
<pre><code class="hljs language-swift"><span class="hljs-comment">// MARK: - INUIAddVoiceShortcutViewControllerDelegate</span>

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">ViewController</span>: <span class="hljs-title class_">INUIAddVoiceShortcutViewControllerDelegate</span> {

    <span class="hljs-keyword">func</span> <span class="hljs-title function_">addVoiceShortcutViewController</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">controller</span>: <span class="hljs-type">INUIAddVoiceShortcutViewController</span>,
    <span class="hljs-params">didFinishWith</span> <span class="hljs-params">voiceShortcut</span>: <span class="hljs-type">INVoiceShortcut</span>?,
    <span class="hljs-params">error</span>: <span class="hljs-type">Error</span>?) {
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> error <span class="hljs-operator">=</span> error {
            <span class="hljs-built_in">print</span>(<span class="hljs-string">"error adding voice shortcut:<span class="hljs-subst">\(error.localizedDescription)</span>"</span>)
            <span class="hljs-keyword">return</span>
        }
        updateVoiceShortcuts()
    }

    <span class="hljs-keyword">func</span> <span class="hljs-title function_">addVoiceShortcutViewControllerDidCancel</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">controller</span>: <span class="hljs-type">INUIAddVoiceShortcutViewController</span>) {
        dismiss(animated: <span class="hljs-literal">true</span>, completion: <span class="hljs-literal">nil</span>)
    }
}

<span class="hljs-comment">// MARK: - INUIEditVoiceShortcutViewControllerDelegate</span>

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">ViewController</span>: <span class="hljs-title class_">INUIEditVoiceShortcutViewControllerDelegate</span> {

    <span class="hljs-keyword">func</span> <span class="hljs-title function_">editVoiceShortcutViewController</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">controller</span>: <span class="hljs-type">INUIEditVoiceShortcutViewController</span>,
    <span class="hljs-params">didUpdate</span> <span class="hljs-params">voiceShortcut</span>: <span class="hljs-type">INVoiceShortcut</span>?,
    <span class="hljs-params">error</span>: <span class="hljs-type">Error</span>?) {
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> error <span class="hljs-operator">=</span> error {
            <span class="hljs-built_in">print</span>(<span class="hljs-string">"error adding voice shortcut:<span class="hljs-subst">\(error.localizedDescription)</span>"</span>)
            <span class="hljs-keyword">return</span>
        }
        updateVoiceShortcuts()
    }

    <span class="hljs-keyword">func</span> <span class="hljs-title function_">editVoiceShortcutViewController</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">controller</span>: <span class="hljs-type">INUIEditVoiceShortcutViewController</span>,
    <span class="hljs-params">didDeleteVoiceShortcutWithIdentifier</span> <span class="hljs-params">deletedVoiceShortcutIdentifier</span>: <span class="hljs-type">UUID</span>) {
        updateVoiceShortcuts()
    }

    <span class="hljs-keyword">func</span> <span class="hljs-title function_">editVoiceShortcutViewControllerDidCancel</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">controller</span>: <span class="hljs-type">INUIEditVoiceShortcutViewController</span>) {
        dismiss(animated: <span class="hljs-literal">true</span>, completion: <span class="hljs-literal">nil</span>)
    }
}
</code></pre>
<p>Here the <code>updateVoiceShortcuts</code> will trigger a re-sync of the shortcuts in the system and dismiss <code>INUIEditVoiceShortcutViewController</code> or <code>INUIAddVoiceShortcutViewController</code>.</p>
<h2>Final notes</h2>
<p>You have now made it all the way to the end of this post, by the end of which, hopefully you will have a clear picture of how to implement and handle interaction to the Google Cast SDK.</p>
<p>Don't forget to download our final project and compare our results. :)</p>
<p><a href="https://github.com/nodes-ios/SiriShortcuts-Demo">Final Project</a></p>
<p>Hope to see you next time!</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/3iTRMP8Uq2k">Alvaro Reyes</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Getting started with Vapor 3]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2018/05/09/Getting-started-with-Vapor-3</link>
            <guid>https://engineering.monstar-lab.com/en/post/2018/05/09/Getting-started-with-Vapor-3</guid>
            <pubDate>Wed, 09 May 2018 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Vapor has been our go-to framework when we develop backend solutions at Nodes since January 2017. A lot has happened during the past year, especially when we saw Vapor 2 got released back in May. Our overall opinion is that Vapor 2 has been a mature and a fairly feature-rich framework we've enjoyed working with. That said, there is still room for improvement and therefore we have been watching the development of Vapor 3 with excitement.</p>
<p>A couple of days ago, <a href="https://medium.com/@codevapor/vapor-3-0-0-released-8356fa619a5d">Vapor released the next major version</a> and we've been following along since the betas came out to see how the changes will affect our daily work. We have developed and helped maintain around 25+ customer projects and 30+ open source packages so it's important for us to know the amount of changes needed in order to migrate these projects to Vapor 3. Some of these projects are already in the progress of being migrated and some of them still need to be worked on. Our progress of migrating our open source projects can be found on this <a href="https://github.com/nodes-vapor/readme/projects/2">GitHub project board</a>.</p>
<h2>Our focus areas</h2>
<p>Before diving in and setting up our first Vapor 3 project, let's reflect a bit on what we want to achieve with this research of Vapor 3. This should guide our exploration and give us some way to categorize our findings and compare it to Vapor 2. These are the following areas we will look into when testing out Vapor 3 for this post:</p>
<ul>
<li>Configuration: How do we setup providers and middlewares and how do we configure these?</li>
<li>Models: How do we create a model and how do we query it?</li>
<li>Routing: How do we set up routes for our project and how do these interface with our controllers?</li>
<li>Views: How do we set up views for our project?</li>
<li>Commands: How do we set up and run commands?</li>
</ul>
<h2>Getting started with Vapor 3</h2>
<p>Please note that some some of the dependencies used in this post (e.g. Leaf) is based on release candidates and things might change before we see the official releases.</p>
<h3>Setting up a project</h3>
<p>First, make sure you have the recent version of the Vapor toolbox:</p>
<pre><code class="hljs language-bash">brew upgrade vapor
</code></pre>
<p>And if you don't have the toolbox already installed, then run:</p>
<pre><code class="hljs language-bash">brew install vapor/tap/vapor
</code></pre>
<p>Also make sure you're running Swift 4.1 as this is now required in Vapor 3:</p>
<pre><code class="hljs language-bash">swift --version
</code></pre>
<p>With that in place, we can now create our Vapor 3 project. For this project, we're going to use the <a href="https://github.com/vapor/api-template"><code>api-template</code></a>:</p>
<pre><code class="hljs language-bash">vapor new vapor-3-test --template=api
</code></pre>
<p>As always, let's make the Vapor toolbox generate our Xcode project for us:</p>
<pre><code class="hljs language-bash"><span class="hljs-built_in">cd</span> vapor-3-test
vapor xcode
</code></pre>
<p>When it's done generating the project you should be able to run the project and when visiting <code>http://localhost:8080/hello</code> in your browser you should see the <code>Hello, world!</code> page.</p>
<p>When looking through the files in our new project, it's already worth noticing some differences. First up, we don't have <code>Droplet+Setup.swift</code> and <code>Config+Setup.swift</code>. They are instead replaced by:</p>
<ul>
<li><code>boot.swift</code>: The <code>boot</code> function is called after initialization but before it starts running, meaning that it is an ideal place for running code that should run each time the application starts. Speaking of "application", notice how the signature now names the <code>Droplet</code> an <code>Application</code> now. The signature also implies now that there's only going to be one <code>Application</code>.</li>
<li><code>configure.swift</code>: This is where you would configure and set up your application. You can basically think of this as a replacement for your old <code>Config+Setup.swift</code> file (although the way you set up is now different).</li>
<li><code>routes.swift</code>: This is where your "main" route collection is and this is where you would add individual routes or other route collections.</li>
</ul>
<h3>Configuration</h3>
<p>Let's go ahead and try and configure something. For this project, we'll use MySQL as this seems to be very common in Vapor projects. Let's add the MySQL package to our <code>Package.swift</code> file:</p>
<pre><code class="hljs language-swift">.package(url: <span class="hljs-string">"https://github.com/vapor/fluent-mysql.git"</span>, from: <span class="hljs-string">"3.0.0-rc"</span>),
</code></pre>
<p>Don't forget to add <code>FluentMySQL</code> to the list of dependencies for the <code>App</code> target:</p>
<pre><code class="hljs language-swift">.target(name: <span class="hljs-string">"App"</span>, dependencies: [<span class="hljs-string">"FluentMySQL"</span>, <span class="hljs-string">"Vapor"</span>]),
</code></pre>
<p>It's worth noticing that we're not adding a <code>mysql-provider</code> package. The concept still exists but <code>Provider</code>s do not come in separate packages anymore.</p>
<p>Replace the contents of <code>configure.swift</code> with the following to set our project up with MySQL:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> FluentMySQL
<span class="hljs-keyword">import</span> Vapor

<span class="hljs-keyword">public</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">configure</span>(
    <span class="hljs-keyword">_</span> <span class="hljs-params">config</span>: <span class="hljs-keyword">inout</span> <span class="hljs-type">Config</span>,
    <span class="hljs-keyword">_</span> <span class="hljs-params">env</span>: <span class="hljs-keyword">inout</span> <span class="hljs-type">Environment</span>,
    <span class="hljs-keyword">_</span> <span class="hljs-params">services</span>: <span class="hljs-keyword">inout</span> <span class="hljs-type">Services</span>
) <span class="hljs-keyword">throws</span> {
    <span class="hljs-comment">// Register providers</span>
    <span class="hljs-keyword">try</span> services.register(<span class="hljs-type">FluentMySQLProvider</span>())

    <span class="hljs-comment">// Configure and register a MySQL database</span>
    <span class="hljs-keyword">let</span> config <span class="hljs-operator">=</span> <span class="hljs-type">MySQLDatabaseConfig</span>(
        username: <span class="hljs-string">"vapor3"</span>,
        password: <span class="hljs-string">"password"</span>,
        database: <span class="hljs-string">"vapor3"</span>
    )
    <span class="hljs-keyword">let</span> mysql <span class="hljs-operator">=</span> <span class="hljs-type">MySQLDatabase</span>(config: config)
    <span class="hljs-keyword">var</span> databases <span class="hljs-operator">=</span> <span class="hljs-type">DatabasesConfig</span>()
    databases.add(database: mysql, as: .mysql)
    services.register(databases)
}
</code></pre>
<p>Let's try and go through the different steps that are happening in the above code:</p>
<ul>
<li>We register the <code>FluentMySQLProvider</code> which takes care of setting up database connections and migrations.</li>
<li>We create a <code>MySQLDatabaseConfig</code> with our MySQL connection settings.</li>
<li>We create a <code>MySQLDatabase</code> using our config.</li>
<li>We create a database config object and add our database to it specifying that we will identify this as the MySQL database (<code>as: .mysql</code>) and register it with the services object.</li>
</ul>
<p>It takes a little to grasp the notion of "Services" and how you now register providers and use configurations. We are excited to see that there are <strong>no more json configs</strong>.</p>
<h3>Models</h3>
<p>Vapor 2 has been pretty boilerplate-heavy when it comes to models. This is something we have tried to solve using Soucery as described in <a href="/en2017-12-20-vapor-code-generation-with-sourcery">this blog post</a>. We've been pretty interested in seeing how this would change with Vapor 3 since the framework is now able to leverage on the <a href="https://developer.apple.com/documentation/swift/codable"><code>Codeable</code></a> features of Swift 4.</p>
<p>Let's start out by creating a simple model conforming to <code>Codeable</code>:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Post</span>: <span class="hljs-title class_">Codable</span> {
    <span class="hljs-keyword">var</span> id: <span class="hljs-type">Int</span>?
    <span class="hljs-keyword">let</span> title: <span class="hljs-type">String</span>
    <span class="hljs-keyword">let</span> body: <span class="hljs-type">String</span>

    <span class="hljs-keyword">init</span>(<span class="hljs-params">id</span>: <span class="hljs-type">Int</span>? <span class="hljs-operator">=</span> <span class="hljs-literal">nil</span>, <span class="hljs-params">title</span>: <span class="hljs-type">String</span>, <span class="hljs-params">body</span>: <span class="hljs-type">String</span>) {
        <span class="hljs-keyword">self</span>.id <span class="hljs-operator">=</span> id
        <span class="hljs-keyword">self</span>.title <span class="hljs-operator">=</span> title
        <span class="hljs-keyword">self</span>.body <span class="hljs-operator">=</span> body
    }
}
</code></pre>
<p>At this point, there's nothing Vapor specific. It's great to see how we are able to keep our raw models independent of the framework and instead use extensions to add the Vapor specific functionality. To make our <code>Post</code> a <code>MySQLModel</code> do:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> FluentMySQL

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">Post</span>: <span class="hljs-title class_">MySQLModel</span> {}
</code></pre>
<p>That's it! For brevity, you can simply conform your model to <code>MySQLModel</code> directly since it inherits <code>Codable</code>.</p>
<h4>Migrations</h4>
<pre><code class="hljs language-swift"><span class="hljs-keyword">extension</span> <span class="hljs-title class_">Post</span>: <span class="hljs-title class_">Migration</span> {}
</code></pre>
<p>That's pretty much it. Using <code>Codable</code> Vapor is now able to convert your Swift types into database field types. To make sure your migration is being run you'll have to register a migration configuration in your <code>configure.swift</code>:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">var</span> migrations <span class="hljs-operator">=</span> <span class="hljs-type">MigrationConfig</span>()
migrations.add(model: <span class="hljs-type">Post</span>.<span class="hljs-keyword">self</span>, database: .mysql)
services.register(migrations)
</code></pre>
<p>This will obviously not be a solution to all cases, and most of the time you will probably have specific requirements for your database fields. One example could be to lower the length of your <code>VARCHAR</code> since a <code>String</code> in Swift will be turned into a 255 characters long VARCHAR, which might not be what you want if you want <a href="https://github.com/nodes-vapor/readme/blob/master/Documentation/how-to-support-emojis.md">to support emojis</a>. To handle your migration manually, you can instead specify the <code>prepare</code> function yourself:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> Vapor

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">Post</span>: <span class="hljs-title class_">Migration</span> {
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">prepare</span>(<span class="hljs-params">on</span> <span class="hljs-params">connection</span>: <span class="hljs-type">MySQLConnection</span>) -> <span class="hljs-type">Future</span>&#x3C;<span class="hljs-type">Void</span>> {
        <span class="hljs-keyword">return</span> <span class="hljs-type">MySQLDatabase</span>.create(<span class="hljs-keyword">self</span>, on: connection) { builder <span class="hljs-keyword">in</span>
            <span class="hljs-keyword">try</span> builder.field(type: <span class="hljs-type">Int</span>.mySQLColumnDefinition, for: \.id, isIdentifier: <span class="hljs-literal">true</span>)
            <span class="hljs-keyword">try</span> builder.field(for: \.title)
            builder.field(type: .varChar(length: <span class="hljs-number">191</span>), for: \.body)
        }
    }
}
</code></pre>
<p>Notice how we're using Swift 4's typesafe KeyPath syntax (<code>\.</code>) for referring to our <code>Post</code>'s properties. We can omit the type (so not <code>\Post.id</code>) because <code>builder</code> is already aware of our model type.
Another thing to mention here is that we have to specify that the <code>id</code> field is an identifier. Because there is no variant of the <code>field</code> function that accepts only <code>KeyPath</code> and <code>isIdentifier</code> we also have to specify the column type (ie. <code>Int.mySQLColumnDefinition</code>).</p>
<h3>Routing</h3>
<p>Let's go through some of common use cases for dealing with a <code>Model</code>:</p>
<ul>
<li>Retrieving all instances of a model.</li>
<li>Retrieving one specific instance of a model using a unique identifier.</li>
<li>Creating a new instance of a model.</li>
<li>Updating an instance of a model.</li>
<li>Deleting an instance of a model.</li>
</ul>
<p>Before we can begin creating these routes we need a router so let's add the following to the <code>configure</code> function in <code>configure.swift</code>:</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Register router and routes</span>
<span class="hljs-keyword">let</span> router <span class="hljs-operator">=</span> <span class="hljs-type">EngineRouter</span>.default()
<span class="hljs-keyword">try</span> routes(router)
services.register(router, as: <span class="hljs-type">Router</span>.<span class="hljs-keyword">self</span>)
</code></pre>
<p>After creating the router we pass it into the <code>routes</code> function defined in <code>routes.swift</code> before finally registering it with the services object.</p>
<p>Replace the contents of <code>routes.swift</code> with:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> Vapor

<span class="hljs-keyword">func</span> <span class="hljs-title function_">routes</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">router</span>: <span class="hljs-type">Router</span>) <span class="hljs-keyword">throws</span> {
    <span class="hljs-keyword">let</span> postsGroup <span class="hljs-operator">=</span> router.grouped(<span class="hljs-string">"posts"</span>)
    <span class="hljs-keyword">let</span> postController <span class="hljs-operator">=</span> <span class="hljs-type">PostController</span>()
}
</code></pre>
<p>For now, let's just create an empty controller:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">PostController</span> {

}
</code></pre>
<p>We'll want to be able to return (arrays of) <code>Post</code> instances in our routes. Conform your model to <code>Content</code> to enable this:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">extension</span> <span class="hljs-title class_">Post</span>: <span class="hljs-title class_">Content</span> {}
</code></pre>
<blockquote>
<p>The default encoding for <code>Content</code> is JSON. This is due to the fact that <code>defaultMediaType</code> for Content is set to <code>.json</code>:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> defaultMediaType: <span class="hljs-type">MediaType</span> {
    <span class="hljs-keyword">return</span> .json
}
</code></pre>
</blockquote>
<p>Lastly, we'll want to make our <code>Post</code> model conform to <code>Parameter</code> so that it can be used as a type-safe parameter for our routes. Go ahead and add this to <code>Post.swift</code>:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">extension</span> <span class="hljs-title class_">Post</span>: <span class="hljs-title class_">Parameter</span> {}
</code></pre>
<h4>Retrieving all instances</h4>
<p>To return all posts created (e.g. by requesting <code>GET /posts</code>), we're going to query them like this in our <code>PostController</code>:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">all</span>(<span class="hljs-params">req</span>: <span class="hljs-type">Request</span>) <span class="hljs-keyword">throws</span> -> <span class="hljs-type">Future</span>&#x3C;[<span class="hljs-type">Post</span>]> {
    <span class="hljs-keyword">return</span> <span class="hljs-type">Post</span>.query(on: req).all()
}
</code></pre>
<p>To link it up to our route group we add the following to our <code>routes</code> function:</p>
<pre><code class="hljs language-swift">postsGroup.get(use: postController.all)
</code></pre>
<p>There's a couple of things to notice in the first snippet when we compare it to Vapor 2.</p>
<p>First is that we're passing the <code>Request</code> to the <code>query</code> function. This is because <code>Request</code> is a <code>Container</code> which has access to the registered services, including the database. This makes it very explicit for who is responsible for performing the lookup. Without having tried it yet, this should help us make tests as well since we should potentially be able mock the database connection and instead of hitting a database, we could return in-memory objects created in our tests.</p>
<p>Next, there's the return type which is now <code>Future&#x3C;[Post]></code> instead of <code>[Post]</code> as you might have expected coming from Vapor 2. <a href="https://docs.vapor.codes/3.0/async/getting-started/">Async, Streams, Futures and Reactive Programming</a> are central topics in Vapor 3 in order to increase the performance of the framework due to their non-blocking nature and it will change the way we work. Without going too much into details, one way of thinking of the concepts could be:</p>
<ul>
<li>Futures: Values that at some point in time will exist. The future part is a wrapper that allows us to continue the work we want to do when the value arrives.</li>
<li>Streams: If a future produces one value when it's done executing, a stream can return an endless number of values. Think of it as generally always running which might produce values once in a while.</li>
<li>Reactive Programming: Related to futures and streams are reactive programming. It's a pattern for dealing with these types of data, usually using functional programming. You can think of it as a way of transforming or dealing with data when it comes in through a future or a stream.</li>
</ul>
<p>If you've worked with any of the reactive programming frameworks, these concepts introduced in Vapor 3 should be familiar.</p>
<h4>Retrieving one specific instance</h4>
<p>To return one specific post (e.g. by requesting <code>GET /posts/:id</code>), we're going to request that instance like this:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">specific</span>(<span class="hljs-params">req</span>: <span class="hljs-type">Request</span>) <span class="hljs-keyword">throws</span> -> <span class="hljs-type">Future</span>&#x3C;<span class="hljs-type">Post</span>> {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> req.parameters.next(<span class="hljs-type">Post</span>.<span class="hljs-keyword">self</span>)
}
</code></pre>
<p>This is very similar to what we've used to in Vapor 2 with the exception that fetching a parameter from the request now returns a <code>Future</code>.</p>
<p>The accompanying entry in <code>routes.swift</code> looks like this:</p>
<pre><code class="hljs language-swift">postsGroup.get(<span class="hljs-type">Post</span>.parameter, use: postController.specific)
</code></pre>
<h4>Creating a new instance</h4>
<p>To create a new post (e.g. by requesting <code>POST /posts</code>), we're going to do it like this:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">create</span>(<span class="hljs-params">req</span>: <span class="hljs-type">Request</span>) <span class="hljs-keyword">throws</span> -> <span class="hljs-type">Future</span>&#x3C;<span class="hljs-type">Post</span>> {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> req.content.decode(<span class="hljs-type">Post</span>.<span class="hljs-keyword">self</span>).save(on: req)
}
</code></pre>
<p>A couple of things have changed here. In Vapor 2 you might have used <code>req.data</code>, <code>req.form</code> or <code>req.json</code> for retrieving the body of a request, but in Vapor 3 this is now contained in a <code>ContentContainer</code> on the request. Next we can use <code>decode</code> to transform the body into the expected type using the <code>Decodable</code> protocol from Swift 4. Remember that our <code>Post</code> model conforms to <code>Codable</code> in order for this to work.</p>
<p>The POST route can be added as follows:</p>
<pre><code class="hljs language-swift">postsGroup.post(use: postController.create)
</code></pre>
<h4>Updating an instance</h4>
<p>To update an existing post (e.g. by requesting <code>PATCH /posts/:id</code>), we're going to do it like this:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">update</span>(<span class="hljs-params">req</span>: <span class="hljs-type">Request</span>) <span class="hljs-keyword">throws</span> -> <span class="hljs-type">Future</span>&#x3C;<span class="hljs-type">Post</span>> {
    <span class="hljs-keyword">let</span> post <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> req.parameter(<span class="hljs-type">Post</span>.<span class="hljs-keyword">self</span>)
    <span class="hljs-keyword">let</span> content <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> req.content.decode(<span class="hljs-type">Post</span>.<span class="hljs-keyword">self</span>)

    <span class="hljs-keyword">return</span> flatMap(to: <span class="hljs-type">Post</span>.<span class="hljs-keyword">self</span>, post, content) { (post, content) <span class="hljs-keyword">in</span>
        post.title <span class="hljs-operator">=</span> content.title
        post.body <span class="hljs-operator">=</span> content.body

        <span class="hljs-keyword">return</span> post.save(on: req)
    }
}
</code></pre>
<p>You could choose to do something similar to the code snippet for creating an instance, since Vapor will update the instance if the payload contains an <code>id</code> field. For this example, we've chosen to make it more explicit what is going on in the update process. In the above code, we're fetching the post that was specified in the request, we're decoding the post model that was giving in the body of the request and last we're updating the currently saved post. Note how we're using a static variant of <code>flatMap</code> that takes in two futures. We need this variant since getting the <code>Post</code> from the parameter and decoding the <code>Post</code> from request content both returns futures and we want to do a transformation once their values has arrived. Finally we use a <code>flatMap</code> since we are returning a <code>Future</code> in the closure when calling <code>save</code>.</p>
<h4>Deleting an instance</h4>
<p>To delete an existing post (e.g. by requesting <code>DELETE /posts/:id</code>), we're going to do it like this:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">delete</span>(<span class="hljs-params">req</span>: <span class="hljs-type">Request</span>) <span class="hljs-keyword">throws</span> -> <span class="hljs-type">Future</span>&#x3C;<span class="hljs-type">HTTPResponse</span>> {
    <span class="hljs-keyword">let</span> post <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> req.parameters.next(<span class="hljs-type">Post</span>.<span class="hljs-keyword">self</span>)
    <span class="hljs-keyword">return</span> post.flatMap(to: <span class="hljs-type">HTTPResponse</span>.<span class="hljs-keyword">self</span>) { post <span class="hljs-keyword">in</span>
        <span class="hljs-keyword">return</span> post.delete(on: req).transform(to: <span class="hljs-type">HTTPResponse</span>(status: .ok))
    }
}
</code></pre>
<p>The above code is very similar to how we deal with updating a post, with the difference being that instead of returning the updated post, we're returning a <code>HTTPResponse</code> with the HTTP code 200.</p>
<h3>Views</h3>
<p>In order to render views using Leaf, we need to pull in the dependency. Go ahead and update <code>Package.swift</code> by adding the following:</p>
<pre><code class="hljs language-swift">.package(url: <span class="hljs-string">"https://github.com/vapor/leaf.git"</span>, from: <span class="hljs-string">"3.0.0-rc"</span>),
</code></pre>
<p>Next, register the provider in <code>configure.swift</code>:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">try</span> services.register(<span class="hljs-type">LeafProvider</span>())
</code></pre>
<h4>Rendering a view</h4>
<p>To render a simple a view, try and add a file called <code>hello.leaf</code> to your <code>Resources/Views</code> folder with the following content:</p>
<pre><code class="hljs language-html">Hello #(name)
</code></pre>
<p>To render this template file, we will have to do the folowing:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">renderHello</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">req</span>: <span class="hljs-type">Request</span>) <span class="hljs-keyword">throws</span> -> <span class="hljs-type">Future</span>&#x3C;<span class="hljs-type">View</span>> {
    <span class="hljs-keyword">let</span> leaf <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> req.make(<span class="hljs-type">LeafRenderer</span>.<span class="hljs-keyword">self</span>)
    <span class="hljs-keyword">let</span> context: [<span class="hljs-type">String</span>: <span class="hljs-type">String</span>] <span class="hljs-operator">=</span> [<span class="hljs-string">"name"</span>: <span class="hljs-string">"VaporMan"</span>]
    <span class="hljs-keyword">return</span> leaf.render(<span class="hljs-string">"hello"</span>, context)
}
</code></pre>
<p>Opening the route in your browser should output <code>Hello VaporMan</code>.</p>
<h4>Rendering a view with persisted data</h4>
<p>Most of the time you need to fetch something from the database using Fluent and then render a view using that data. Earlier we looked at how we can fetch and return a <code>Post</code> in a JSON format by doing:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">specific</span>(<span class="hljs-params">req</span>: <span class="hljs-type">Request</span>) <span class="hljs-keyword">throws</span> -> <span class="hljs-type">Future</span>&#x3C;<span class="hljs-type">Post</span>> {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> req.parameters.next(<span class="hljs-type">Post</span>.<span class="hljs-keyword">self</span>)
}
</code></pre>
<p>Now let's have a look at how we can render a view displaying a single post. First thing is to create a new leaf file with the content:</p>
<pre><code class="hljs language-html"><span class="hljs-tag">&#x3C;<span class="hljs-name">h1</span>></span>#(title)<span class="hljs-tag">&#x3C;/<span class="hljs-name">h1</span>></span>
<span class="hljs-tag">&#x3C;<span class="hljs-name">p</span>></span>#(body)<span class="hljs-tag">&#x3C;/<span class="hljs-name">p</span>></span>
</code></pre>
<p>The file will simply output the title and the body of the blog post. The function for rendering the view looks like this:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">renderSpecific</span>(<span class="hljs-params">req</span>: <span class="hljs-type">Request</span>) <span class="hljs-keyword">throws</span> -> <span class="hljs-type">Future</span>&#x3C;<span class="hljs-type">Post</span>> {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> req.parameters.next(<span class="hljs-type">Post</span>.<span class="hljs-keyword">self</span>)
        .flatMap(to: <span class="hljs-type">View</span>.<span class="hljs-keyword">self</span>) { post <span class="hljs-keyword">in</span>
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> req.make(<span class="hljs-type">LeafRenderer</span>.<span class="hljs-keyword">self</span>).render(<span class="hljs-string">"post"</span>, post)
    }
}
</code></pre>
<p>Instead of returning the <code>Post</code> directly as in the previous function, this one transforms it to a <code>View</code> using <code>flatMap</code> since rendering a view returns another <code>Future</code>.</p>
<h3>Commands</h3>
<p>Creating a command is fairly simple and a bit more structured than in Vapor 2. For this example, let's have a look at how we can make a command that seeds a post. For a command, there's basically four parts you need to consider:</p>
<ul>
<li>Arguments: Required input for the command.</li>
<li>Options: Optional input for the command.</li>
<li>Help: Text to aid the user in how to use the command.</li>
<li>Body: The actual work we want to perform when running the command.</li>
</ul>
<p>Let's start by creating a simple command and make it comform to <code>Command</code>:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">public</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">PostCommand</span>: <span class="hljs-title class_">Command</span> {

}
</code></pre>
<h4>Arguments</h4>
<p>To satisfy the conformance to <code>Command</code>, the first step would be to supply the available arguments for our command. Remember, that this list of commands are required in order to run the command. Go ahead and add the following to your <code>PostCommand</code>:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> arguments: [<span class="hljs-type">CommandArgument</span>] {
    <span class="hljs-keyword">return</span> [.argument(name: <span class="hljs-string">"title"</span>, help: [<span class="hljs-string">"Title of the post"</span>])]
}
</code></pre>
<p>This will make our <code>PostCommand</code> require a title to be passed in every time the command is run.</p>
<h4>Options</h4>
<p>Options are very similar to arguments:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> options: [<span class="hljs-type">CommandOption</span>] {
    <span class="hljs-keyword">return</span> [
        .value(name: <span class="hljs-string">"body"</span>, short: <span class="hljs-string">"b"</span>, default: <span class="hljs-string">"This is a seeded post"</span>, help: [<span class="hljs-string">"Change post's body"</span>]),
    ]
}
</code></pre>
<p>Options are optional and the command will run without these options being defined.</p>
<h4>Help</h4>
<p>We also need to supply a help message to describe what our command does, this is done like this:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> help: [<span class="hljs-type">String</span>] {
    <span class="hljs-keyword">return</span> [<span class="hljs-string">"Seeds a post."</span>]
}
</code></pre>
<p>This help text will be included when running our command with <code>--help</code>.</p>
<h4>Command body</h4>
<p>The last step is to provide the actual work we want to perform in our command. For this simple command, we will simply create a <code>Post</code> based on the input:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">public</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">run</span>(<span class="hljs-params">using</span> <span class="hljs-params">context</span>: <span class="hljs-type">CommandContext</span>) <span class="hljs-keyword">throws</span> -> <span class="hljs-type">Future</span>&#x3C;<span class="hljs-type">Void</span>> {
    <span class="hljs-keyword">let</span> title <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> context.argument(<span class="hljs-string">"title"</span>)
    <span class="hljs-keyword">let</span> body <span class="hljs-operator">=</span> context.options[<span class="hljs-string">"body"</span>] <span class="hljs-operator">??</span> <span class="hljs-string">"Seeded post"</span>

    <span class="hljs-keyword">return</span> context.container.withNewConnection(to: .mysql) { db <span class="hljs-keyword">in</span>
        <span class="hljs-keyword">let</span> post <span class="hljs-operator">=</span> <span class="hljs-type">Post</span>(title: title, body: body)
        <span class="hljs-keyword">return</span> post.save(on: db).transform(to: ())
    }
}
</code></pre>
<p>Let's walk through the steps we take in the above snippet:</p>
<ul>
<li>We pull out <code>title</code> from the arguments. Since arguments are required, we don't have to unwrap the value.</li>
<li>We pull out the <code>body</code> option from the dictionary of options and supply a fallback. Another approach would be to do <code>try context.requireOption("body")</code> since there's already a fallback defined in the option. However doing this would mean that we would need to always pass in the option flag (in this case <code>-b</code>) but we can omit the value for the option if we want to. A way to think of it is to consider the value of the option to be optional, and not the existence of the option.</li>
<li>Lastly we connect to our MySQL database and we create and save the post.</li>
</ul>
<h4>Running our command</h4>
<p>With the command in place, the last thing to do is to register it in our <code>configure.swift</code>:</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Commands</span>
<span class="hljs-keyword">var</span> commandConfig <span class="hljs-operator">=</span> <span class="hljs-type">CommandConfig</span>.default()
commandConfig.use(<span class="hljs-type">PostCommand</span>(), as: <span class="hljs-string">"post-seeder"</span>)
services.register(commandConfig)
</code></pre>
<p>We can now run our command:</p>
<ul>
<li><code>swift run Run post-seeder --help</code>: Outputs the help text</li>
<li><code>swift run Run post-seeder "My post"</code>: Creates a post using the title and the default body. The body will be the one defined in <code>run</code>.</li>
<li><code>swift run Run post-seeder "My post" -b</code>: Creates a post using the title and the default body. The body will be the one defined in the option.</li>
<li><code>swift run Run post-seeder "My post" -b "My body"</code>: Creates a post using the title and body.</li>
</ul>
<h2>Conclusion</h2>
<p>Having worked with Vapor 3 for a couple of weeks we think that Vapor has come a long way. There's a steeper learning curve to the framework compared to older versions of the framework mostly due to the async principles adopted in Vapor 3. However, once you get past that, then the framework feels more mature and more fun to use compared to older versions. One example is the fact that using <code>Codable</code> heavily reduces boilerplate and introduces some more safety in your code. Overall we're excited about this major version and we can't wait to migrate all of our packages and work on more projects using this new version.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/k_T9Zj3SE8k">Simon Abrams</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Google Cast]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2018/03/20/GoogleCast</link>
            <guid>https://engineering.monstar-lab.com/en/post/2018/03/20/GoogleCast</guid>
            <pubDate>Tue, 20 Mar 2018 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Today, on the App Store, you are able to find a rapidly increasing number of apps that will allow their users to watch video content. There might come a time when you will have to develop such an app, and functionality like Google Cast and AirPlay is not only nice to have, but also expected by the end user.</p>
<p>In this post we will tackle together the steps required in order to allow your users casting videos to a Google Cast device</p>
<h2>Let's get started</h2>
<p>In order to be able to solely focus on integrating the Google Cast SDK to our app, I have created a demo project that you can use in order to follow along.</p>
<p>The project containts:</p>
<ul>
<li>Added Google Cast SDK via <code>CocoaPods</code> <a href="https://developers.google.com/cast/docs/ios_sender_setup#xcode_setup">Setup Instructions</a></li>
<li>An <code>AVPlayer</code> that allows users to play video content locally</li>
</ul>
<p>Furthermore you will need to register your application to Google Cast SDK Developer Console at: <a href="https://cast.google.com/publish/#/signup">https://cast.google.com/publish/#/signup</a></p>
<p>You can download the demo project here: <a href="https://github.com/nodes-ios/GoogleCast-Demo/tree/cast-demo-start">Starter Project</a></p>
<h3>Part 1: Connecting to a Google Cast device</h3>
<p>To establish a connection with a device we need our app to:</p>
<ul>
<li>initialise the <code>GCKCastContext</code></li>
<li>create a <code>GCKSessionManager</code></li>
<li>add the <code>GCKUICastButton</code></li>
</ul>
<p>A recommendable way of achieving this, is to create a <code>CastManager</code>, a Singleton instance in our case.</p>
<p>In our manager we now create <code>func initialise()</code> where we will do the initial setup. Here we will call <code>func initialiseContext()</code> and <code>createSessionManager()</code></p>
<p>Go ahead and declare your <code>sessionManager</code>, which will be in charge of handling the connection to a device, and create the functions as follows:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> sessionManager: <span class="hljs-type">GCKSessionManager</span>!

<span class="hljs-keyword">func</span> <span class="hljs-title function_">initialise</span>() {
    initialiseContext()
    createSessionManager()
}

<span class="hljs-comment">//creates the GCKSessionManager</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">createSessionManager</span>() {
    sessionManager <span class="hljs-operator">=</span> <span class="hljs-type">GCKCastContext</span>.sharedInstance().sessionManager
}

<span class="hljs-comment">//initialises the GCKCastContext</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">initialiseContext</span>() {
    <span class="hljs-comment">//application Id from the registered application</span>
    <span class="hljs-keyword">let</span> options <span class="hljs-operator">=</span> <span class="hljs-type">GCKCastOptions</span>(discoveryCriteria: <span class="hljs-type">GCKDiscoveryCriteria</span>.<span class="hljs-keyword">init</span>(applicationID: <span class="hljs-string">"B21B9F3A"</span>))
    <span class="hljs-type">GCKCastContext</span>.setSharedInstanceWith(options)
    <span class="hljs-type">GCKCastContext</span>.sharedInstance().useDefaultExpandedMediaControls <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>
}
</code></pre>
<p>Now in your AppDelegate's <code>didFinishLaunchingWithOptions</code> call <code>CastManager.shared.initialise()</code></p>
<p>The last thing needed is a <code>GCKUICastButton</code>. For this we need to create a <code>Castable</code> protocol that contains a <code>UIBarButtonItem</code> for our <code>UINavigationBar</code>. This will allow us to get the <code>GCKUICastButton</code> and add it to our <code>UINavigationBar</code> with just one line of code.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> UIKit
<span class="hljs-keyword">import</span> GoogleCast

<span class="hljs-keyword">protocol</span> <span class="hljs-title class_">Castable</span> {
    <span class="hljs-keyword">var</span> googleCastBarButton: <span class="hljs-type">UIBarButtonItem</span>! { <span class="hljs-keyword">get</span> }
}

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">Castable</span> <span class="hljs-title class_">where</span> <span class="hljs-title class_">Self</span>:<span class="hljs-title class_">UIViewController</span> {
    <span class="hljs-keyword">var</span> googleCastBarButton: <span class="hljs-type">UIBarButtonItem</span>! {
        <span class="hljs-keyword">let</span> castButton <span class="hljs-operator">=</span> <span class="hljs-type">GCKUICastButton</span>(frame: <span class="hljs-type">CGRect</span>(x: <span class="hljs-number">0</span>, y: <span class="hljs-number">0</span>, width: <span class="hljs-number">24</span>, height: <span class="hljs-number">24</span>))
        castButton.tintColor <span class="hljs-operator">=</span> .white
        <span class="hljs-keyword">return</span> <span class="hljs-type">UIBarButtonItem</span>(customView: castButton)
    }
}

</code></pre>
<p>Now go to our ViewControllers and add the following line inside <code>viewDidLoad</code>:</p>
<pre><code class="hljs language-swift">navigationItem.rightBarButtonItems <span class="hljs-operator">=</span> [googleCastBarButton]
</code></pre>
<p>You should be able at this stage to run the app and tap the <code>GCKUICastButton</code> to connect to a nearby Google Cast device. Just make sure you are on the same network as your device. When connected you should see the <code>GCKUICastButton</code> change to its active state.</p>
<h3>Part 2: Listening to <code>GCKSession</code> changes</h3>
<p>Now that we are able to connect to a device, we need to inform our player that a connection is established and play the video content on the receiving device instead.</p>
<p>The steps required for this process are:</p>
<ul>
<li>create an enum called <code>CastSessionStatus</code> with possible <code>GCKSession</code> states</li>
<li>add a <code>GCKSessionManagerListener</code></li>
<li>inform the player about the <code>GCKSession</code> changes</li>
</ul>
<p>First we will create the <code>CastSessionStatus</code> enum. Here we will add all posible states for our <code>GCKSession</code>.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">enum</span> <span class="hljs-title class_">CastSessionStatus</span> {
    <span class="hljs-keyword">case</span> started
    <span class="hljs-keyword">case</span> resumed
    <span class="hljs-keyword">case</span> ended
    <span class="hljs-keyword">case</span> failedToStart
    <span class="hljs-keyword">case</span> alreadyConnected
}
</code></pre>
<p>In your <code>CastManager</code> declare a closure called <code>sessionStatusListener</code> that will be used to send changes back to the player. In order to initialise this we need to create the following:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> sessionStatusListener: ((<span class="hljs-type">CastSessionStatus</span>, <span class="hljs-type">String</span>) -> <span class="hljs-type">Void</span>)<span class="hljs-operator">?</span>

<span class="hljs-keyword">func</span> <span class="hljs-title function_">addSessionStatusListener</span>(<span class="hljs-params">listener</span>: <span class="hljs-keyword">@escaping</span> (<span class="hljs-type">CastSessionStatus</span>, <span class="hljs-type">String</span>) -> <span class="hljs-type">Void</span>) {
    <span class="hljs-keyword">self</span>.sessionStatusListener <span class="hljs-operator">=</span> listener
}
</code></pre>
<p>Inside our <code>Player</code> class add the following function and call it in the <code>init(frame: CGRect)</code>:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">listenForCastConnection</span>() {
    <span class="hljs-keyword">let</span> sessionStatusListener: (<span class="hljs-type">CastSessionStatus</span>) -> <span class="hljs-type">Void</span> <span class="hljs-operator">=</span> { status <span class="hljs-keyword">in</span>
        <span class="hljs-built_in">print</span>(status)
    }

    <span class="hljs-type">CastManager</span>.shared.addSessionStatusListener(listener: sessionStatusListener)
}
</code></pre>
<p>Here we create the closure and pass it to our <code>CastManager</code>. In the next step we will use this to handle the connection states inside the <code>Player</code>.</p>
<h3>Part 3: Play video on receiving device</h3>
<p>Now that we have an established connection and our player is aware that a connection is established, we can play a <code>MediaItem</code> on our receiving device.</p>
<p>To do so we need to cover the following steps:</p>
<ul>
<li>create <code>GCKMediaMetadata</code></li>
<li>create <code>GCKMediaInformation</code></li>
<li>start video on cast</li>
</ul>
<p>In our <code>CastManager</code> add the following functions that together help create a <code>GCKMediaInformation</code>, containing <code>GCKMediaMetadata</code>, which will be send to the receiving device.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">buildMediaInformation</span>(<span class="hljs-params">with</span> <span class="hljs-params">title</span>: <span class="hljs-type">String</span>, <span class="hljs-params">with</span> <span class="hljs-params">description</span>: <span class="hljs-type">String</span>, <span class="hljs-params">with</span> <span class="hljs-params">studio</span>: <span class="hljs-type">String</span>, <span class="hljs-params">with</span> <span class="hljs-params">duration</span>: <span class="hljs-type">TimeInterval</span>, <span class="hljs-params">with</span> <span class="hljs-params">movieUrl</span>: <span class="hljs-type">String</span>, <span class="hljs-params">with</span> <span class="hljs-params">streamType</span>: <span class="hljs-type">GCKMediaStreamType</span>, <span class="hljs-params">with</span> <span class="hljs-params">thumbnailUrl</span>: <span class="hljs-type">String</span>?) -> <span class="hljs-type">GCKMediaInformation</span> {
    <span class="hljs-keyword">let</span> metadata <span class="hljs-operator">=</span> buildMetadata(with: title, with: description, with: studio, with: thumbnailUrl)

    <span class="hljs-keyword">let</span> mediaInfo <span class="hljs-operator">=</span> <span class="hljs-type">GCKMediaInformation</span>.<span class="hljs-keyword">init</span>(contentID: movieUrl, streamType: streamType, contentType: <span class="hljs-string">"video/m3u8"</span>, metadata: metadata, streamDuration: duration, mediaTracks: <span class="hljs-literal">nil</span>, textTrackStyle: <span class="hljs-literal">nil</span>, customData: <span class="hljs-literal">nil</span>)

    <span class="hljs-keyword">return</span> mediaInfo
}

<span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">buildMetadata</span>(<span class="hljs-params">with</span> <span class="hljs-params">title</span>: <span class="hljs-type">String</span>, <span class="hljs-params">with</span> <span class="hljs-params">description</span>: <span class="hljs-type">String</span>, <span class="hljs-params">with</span> <span class="hljs-params">studio</span>: <span class="hljs-type">String</span>, <span class="hljs-params">with</span> <span class="hljs-params">thumbnailUrl</span>: <span class="hljs-type">String</span>?) -> <span class="hljs-type">GCKMediaMetadata</span> {
    <span class="hljs-keyword">let</span> metadata <span class="hljs-operator">=</span> <span class="hljs-type">GCKMediaMetadata</span>.<span class="hljs-keyword">init</span>(metadataType: .movie)
    metadata.setString(title, forKey: kGCKMetadataKeyTitle)
    metadata.setString(description, forKey: <span class="hljs-string">"description"</span>)

    <span class="hljs-keyword">let</span> deviceName <span class="hljs-operator">=</span> sessionManager.currentCastSession<span class="hljs-operator">?</span>.device.friendlyName <span class="hljs-operator">??</span> studio
    metadata.setString(deviceName, forKey: kGCKMetadataKeyStudio)

    <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> thumbnailUrl <span class="hljs-operator">=</span> thumbnailUrl, <span class="hljs-keyword">let</span> url <span class="hljs-operator">=</span> <span class="hljs-type">URL</span>(string: thumbnailUrl) {
        metadata.addImage(<span class="hljs-type">GCKImage</span>.<span class="hljs-keyword">init</span>(url: url, width: <span class="hljs-number">720</span>, height: <span class="hljs-number">480</span>))
    }

    <span class="hljs-keyword">return</span> metadata
}
</code></pre>
<p>Now that we can create a <code>GCKMediaInformation</code> we need to create a function that will allow us to start a video on our connected receiver device.</p>
<p>For this add the following function in your <code>CastManager</code>:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">startSelectedItemRemotely</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">mediaInfo</span>: <span class="hljs-type">GCKMediaInformation</span>, <span class="hljs-params">at</span> <span class="hljs-params">time</span>: <span class="hljs-type">TimeInterval</span>, <span class="hljs-params">completion</span>: (<span class="hljs-type">Bool</span>) -> <span class="hljs-type">Void</span>) {
    <span class="hljs-keyword">let</span> castSession <span class="hljs-operator">=</span> sessionManager.currentCastSession

    <span class="hljs-keyword">if</span> castSession <span class="hljs-operator">!=</span> <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">let</span> options <span class="hljs-operator">=</span> <span class="hljs-type">GCKMediaLoadOptions</span>()
        options.playPosition <span class="hljs-operator">=</span> time
        options.autoplay <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>
        castSession<span class="hljs-operator">?</span>.remoteMediaClient<span class="hljs-operator">?</span>.loadMedia(mediaInfo, with: options)
        completion(<span class="hljs-literal">true</span>)

        sessionStatus <span class="hljs-operator">=</span> .alreadyConnected
    } <span class="hljs-keyword">else</span> {
        completion(<span class="hljs-literal">false</span>)
    }
}
</code></pre>
<p>This will take the <code>GCKMediaInformation</code> we will create with the previous created functions and, if a cast session is available, load media on the remote client. Once this happens the completion will return true if a cast session exists and false if we can not send a the <code>GCKMediaInformation</code> because there is no active cast session at the moment.</p>
<p>Now that we have all the components to start streaming our first video go ahead and update your <code>listenForCastConnection</code> function in the <code>Player</code> class.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">listenForCastConnection</span>() {
    <span class="hljs-keyword">let</span> sessionStatusListener: (<span class="hljs-type">CastSessionStatus</span>) -> <span class="hljs-type">Void</span> <span class="hljs-operator">=</span> { status <span class="hljs-keyword">in</span>
        <span class="hljs-keyword">switch</span> status {
        <span class="hljs-keyword">case</span> .started:
            <span class="hljs-keyword">self</span>.startCastPlay()
        deafult: <span class="hljs-keyword">break</span>
        }
    }

    <span class="hljs-type">CastManager</span>.shared.addSessionStatusListener(listener: sessionStatusListener)

}
</code></pre>
<p>Run your program, go to the <code>PlayerViewController</code> and start playing the video. Once the video is playing establish a connection with a cast device and see the video loading on the receiver. At this point our video will load on the receiver but our local player will continue playing the content as well. You guessed it, the fine tuning will be handled in the next part. :)</p>
<h3>Part 4: Tune player</h3>
<p>Now that our app is capable of playing content remotely we need to let our local player now that this is happening and stop or start it accordignly.</p>
<p>Once again update your <code>listenForCastConnection</code> function in the <code>Player</code> class as follow:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">listenForCastConnection</span>() {
    <span class="hljs-keyword">let</span> sessionStatusListener: (<span class="hljs-type">CastSessionStatus</span>) -> <span class="hljs-type">Void</span> <span class="hljs-operator">=</span> { status <span class="hljs-keyword">in</span>
    <span class="hljs-keyword">switch</span> status {
        <span class="hljs-keyword">case</span> .started:
            <span class="hljs-keyword">self</span>.startCastPlay()
        <span class="hljs-keyword">case</span> .resumed:
            <span class="hljs-keyword">self</span>.continueCastPlay()
        <span class="hljs-keyword">case</span> .ended, .failedToStart:
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">self</span>.playbackState <span class="hljs-operator">==</span> .playCast {
                <span class="hljs-keyword">self</span>.playbackState <span class="hljs-operator">=</span> .pause
                <span class="hljs-comment">//restart the video on local</span>
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">self</span>.playbackState <span class="hljs-operator">==</span> .pauseCast {
                <span class="hljs-keyword">self</span>.playbackState <span class="hljs-operator">=</span> .play
                <span class="hljs-comment">//stop the video on local</span>
            }
        <span class="hljs-keyword">default</span>: <span class="hljs-keyword">break</span>
        }
    }

    <span class="hljs-type">CastManager</span>.shared.addSessionStatusListener(listener: sessionStatusListener)
}
</code></pre>
<p>We will also need to create a few more functions to help us:</p>
<ul>
<li>pause the cast session (inside our <code>CastManager</code>)</li>
<li>restart the cast session (inside our <code>CastManager</code>)</li>
<li>play the local video if the cast session gets interruped or ended</li>
<li>pause the local video if the cast session gets started or resumed</li>
</ul>
<p>Lets add the functions needed to pause or restart the cast session in our <code>CastManager</code>. These will let us play or pause the video playing remotely at any time. As the <code>startSelectedItemRemotely</code> function does, the following will as well return true in their completion if the cast session is still active and false if there is no cast session active.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// MARK: - Play/Resume</span>

<span class="hljs-keyword">func</span> <span class="hljs-title function_">playSelectedItemRemotely</span>(<span class="hljs-params">to</span> <span class="hljs-params">time</span>: <span class="hljs-type">TimeInterval</span>?, <span class="hljs-params">completion</span>: (<span class="hljs-type">Bool</span>) -> <span class="hljs-type">Void</span>) {
    <span class="hljs-keyword">let</span> castSession <span class="hljs-operator">=</span> sessionManager.currentCastSession
        <span class="hljs-keyword">if</span> castSession <span class="hljs-operator">!=</span> <span class="hljs-literal">nil</span> {
            <span class="hljs-keyword">let</span> remoteClient <span class="hljs-operator">=</span> castSession<span class="hljs-operator">?</span>.remoteMediaClient
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> time <span class="hljs-operator">=</span> time {
            <span class="hljs-keyword">let</span> options <span class="hljs-operator">=</span> <span class="hljs-type">GCKMediaSeekOptions</span>()
            options.interval <span class="hljs-operator">=</span> time
            options.resumeState <span class="hljs-operator">=</span> .play
            remoteClient<span class="hljs-operator">?</span>.seek(with: options)
        } <span class="hljs-keyword">else</span> {
            remoteClient<span class="hljs-operator">?</span>.play()
        }
        completion(<span class="hljs-literal">true</span>)
    } <span class="hljs-keyword">else</span> {
        completion(<span class="hljs-literal">false</span>)
    }
}

<span class="hljs-comment">// MARK: - Pause</span>

<span class="hljs-keyword">func</span> <span class="hljs-title function_">pauseSelectedItemRemotely</span>(<span class="hljs-params">to</span> <span class="hljs-params">time</span>: <span class="hljs-type">TimeInterval</span>?, <span class="hljs-params">completion</span>: (<span class="hljs-type">Bool</span>) -> <span class="hljs-type">Void</span>) {
    <span class="hljs-keyword">let</span> castSession <span class="hljs-operator">=</span> sessionManager.currentCastSession
    <span class="hljs-keyword">if</span> castSession <span class="hljs-operator">!=</span> <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">let</span> remoteClient <span class="hljs-operator">=</span> castSession<span class="hljs-operator">?</span>.remoteMediaClient
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> time <span class="hljs-operator">=</span> time {
            <span class="hljs-keyword">let</span> options <span class="hljs-operator">=</span> <span class="hljs-type">GCKMediaSeekOptions</span>()
            options.interval <span class="hljs-operator">=</span> time
            options.resumeState <span class="hljs-operator">=</span> .pause
            remoteClient<span class="hljs-operator">?</span>.seek(with: options)
        } <span class="hljs-keyword">else</span> {
            remoteClient<span class="hljs-operator">?</span>.pause()
        }
        completion(<span class="hljs-literal">true</span>)
    } <span class="hljs-keyword">else</span> {
        completion(<span class="hljs-literal">false</span>)
    }
}
</code></pre>
<h3>Challenge time!</h3>
<p>Now that you have been following along for a while and that you can play/pause/resume video played on a remote session you will need to create some functionality in the <code>Player</code> class to handle the switch between the video playing locally and the video playing remotely on a cast session.</p>
<p>In order to do so you will need to stop the local video when a cast session starts, play the local video when a cast session ends, pause the remove video if the pause button is tapped while a session is active and restart the removed video if the play button is tapped when a session is active.</p>
<p>Good luck and do not worry if you don't manage to do so. You can check how this is done in the final version of the project.</p>
<h3>Part 5: Tracking Cast Video progress</h3>
<p>Our app is now capable of playing videos both on cast and locally. You might notice though that playing the videos locally make the our <code>UISlider</code> and Current time <code>UILabel</code> update, but this doesn't happen when we are casting a video.</p>
<p>In order to receive information about the current time of the video casted we need to ask our remote client of the current stream possition. This can be done by calling <code>approximateStreamPosition()</code> on the remote client. Go ahead and create a function called <code>getSessionCurrentTime(completion: (TimeInterval?) -> Void)</code> which takes a closure parameter of <code>TimeInterval</code>, and add it to our <code>CastManager</code>.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">getSessionCurrentTime</span>(<span class="hljs-params">completion</span>: (<span class="hljs-type">TimeInterval</span>?) -> <span class="hljs-type">Void</span>) {
    <span class="hljs-keyword">let</span> castSession <span class="hljs-operator">=</span> sessionManager.currentCastSession
    <span class="hljs-keyword">if</span> castSession <span class="hljs-operator">!=</span> <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">let</span> remoteClient <span class="hljs-operator">=</span> castSession<span class="hljs-operator">?</span>.remoteMediaClient
        <span class="hljs-keyword">let</span> currentTime <span class="hljs-operator">=</span> remoteClient<span class="hljs-operator">?</span>.approximateStreamPosition()
        completion(currentTime)
    } <span class="hljs-keyword">else</span> {
        completion(<span class="hljs-literal">nil</span>)
    }
}
</code></pre>
<p>Now in our Player we just need to ask our <code>CastManager</code> for the session current time and update the slider's possition and the current time <code>UILabel</code>.</p>
<h3>Part 6: Adding <code>GCKUIMiniMediaControlsViewController</code></h3>
<p>Now that we can control a cast session from the player while we are presenting the <code>PlayerViewController</code>, we might also want to control the video playback as well if we have returned to the <code>FirstViewController</code>.</p>
<p>This is a quite simple process since most of the functionality is received together with the Google Cast SDK.</p>
<p>We just need to add a <code>UIView</code> which will play the role of a container for the <code>GCKUIMiniMediaControlsViewController</code> in our <code>FirstViewController</code> and add the <code>GCKUIMiniMediaControlsViewController</code> to it. As well we can be notified when should display the media controls by implementing the <code>GCKUIMiniMediaControlsViewControllerDelegate</code>.</p>
<p>After you have created the container <code>UIView</code> add the to it <code>GCKUIMiniMediaControlsViewController</code> as follows:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">createMiniMediaControl</span>() {
    <span class="hljs-keyword">let</span> castContext <span class="hljs-operator">=</span> <span class="hljs-type">GCKCastContext</span>.sharedInstance()
    miniMediaControlsViewController <span class="hljs-operator">=</span> castContext.createMiniMediaControlsViewController()
    miniMediaControlsViewController.delegate <span class="hljs-operator">=</span> <span class="hljs-keyword">self</span>
    installViewController(miniMediaControlsViewController, inContainerView: mediaControlsContainerView)
}
</code></pre>
<p>For a more detailed implementation, including of how and when to present the <code>GCKUIMiniMediaControlsViewController</code> by animating it on screen you can check the full project.</p>
<p>Run the app, open a video and a cast session and play the video remotely. Now press the back button in the <code>UINavigationBar</code> and you should be able to see the <code>GCKUIMiniMediaControlsViewController</code> in the bottom of your <code>FirstViewController</code>.</p>
<h3>Part 7: Cutomise style of your default castViews</h3>
<p>With our app's functionality now completed, we can now start thinking about matching our app's theme with the castViews provided by the SDK.</p>
<p>Since we would like our castViews to use the same pink color that we use all over out app, we can configure all the castViews to do the same by assining our color to the <code>GCKUIStyle</code> as follows.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">style</span>() {
    <span class="hljs-keyword">let</span> castStyle <span class="hljs-operator">=</span> <span class="hljs-type">GCKUIStyle</span>.sharedInstance()
    castStyle.castViews.backgroundColor <span class="hljs-operator">=</span> .white
    castStyle.castViews.bodyTextColor <span class="hljs-operator">=</span> <span class="hljs-type">UIColor</span>.nodesColor
    castStyle.castViews.buttonTextColor <span class="hljs-operator">=</span> <span class="hljs-type">UIColor</span>.nodesColor
    castStyle.castViews.headingTextColor <span class="hljs-operator">=</span> <span class="hljs-type">UIColor</span>.nodesColor
    castStyle.castViews.captionTextColor <span class="hljs-operator">=</span> <span class="hljs-type">UIColor</span>.nodesColor
    castStyle.castViews.iconTintColor <span class="hljs-operator">=</span> <span class="hljs-type">UIColor</span>.nodesColor
    castStyle.apply()
}
</code></pre>
<p>Alternatively you can customise each castView individually the same way we are customising our miniController.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">miniControllerStyle</span>() {
    <span class="hljs-keyword">let</span> castStyle <span class="hljs-operator">=</span> <span class="hljs-type">GCKUIStyle</span>.sharedInstance()
    castStyle.castViews.mediaControl.miniController.backgroundColor <span class="hljs-operator">=</span> <span class="hljs-type">UIColor</span>.nodesColor
    castStyle.castViews.mediaControl.miniController.bodyTextColor <span class="hljs-operator">=</span> .white
    castStyle.castViews.mediaControl.miniController.buttonTextColor <span class="hljs-operator">=</span> .white
    castStyle.castViews.mediaControl.miniController.headingTextColor <span class="hljs-operator">=</span> .white
    castStyle.castViews.mediaControl.miniController.captionTextColor <span class="hljs-operator">=</span> .white
    castStyle.castViews.mediaControl.miniController.iconTintColor <span class="hljs-operator">=</span> .white
    castStyle.apply()
}
</code></pre>
<p>You can add this functions in your <code>CastManager</code> and call them inside the <code>initialise</code> and voila, you will have all the cast views matching your app's theme.</p>
<p>To read more about this views you can go to <a href="https://developers.google.com/cast/docs/ios_sender_styles">Google's documentation here</a></p>
<h2>Final notes</h2>
<p>You have now made it all the way to the end of this post, by the end of which, hopefully you will have a clear picture of how to implement and handle interaction to the Google Cast SDK.</p>
<p>Don't forget to download our final project and compare our results. :)</p>
<p><a href="https://github.com/nodes-ios/GoogleCast-Demo/tree/cast-demo-final">Final Project</a></p>
<p>Hope to see you next time!</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/JO_S6ewBqAk">Austin Poon</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[URLComponents]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2018/03/05/URLComponents</link>
            <guid>https://engineering.monstar-lab.com/en/post/2018/03/05/URLComponents</guid>
            <pubDate>Mon, 05 Mar 2018 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>When writing mobile apps, more often than not we have to communicate with a backend to get some data to present to the user.</p>
<p>The shared language between frontend and backend is usually URLs combined with either a POST body or maybe some URL query parameters, depending on what the backend offers.</p>
<p>In this post we will look at <a href="https://developer.apple.com/documentation/foundation/urlcomponents">URLComponents</a>, a small but very useful helper that will become your new best friend when composing or decomposing URLs (we promise!)</p>
<p>But before we look at <code>URLComponents</code>, lets first look at a world without <code>URLComponents</code> just to make you extra grateful that such a thing exists.</p>
<h2>Setting the Stage</h2>
<p>OK, lets assume that we need to use a backend service for searching and showing details about TV shows.</p>
<h3>Searching</h3>
<p>Our hard working backend developing friends have made a service for us to use. The base URL looks like this:</p>
<p><code>https://showsknownfrom.tv/search</code></p>
<p>(no...it doesn't exist)</p>
<p>And it offers the following query parameters:</p>
<ul>
<li>q: A query string we would like to search for</li>
<li>order (optional parameter): Do we want the search results ascending or descending</li>
<li>number (optional parameter): Max number of search results to return</li>
</ul>
<h3>Showing Details</h3>
<p>To see details about a TV show, we get a URL to a HTML page from the backend which we then present in a WebView.</p>
<p>The URL looks like this:</p>
<p><code>https://showsknownfrom.tv/12345678</code></p>
<p>And can have an optional URL parameter called <code>featured</code>. If featured is present and is true, we would like to show a flashing red <code>UILabel</code> with the caption "FEATURED" over our <code>WebView</code> (note: this feature has not been cleared with the designer yet!)</p>
<h2>Composing and Decomposing URLs - The Hard Way</h2>
<h3>Composing URLs for Searching</h3>
<p>The stage is set and we are ready to look at how to call our search service in our pre-URLComponents world.</p>
<p>To generate a <code>URL</code> in our iOS app, we could write some code like this:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">searchURL</span>(<span class="hljs-params">with</span> <span class="hljs-params">q</span>: <span class="hljs-type">String</span>, <span class="hljs-params">optionalParameters</span>: [<span class="hljs-params">String</span>: <span class="hljs-type">String</span>] <span class="hljs-operator">=</span> [:]) -> <span class="hljs-type">URL</span>? {
    <span class="hljs-keyword">var</span> urlString <span class="hljs-operator">=</span> <span class="hljs-string">"https://showsknownfrom.tv/search"</span>

    <span class="hljs-comment">//Append query</span>
    urlString.append(<span class="hljs-string">"?q=<span class="hljs-subst">\(q)</span>"</span>)

    <span class="hljs-comment">//Append parameters if any</span>
    optionalParameters.forEach({key, value <span class="hljs-keyword">in</span>
        urlString.append(<span class="hljs-string">"&#x26;<span class="hljs-subst">\(key)</span>=<span class="hljs-subst">\(value)</span>"</span>)
    })

    <span class="hljs-keyword">return</span> <span class="hljs-type">URL</span>(string: urlString)
}
</code></pre>
<p>Granted, it could be worse, but note that we have to know about a "magic" <code>?</code> if this is the first parameter and <code>&#x26;</code> if it is any other parameters.</p>
<p>Lets take it for a spin:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> url <span class="hljs-operator">=</span> searchURL(with: <span class="hljs-string">"Sopranos"</span>) {
    <span class="hljs-built_in">print</span>(url) <span class="hljs-comment">//gives us: https://showsknownfrom.tv/search?q=Sopranos</span>
}

<span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> url <span class="hljs-operator">=</span> searchURL(with: <span class="hljs-string">"Sopranos"</span>, optionalParameters: [<span class="hljs-string">"order"</span> : <span class="hljs-string">"desc"</span>, <span class="hljs-string">"number"</span> : <span class="hljs-string">"100"</span>]) {
    <span class="hljs-built_in">print</span>(url) <span class="hljs-comment">//gives us: https://showsknownfrom.tv/search?q=Sopranos&#x26;number=100&#x26;order=desc</span>
}
</code></pre>
<p>By now you might be thinking "Hey!! What gives! Why are those morons hyping <code>URLComponents</code> so hard? This clearly works!"</p>
<p>OK, lets look at another example:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> url <span class="hljs-operator">=</span> searchURL(with: <span class="hljs-string">"Halt and Catch Fire"</span>, optionalParameters: [<span class="hljs-string">"order"</span> : <span class="hljs-string">"desc"</span>, <span class="hljs-string">"number"</span> : <span class="hljs-string">"100"</span>]) {
    <span class="hljs-built_in">print</span>(url)
} <span class="hljs-keyword">else</span> {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">"no URL"</span>) <span class="hljs-comment">//We end up here</span>
}
</code></pre>
<p>That didn't work, why?</p>
<p>Yes, you're right, the query contains spaces!</p>
<p>We need to <a href="https://en.wikipedia.org/wiki/Percent-encoding">Percent Encode</a> our query, so lets do that before we try to use it. We add this to the top of our function:</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">//Append query</span>
<span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> encodedQuery <span class="hljs-operator">=</span> q.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span> }
urlString.append(<span class="hljs-string">"?q=<span class="hljs-subst">\(encodedQuery)</span>"</span>)
<span class="hljs-operator">...</span>
</code></pre>
<p>And now we get <code>https://showsknownfrom.tv/search?q=Halt%20and%20Catch%20Fire&#x26;number=100&#x26;order=desc</code></p>
<p>Great! Our function seems to be working as expected now, but it took some time and some tries to get here.</p>
<h3>Decomposing URLs</h3>
<p>Now, lets look at another example.</p>
<p>Remember the details page and the <code>featured</code> parameter?</p>
<p>Here is how we could fetch that from the URL in our iOS app:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">receivedURL</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">url</span>: <span class="hljs-type">URL</span>, <span class="hljs-params">contains</span> <span class="hljs-params">parameter</span>: <span class="hljs-type">String</span>) -> <span class="hljs-type">String</span>? {
    <span class="hljs-keyword">let</span> urlString <span class="hljs-operator">=</span> url.absoluteString

    <span class="hljs-comment">//add = to parameter</span>
    <span class="hljs-keyword">let</span> parameterNameWithEqual <span class="hljs-operator">=</span> <span class="hljs-string">"<span class="hljs-subst">\(parameter)</span>="</span>

    <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> rangeOfParameterName <span class="hljs-operator">=</span> urlString.range(of: parameterNameWithEqual) <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
    }

    <span class="hljs-comment">//create a substring starting after the parameter name</span>
    <span class="hljs-keyword">let</span> parametersAfterParameterName <span class="hljs-operator">=</span> <span class="hljs-type">String</span>(urlString[rangeOfParameterName.upperBound<span class="hljs-operator">...</span>])

    <span class="hljs-comment">//check if we have more parameters after the one we're looking for</span>
    <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> rangeOfNextAmpersand <span class="hljs-operator">=</span> parametersAfterParameterName.index(of: <span class="hljs-string">"&#x26;"</span>) <span class="hljs-keyword">else</span> {
        <span class="hljs-comment">//we did not, just return the value then</span>
        <span class="hljs-keyword">return</span> parametersAfterParameterName
    }

    <span class="hljs-comment">//cut the substring where the next parameter starts</span>
    <span class="hljs-keyword">return</span> <span class="hljs-type">String</span>(parametersAfterParameterName.prefix(upTo: rangeOfNextAmpersand))
}

<span class="hljs-comment">//test</span>
<span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> url <span class="hljs-operator">=</span> <span class="hljs-type">URL</span>(string: <span class="hljs-string">"https://showsknownfrom.tv/shows/12345678?featured=true"</span>),
   <span class="hljs-keyword">let</span> parameter <span class="hljs-operator">=</span> receivedUrl(url, contains: <span class="hljs-string">"featured"</span>) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">"found <span class="hljs-subst">\(parameter)</span>"</span>)
} <span class="hljs-keyword">else</span> {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">"not found"</span>)
}
</code></pre>
<p>Granted, it works but...wow! So much going on here, magic values and String gymnastics galore.</p>
<p>We could probably make this prettier but...why should we, when something as beautiful as <code>URLComponents</code> exists.</p>
<h2>Composing and Decomposing URLs - The Easy Way</h2>
<p>By now you should be more than ready for <code>URLComponents</code> so lets dive in.</p>
<h3>URLComponents - An Introduction</h3>
<p><code>URLComponents</code> was introduced in iOS 7.0 and macOS 10.9 so it has been around for some time.</p>
<p>To create a <code>URLComponents</code> object you can use either a <code>String</code> or a <code>URL</code>, in both cases you'll end out with an optional <code>URLComponents</code> object.</p>
<p>Just a quick note about the <code>init</code> method if you are using a <code>URL</code>. As it says in the <a href="https://developer.apple.com/documentation/foundation/urlcomponents/3126811-init">documentation</a>:</p>
<blockquote>
<p>If resolvingAgainstBaseURL is true and url is a relative URL, the components of url.absoluteURL are used. If the url string from the URL is malformed, nil is returned.</p>
</blockquote>
<p>To create a new <code>URLComponents</code> object we can write something like this:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> urlString <span class="hljs-operator">=</span> <span class="hljs-string">"https://showsknownfrom.tv/search?q=Sopranos&#x26;order=desc"</span>

<span class="hljs-keyword">guard</span> <span class="hljs-keyword">var</span> urlComponents <span class="hljs-operator">=</span> <span class="hljs-type">URLComponents</span>(string: urlString) <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}
</code></pre>
<p>Now we have access to all parts of the <code>URL</code> and can probe them as we see fit.</p>
<p>Here are some examples:</p>
<pre><code class="hljs language-arduino">urlComponents.host   <span class="hljs-comment">//gives us: showsknownfrom.tv</span>
urlComponents.scheme <span class="hljs-comment">//gives us: https</span>
urlComponents.query  <span class="hljs-comment">//gives us: q=Sopranos&#x26;order=desc</span>
</code></pre>
<p>But the best part is this one:</p>
<pre><code class="hljs language-swift">urlComponents.queryItems
</code></pre>
<p>Which gives us back an array of <code>URLQueryItem</code> objects, where <code>URLQueryItem</code> objects are basically just key/value objects for the individual query items (documented <a href="https://developer.apple.com/documentation/foundation/urlcomponents/1779966-queryitems">here</a>).</p>
<p>Oh joy! That makes it so much easier for us to append query parameters, or check if a URL contains a query parameter.</p>
<h3>Composing URLs for Searching</h3>
<p>Armed with our new knowledge, lets now look at how we can use <code>URLComponents</code> to build a <code>URL</code> with parameters for us:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">searchURL</span>(<span class="hljs-params">with</span> <span class="hljs-params">q</span>: <span class="hljs-type">String</span>, <span class="hljs-params">optionalParameters</span>: [<span class="hljs-params">String</span>: <span class="hljs-type">String</span>] <span class="hljs-operator">=</span> [:]) -> <span class="hljs-type">URL</span>? {
    <span class="hljs-keyword">let</span> urlString <span class="hljs-operator">=</span> <span class="hljs-string">"https://showsknownfrom.tv/search"</span>

    <span class="hljs-keyword">guard</span> <span class="hljs-keyword">var</span> urlComponents <span class="hljs-operator">=</span> <span class="hljs-type">URLComponents</span>(string: urlString) <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
    }

    <span class="hljs-keyword">var</span> queryItems: [<span class="hljs-type">URLQueryItem</span>] <span class="hljs-operator">=</span> [<span class="hljs-type">URLQueryItem</span>(name: <span class="hljs-string">"q"</span>, value: q)]

    <span class="hljs-keyword">let</span> optionalURLQueryItems <span class="hljs-operator">=</span> optionalParameters.map {
        <span class="hljs-keyword">return</span> <span class="hljs-type">URLQueryItem</span>(name: <span class="hljs-variable">$0</span>, value: <span class="hljs-variable">$1</span>)
    }
    queryItems.append(contentsOf: optionalURLQueryItems)

    urlComponents.queryItems <span class="hljs-operator">=</span> queryItems

    <span class="hljs-keyword">return</span> urlComponents.url
}
</code></pre>
<p>First we use the <code>urlString</code> to create a new <code>URLComponents</code> object. (Note that we create the <code>urlComponents</code> as a <code>var</code> as we'll need to write to it later on.)</p>
<p>Next we create an array of <code>URLQueryItem</code> objects and append our only required parameter, the <code>q</code> parameter.</p>
<p>Then we loop through our <code>optionalParameters</code> dictionary, map the elements to <code>URLQueryItem</code> objects and append them to our existing array.</p>
<p>Finally, we ask <code>URLComponents</code> to try and return a <code>URL</code> for us.</p>
<p>Simple and beautiful, all the hard work has been delegated onwards to <code>URLComponents</code> and we don't even have to worry about whether to use <code>?</code> or <code>&#x26;</code> any more.</p>
<p>Lets see how it works:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> url <span class="hljs-operator">=</span> searchURL(with: <span class="hljs-string">"Sopranos"</span>) {
    <span class="hljs-built_in">print</span>(url) <span class="hljs-comment">//gives us: https://showsknownfrom.tv/search?q=Sopranos</span>
}

<span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> url <span class="hljs-operator">=</span> searchURL(with: <span class="hljs-string">"Sopranos"</span>, optionalParameters: [<span class="hljs-string">"order"</span> : <span class="hljs-string">"desc"</span>, <span class="hljs-string">"number"</span> : <span class="hljs-string">"100"</span>]) {
    <span class="hljs-built_in">print</span>(url) <span class="hljs-comment">//gives us: https://showsknownfrom.tv/search?q=Sopranos&#x26;number=100&#x26;order=desc</span>
}

<span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> url <span class="hljs-operator">=</span> searchURL(with: <span class="hljs-string">"Halt and Catch Fire"</span>, optionalParameters: [<span class="hljs-string">"order"</span> : <span class="hljs-string">"desc"</span>, <span class="hljs-string">"number"</span> : <span class="hljs-string">"100"</span>]) {
    <span class="hljs-built_in">print</span>(url) <span class="hljs-comment">//gives us: https://showsknownfrom.tv/search?q=Halt%20and%20Catch%20Fire&#x26;number=100&#x26;order=desc</span>
} <span class="hljs-keyword">else</span> {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">"no URL"</span>)
}
</code></pre>
<p>Perfect!</p>
<h3>Decomposing URLs</h3>
<p>Remember our example from before?</p>
<p>Now that we know about <code>URLQueryItem</code>s, checking if a query parameter exists is soooo much easier:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">receivedURL</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">url</span>: <span class="hljs-type">URL</span>, <span class="hljs-params">contains</span> <span class="hljs-params">parameter</span>: <span class="hljs-type">String</span>) -> <span class="hljs-type">String</span>? {
    <span class="hljs-keyword">guard</span>
        <span class="hljs-keyword">let</span> urlComponents <span class="hljs-operator">=</span> <span class="hljs-type">URLComponents</span>(url: url, resolvingAgainstBaseURL: <span class="hljs-literal">true</span>),
        <span class="hljs-keyword">let</span> queryItems <span class="hljs-operator">=</span> urlComponents.queryItems
    <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
    }

    <span class="hljs-keyword">let</span> items <span class="hljs-operator">=</span> queryItems.filter { <span class="hljs-variable">$0</span>.name <span class="hljs-operator">==</span> parameter }
    <span class="hljs-keyword">return</span> items.first<span class="hljs-operator">?</span>.value
}

<span class="hljs-comment">//test</span>
<span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> url <span class="hljs-operator">=</span> <span class="hljs-type">URL</span>(string: <span class="hljs-string">"https://showsknownfrom.tv/shows/12345678?featured=true"</span>),
   <span class="hljs-keyword">let</span> parameter <span class="hljs-operator">=</span> receivedUrl(url, contains: <span class="hljs-string">"featured"</span>) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">"found <span class="hljs-subst">\(parameter)</span>"</span>)
} <span class="hljs-keyword">else</span> {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">"not found"</span>)
}
</code></pre>
<p>Pretty, right? At least compared to the initial version.</p>
<h2>Composing and Decomposing URLs - The Easier Way</h2>
<p>Well, we made it this far! But can we do better? Of course we can!</p>
<p>Lets write an <code>extension</code> to <code>URL</code> so we can append query parameters and also ask if a URL contains a query parameter:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">extension</span> <span class="hljs-title class_">URL</span> {
  <span class="hljs-keyword">func</span> <span class="hljs-title function_">append</span>(<span class="hljs-params">queryParameters</span>: [<span class="hljs-params">String</span>: <span class="hljs-type">String</span>]) -> <span class="hljs-type">URL</span>? {
      <span class="hljs-keyword">guard</span> <span class="hljs-keyword">var</span> urlComponents <span class="hljs-operator">=</span> <span class="hljs-type">URLComponents</span>(url: <span class="hljs-keyword">self</span>, resolvingAgainstBaseURL: <span class="hljs-literal">true</span>) <span class="hljs-keyword">else</span> {
          <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
      }

      <span class="hljs-keyword">let</span> urlQueryItems <span class="hljs-operator">=</span> queryParameters.map {
          <span class="hljs-keyword">return</span> <span class="hljs-type">URLQueryItem</span>(name: <span class="hljs-variable">$0</span>, value: <span class="hljs-variable">$1</span>)
      }
      urlComponents.queryItems <span class="hljs-operator">=</span> urlQueryItems
      <span class="hljs-keyword">return</span> urlComponents.url
  }

  <span class="hljs-keyword">func</span> <span class="hljs-title function_">value</span>(<span class="hljs-params">forParameter</span> <span class="hljs-params">name</span>: <span class="hljs-type">String</span>) -> <span class="hljs-type">String</span>? {
    <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> urlComponents <span class="hljs-operator">=</span> <span class="hljs-type">URLComponents</span>(url: <span class="hljs-keyword">self</span>, resolvingAgainstBaseURL: <span class="hljs-literal">true</span>),
        <span class="hljs-keyword">let</span> queryItems <span class="hljs-operator">=</span> urlComponents.queryItems <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
    }
    <span class="hljs-keyword">let</span> items <span class="hljs-operator">=</span> queryItems.filter { <span class="hljs-variable">$0</span>.name <span class="hljs-operator">==</span> name }
    <span class="hljs-keyword">return</span> items.first<span class="hljs-operator">?</span>.value
  }
}
</code></pre>
<p>Poetry, no less!</p>
<p>Lets test it out:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> url <span class="hljs-operator">=</span> <span class="hljs-type">URL</span>(string: <span class="hljs-string">"https://showsknownfrom.tv"</span>),
   <span class="hljs-keyword">let</span> appendedURL <span class="hljs-operator">=</span> url.append(queryParameters: [<span class="hljs-string">"q"</span>: <span class="hljs-string">"Halt and Catch Fire"</span>, <span class="hljs-string">"order"</span> : <span class="hljs-string">"desc"</span>, <span class="hljs-string">"number"</span>: <span class="hljs-string">"100"</span>]) {
        <span class="hljs-built_in">print</span>(appendedURL) <span class="hljs-comment">//gives us: https://showsknownfrom.tv?q=Halt%20and%20Catch%20Fire&#x26;number=100&#x26;order=desc</span>
}

<span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> url <span class="hljs-operator">=</span> <span class="hljs-type">URL</span>(string: <span class="hljs-string">"https://showsknownfrom.tv/shows/12345678?featured=true&#x26;test=1"</span>),
   <span class="hljs-keyword">let</span> value <span class="hljs-operator">=</span> url.value(forParameter: <span class="hljs-string">"featured"</span>) {
     <span class="hljs-built_in">print</span>(<span class="hljs-string">"found <span class="hljs-subst">\(value)</span>"</span>) <span class="hljs-comment">//gives us: found true</span>
}
</code></pre>
<p>Fantastic. We've composed two simple functions that makes our daily work so much easier when working with URL parameters.</p>
<h2>Composing and Decomposing URLs - The Easier Way</h2>
<p>Now you might be thinking "Urgh!! Extensions!! Why should I write code myself if I can download something from the internet, written by complete strangers instead!!"</p>
<p>If that is you, then good news pal!</p>
<p>We have made a collection of helper methods and extensions called <a href="https://github.com/nodes-ios/Codemine">CodeMine</a>. Next to other nuggets of gold you'll find <a href="https://github.com/nodes-ios/Codemine/blob/master/Codemine/Extensions/NSURL%2BUtilities.swift">two extension methods</a> to <code>URL</code> that looks awfully familiar by now:</p>
<p><code>public func value(forParameter name: String) -> String?</code></p>
<p>which will return the value for a query parameter in a <code>URL</code> (if it exists).</p>
<p>And:</p>
<p><code>public func append(queryParameters: [String: String]) -> URL?</code></p>
<p>which will append a <code>String: String</code> dictionary to an already existing <code>URL</code> and return a new <code>URL</code> with the query parameters appended and paramter encoded.</p>
<p>So, if you're into Cocoapods, add this to your <code>podfile</code>:</p>
<p><code>pod 'Codemine', '~>1.0.0'</code></p>
<p>And if you're more of a Carthage person, add this to your <code>Cartfile</code></p>
<p><code>github "nodes-ios/Codemine" ~> 1.0</code></p>
<p>Update and you're done, you can now check if your <code>URL</code> object contains specific query parameters or you can build your own <code>URL</code> and append parameters easily.</p>
<p>Hey...you're welcome :)</p>
<h2>Parting Words</h2>
<p>Whew, we made it!</p>
<p>We started out writing a lot of error prone code ourself for managing URLs more or less as expected.</p>
<p>That was then replaced with <code>URLComponents</code> that does all the hard work for us, and finally we made our own <code>extension</code> to add some building blocks to make our daily work easier.</p>
<p>If you didn't already know about <code>URLComponents</code> we hope to have convinced you that it is an awesome little helper to add to your toolbelt for when you need to work with <code>URL</code>s.</p>
<p>Thank you for reading along.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/TZj-urJKRao">Yingchih Hao</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Making a Presentation Application with Google Nearby Messages & Firebase]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2018/01/05/Making-a-presentation-app-with-Google-Nearby-Messages-and-Firebase</link>
            <guid>https://engineering.monstar-lab.com/en/post/2018/01/05/Making-a-presentation-app-with-Google-Nearby-Messages-and-Firebase</guid>
            <pubDate>Fri, 05 Jan 2018 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Here at Nodes, we regularly run events and give presentations. We wanted to use technology to make these more accessible and enhance these events for people who attend, and smooth out the event process entirely the only way we know how; making applications!</p>
<p>Although we have many ideas for what this application could encompass, I decided to focus on the presentation process and in particular, presenting slideshows. I set out to make an application that could display slideshows and "broadcast" them to other local devices running the same application, while making the process as simple for a user as possible. I have developed a prototype application that allows users to give presentations while letting other users view the same presentation on their own devices, while it automatically follows along in real time with the presenter. I’m going to explain how this was made possible by using two mobile platforms from Google, <a href="https://firebase.google.com/">Firebase</a> &#x26; <a href="https://developers.google.com/nearby/">Nearby Messages</a>.</p>
<p>The application currently works with PDFs; using a <code>UIImage</code> extension, it takes PDF files and turns pages into images. I then use a <code>UIPageViewController</code> to create the required number of ViewControllers and display each pdf page in a separate view controller allowing the user to flick through them. In this blog, I want to focus more on the Google platforms.</p>
<p>All libraries need to be added to your project which can be done using Cocoapods. Google provide simple tutorials in setting these libraries up online.</p>
<h2>The First Step: Getting two local devices to communicate</h2>
<p>The first step was always going to get two devices to communicate locally. I wanted this to be all triggered by the applications ran in near proximity to any device running the same application. No logging in, no long sign up process, all automatically handled by the application as soon as you take your seat at the event.</p>
<p>My first thought was to look through the Apple Libraries to see if anything would suffice. The framework, <a href="https://developer.apple.com/documentation/multipeerconnectivity">MultipeerConnectivity</a> very much did what I wanted to a high standard but there was one factor that restricted me using this. I wanted the application to also have the possibility of working together with Android devices. Something that was not possible using this. So from that moment I searched for other options.</p>
<p>I then explored the Google Nearby Messages API. This was perfect for what I needed, offering a way of publishing payloads to subscribers with the cross platform support I was searching for. You can find out more about this API <a href="https://developers.google.com/nearby/messages/overview">here</a>. Using a combination of Bluetooth and near-ultrasonic audio, the library requires Microphone &#x26; Bluetooth permissions so I was sure to add these to the applications permission requirements.</p>
<p>When first initialising Google Nearby Messages, you will need to initalise the <code>GNSMessageManager</code> with an API Key that you can get from Google. It will also take a parameterBlock with some error handlers for when the required permissions are not correctly configured. You will also have to set the <code>GNSPermission</code> to granted once the user has done so. I have chosen to hold these managers in a single manager class. Below you can see the setup being complete in the initialisation of that class.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">override</span> <span class="hljs-keyword">init</span>() {

    <span class="hljs-keyword">super</span>.<span class="hljs-keyword">init</span>()

    <span class="hljs-comment">// Enable debug logging to help track down problems.</span>
    <span class="hljs-type">GNSMessageManager</span>.setDebugLoggingEnabled(<span class="hljs-literal">false</span>)
    <span class="hljs-type">GNSPermission</span>.setGranted(<span class="hljs-literal">true</span>)

    <span class="hljs-comment">// Create the message manager, which lets you publish messages and subscribe to messages</span>
    <span class="hljs-comment">// published by nearby devices.</span>
    messageMgr <span class="hljs-operator">=</span> <span class="hljs-type">GNSMessageManager</span>(apiKey: kMyAPIKey,
    paramsBlock: {(params: <span class="hljs-type">GNSMessageManagerParams</span>?) -> <span class="hljs-type">Void</span> <span class="hljs-keyword">in</span>
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> params <span class="hljs-operator">=</span> params <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> }

        <span class="hljs-comment">// This is called when microphone permission is enabled or disabled by the user.</span>
        params.microphonePermissionErrorHandler <span class="hljs-operator">=</span> { hasError <span class="hljs-keyword">in</span>
            <span class="hljs-keyword">if</span> (hasError) {
                <span class="hljs-built_in">print</span>(<span class="hljs-string">"Nearby works better if microphone use is allowed"</span>)
            }
        }
        <span class="hljs-comment">// This is called when Bluetooth permission is enabled or disabled by the user.</span>
        params.bluetoothPermissionErrorHandler <span class="hljs-operator">=</span> { hasError <span class="hljs-keyword">in</span>
            <span class="hljs-keyword">if</span> (hasError) {
                <span class="hljs-built_in">print</span>(<span class="hljs-string">"Nearby works better if Bluetooth use is allowed"</span>)
            }
        }
        <span class="hljs-comment">// This is called when Bluetooth is powered on or off by the user.</span>
        params.bluetoothPowerErrorHandler <span class="hljs-operator">=</span> { hasError <span class="hljs-keyword">in</span>
            <span class="hljs-keyword">if</span> (hasError) {
                <span class="hljs-built_in">print</span>(<span class="hljs-string">"Nearby works better if Bluetooth is turned on"</span>)
            }
        }
    })
}
</code></pre>
<p>When the application is first opened the user is provided with the option to be a presenter or an observer. Lets look closer at how we handle these states.</p>
<h4>The Presenter</h4>
<p>When ‘presenter' is selected, the application generates a random id. This ID will be a reference to this event. We then can simply start sharing the event id with local devices running the same application (known using the Google API Key), like below. Currently I’m just sharing a string, but its possible to share JSON objects if required. I simply create a message (<code>GNSMessage</code>) using the string’s data and publish it by the messageManager publication function. To stop publishing this message, you must release the publication object.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">startSharing</span>(<span class="hljs-params">withEventId</span> <span class="hljs-params">eventId</span>: <span class="hljs-type">String</span>) {
    stopSharing()
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> messageMgr <span class="hljs-operator">=</span> <span class="hljs-keyword">self</span>.messageMgr {

        <span class="hljs-comment">// Publish the name to nearby devices.</span>
        <span class="hljs-keyword">let</span> pubMessage: <span class="hljs-type">GNSMessage</span> <span class="hljs-operator">=</span> <span class="hljs-type">GNSMessage</span>(content: eventId.data(using: .utf8,
        allowLossyConversion: <span class="hljs-literal">true</span>))
        publication <span class="hljs-operator">=</span> messageMgr.publication(with: pubMessage)
        <span class="hljs-built_in">print</span>(<span class="hljs-string">"<span class="hljs-subst">\(UIDevice.current.name)</span> sent <span class="hljs-subst">\(eventId)</span>"</span>)
    }
}
</code></pre>
<h3>The Observer</h3>
<p>The observer works with the same initialisation as the presenter. What it differs in is, instead of creating an event id, it will listen for local devices publishing and set the event ids from any results it finds. Any messages found are then handled in the completion block. Initially I believed this would be sufficient to do all I required, send presentation objects with names and slide numbers where observers could update their data on receiving nearby messages. I found nearby messages to be a little slow and unreliable, some messages would occasionally drop and didn’t seem efficient enough to send each slide update via this API. Because of this, I have added implementation of Firebase. That was required for what would be the next step...</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">subscribeToMessages</span>() {
    <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> messageMgr <span class="hljs-operator">=</span> <span class="hljs-keyword">self</span>.messageMgr <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> }

    <span class="hljs-comment">// Subscribe to messages from nearby devices and display them in the message view.</span>
    subscription <span class="hljs-operator">=</span> messageMgr.subscription(messageFoundHandler: {[<span class="hljs-keyword">unowned</span> <span class="hljs-keyword">self</span>] (message: <span class="hljs-type">GNSMessage</span>?) -> <span class="hljs-type">Void</span> <span class="hljs-keyword">in</span>

        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> message <span class="hljs-operator">=</span> message <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> }
        <span class="hljs-keyword">let</span> str <span class="hljs-operator">=</span> <span class="hljs-type">String</span>(data: message.content, encoding: .utf8)<span class="hljs-operator">!</span>

        <span class="hljs-built_in">print</span>(<span class="hljs-string">"<span class="hljs-subst">\(UIDevice.current.name)</span> recieved <span class="hljs-subst">\(str)</span>"</span>)
        <span class="hljs-keyword">self</span>.eventId <span class="hljs-operator">=</span> str
        <span class="hljs-keyword">self</span>.subscribeToFirebase()

    }, messageLostHandler: {[<span class="hljs-keyword">unowned</span> <span class="hljs-keyword">self</span>](message: <span class="hljs-type">GNSMessage</span>?) -> <span class="hljs-type">Void</span> <span class="hljs-keyword">in</span>
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> message <span class="hljs-operator">=</span> message <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> }
        <span class="hljs-keyword">let</span> str <span class="hljs-operator">=</span> <span class="hljs-type">String</span>(data: message.content, encoding: .utf8)<span class="hljs-operator">!</span>
        <span class="hljs-built_in">print</span>(<span class="hljs-string">"<span class="hljs-subst">\(UIDevice.current.name)</span> lost <span class="hljs-subst">\(str)</span>"</span>)
    })
}
</code></pre>
<h2>The Second Step: Real-time updates between the connected devices</h2>
<p>Using Firebase is pretty straight forward and pretty powerful. It’s impressively quick and works incredibly well with situations like this. The first step is to setup a reference to the Firebase database. I simply hold a reference to that in my manager class like so and initialise it where required.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">var</span> databaseReference: <span class="hljs-type">DatabaseReference</span>!
databaseReference <span class="hljs-operator">=</span> <span class="hljs-type">Database</span>.database().reference()
</code></pre>
<p>As a Presenter application, we want to update the database with what it is currently displaying. We use the event ID as the tracker so if you want to move to a different slideshow that is also possible. We simply call this method using a delegate overtime the presenter, turns to a different slideshow or name and it will update the database with these properties. Please note; this is with the database set as public meaning anyone can read/write to the database. You can learn more about <a href="https://firebase.google.com/docs/database/security/quickstart?authuser=0">Firebase Database Authourization here</a></p>
<pre><code class="hljs language-swift"><span class="hljs-comment">//Update Firebase database with current presentation state (for Presenter applications)</span>
<span class="hljs-keyword">func</span> <span class="hljs-title function_">sharePresentationProgressToFirebase</span>(<span class="hljs-params">with</span> <span class="hljs-params">presentaionName</span>: <span class="hljs-type">String</span>, <span class="hljs-params">page</span>: <span class="hljs-type">String</span>) {

    <span class="hljs-keyword">guard</span> <span class="hljs-keyword">self</span>.mode <span class="hljs-operator">==</span> .present <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> }
    <span class="hljs-keyword">self</span>.databaseReference.child(eventId).setValue([<span class="hljs-string">"presentationName"</span> : presentaionName, <span class="hljs-string">"pageNumber"</span>: page])
}
</code></pre>
<h5>Example of a Firebase data entry</h5>
<pre><code class="hljs language-swift"><span class="hljs-type">EVENT</span>:<span class="hljs-type">NJ8WVJlpdygj1nqhry8aPDZbvkDFiMieuCuD1V3u</span> {
  pageNumber: <span class="hljs-string">"4"</span>;
  presentationName: <span class="hljs-string">"PresentationExampleName"</span>
}
</code></pre>
<p>For observers, once the event id is retrieved, we set up observers for the firebase database. The database is handled by application id, (you provide this when setting Firebase up), so all builds of the application will run on the same database. We can then run handlers for every time data is added or changed for that event ID like so. The code below simply displays a delegate which can make your application do what is required. For example this could be, turn the presentation to a new page, fetch a new presentation from an api, etc.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">//Add Firebase Observers and call required changes via the delegate when they are hit</span>
<span class="hljs-keyword">func</span> <span class="hljs-title function_">subscribeToFirebase</span>() {
    <span class="hljs-keyword">self</span>.databaseReference.observe(<span class="hljs-type">DataEventType</span>.childChanged) { (dataSnapshot) <span class="hljs-keyword">in</span>

        <span class="hljs-keyword">if</span> dataSnapshot.key <span class="hljs-operator">==</span> <span class="hljs-keyword">self</span>.eventId {
            <span class="hljs-keyword">let</span> dictionary <span class="hljs-operator">=</span> dataSnapshot.value  <span class="hljs-keyword">as?</span> [<span class="hljs-type">String</span> : <span class="hljs-type">AnyObject</span>] <span class="hljs-operator">??</span> [:]
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> pageNumber <span class="hljs-operator">=</span> dictionary[<span class="hljs-string">"pageNumber"</span>] <span class="hljs-keyword">as?</span> <span class="hljs-type">String</span> {
                <span class="hljs-keyword">self</span>.delegate<span class="hljs-operator">?</span>.slideChanged(manager: <span class="hljs-keyword">self</span>, slideName: pageNumber)
            }
        }
    }

    <span class="hljs-keyword">self</span>.databaseReference.observe(<span class="hljs-type">DataEventType</span>.childAdded) { (dataSnapshot) <span class="hljs-keyword">in</span>

        <span class="hljs-keyword">if</span> dataSnapshot.key <span class="hljs-operator">==</span> <span class="hljs-keyword">self</span>.eventId {
            <span class="hljs-keyword">let</span> dictionary <span class="hljs-operator">=</span> dataSnapshot.value  <span class="hljs-keyword">as?</span> [<span class="hljs-type">String</span> : <span class="hljs-type">AnyObject</span>] <span class="hljs-operator">??</span> [:]
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> pageNumber <span class="hljs-operator">=</span> dictionary[<span class="hljs-string">"pageNumber"</span>] <span class="hljs-keyword">as?</span> <span class="hljs-type">String</span> {
                <span class="hljs-keyword">self</span>.delegate<span class="hljs-operator">?</span>.slideChanged(manager: <span class="hljs-keyword">self</span>, slideName: pageNumber)
            }
        }
    }
}
</code></pre>
<h2>What's next</h2>
<p>I've only recently started exploring the possibilites with these mobile platforms but I've been very impressed. Currently the application I've built only uses a local PDF for testing purposes but it would be really great in the future if we can build a backend where users can upload PDFs and then observers can fetch them from the backend if required &#x26; turn to the slide, all triggered by someone else device. There are many more possibilities with this I’m yet to explore but this proof of concept shows that we can use an application in a controlling mode to display and control data on other devices without observer devices having to do anything.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/MAYsdoYpGuk">Alex Litvin</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[App accelerators]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2018/01/04/App-Accelerators</link>
            <guid>https://engineering.monstar-lab.com/en/post/2018/01/04/App-Accelerators</guid>
            <pubDate>Thu, 04 Jan 2018 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>The competition in the mobile app industry gets bigger and bigger so we have to deliver better and better apps to keep the users hooked. Extra niceness goes a long way in user retention and introducing <strong>app accelerators</strong> is a great example because it’s not just about looks it’s also about functionality.</p>
<p>We all know and use shortcuts in our favourite desktop apps and we should do the same with mobile apps.</p>
<p>What shortcuts you should give the users depends on the app’s purpose. Social media platforms like Facebook, Instagram and Snapchat will let the user create stories/snapchats and send direct messages at one swipe away, while Slack and Facebook Messenger will let you preview messages or users with a forced touch.</p>
<p>You can either make a documentation available for those accelerators or you can let the users discover them as easter eggs in your app. Just remember that shortcuts are an alternative to complete tasks faster and they shouldn’t be the only option.</p>
<p>They don’t only bring value to the users in the short term, but they also bring a lot of value to the clients in the long term. For example, Instagram by making it very easy for their main users who post a lot of content every day in the long run will keep their app relevant.</p>
<h3>Gestures</h3>
<p>Apple has made it extremely easy for developers to implement gestures in the apps, in just a few lines of code you can take advantage of an extra layer of interactivity in your app. You can either use the predefined gestures or create your own.</p>
<p>There are a few standard gestures that Apple’s guidelines advise us to keep the same in our apps. The users would expect, for example, that taps would activate a control or select an item or pinches would zoom in and out. You can check out their article with all of the examples <a href="https://developer.apple.com/ios/human-interface-guidelines/user-interaction/gestures/">here</a>.</p>
<h3>3D Touch</h3>
<p>3D touch brought new UX possibilities with the launch of iPhone 6s and 6s Plus. We can take advantage of the pressure sensitive layer on the screen to improve our app’s user experience. You can either use 3d Touch for quick actions right from the Home screen, let users preview content with peek and pop interactions or even develop features based on the pressure sensing display.</p>
<h2>Get inspired</h2>
<h3><a href="https://slack.com/">Slack</a> - Gestures</h3>
<p>Slack is doing a great job at adding shortcuts in their apps and considering it is a tool used by over six million people daily it makes sense. You can see below some of the accelerators they made available for their users:</p>
<p align="center"><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/accelerators-slack-gestures_eyX3C5YKfB.png"></p>
<h3><a href="https://slack.com/">Slack</a> - Peek and pop - 3d Touch</h3>
<p align="center"><img width="40%" src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/accelerators-peek-and-pop_15bgFVdIlh.gif"></p>
<h3>App icon quick actions - 3d Touch</h3>
<p>Here are some examples of how popular apps use quick actions powered by the 3d touch, some might make our interactions so much faster and some of them might not make such a big difference.</p>
<p align="center"><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/accelerators-app-icon-shortcuts_oYKfUvAOe4.png"></p>
<h3><a href="https://www.nodesagency.com/">Nodes</a> example</h3>
<p>In the Fitness DK app we provide a faster switch between Weekly and Monthly stats in the Tracker by letting the user tap on the background of the screen:</p>
<p align="center"><img width="40%" src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/accelerators-fitness-dk_ce9cGyOPEG.gif"></p>
<h2>Code examples</h2>
<p>Check out our accelerators code examples:</p>
<ul>
<li><a href="/en2017-12-20-UIGestures">UIGestures</a></li>
<li><a href="/en2017-12-20-Custom-Gesture-Recognizers">Custom Gestures</a></li>
</ul>
<h2>Links/Resources</h2>
<ul>
<li><a href="https://developer.apple.com/ios/human-interface-guidelines/user-interaction/gestures/">Apple - Gesture guidelines</a></li>
<li><a href="https://developer.apple.com/ios/3d-touch/">Apple Developer - Take advantage of 3D Touch</a></li>
<li><a href="https://www.raywenderlich.com/167174/design-patterns-mobile-apps-which-why">UX Design pattern for Mobile Apps - Which and Why by Luis Abreu (raywenderlich.com)</a></li>
<li><a href="https://stories.uplabs.com/extra-depth-of-interaction-3d-touch-icons-for-ios-7b0d90c81576">Extra depth of Interaction: 3D Touch Icons for iOS</a></li>
<li><a href="https://get.slack.help/hc/en-us/articles/208401947-Slack-for-iOS">Slack for iOS Shortcuts Article</a></li>
<li><a href="http://psdboom.com/downloads/free-iphone-6-psd-wireframe">Iphone Wireframes by Al Rayhan - PsdBoom</a></li>
</ul>
<p><em>Article Photo by <a href="https://unsplash.com/photos/2rLoAbLT9I4">Oleg Magni</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Custom Gesture Recognizers]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2017/12/20/Custom-Gesture-Recognizers</link>
            <guid>https://engineering.monstar-lab.com/en/post/2017/12/20/Custom-Gesture-Recognizers</guid>
            <pubDate>Wed, 20 Dec 2017 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>If you want to implement a gesture that is not already provided by Apple you can customise your own. However, they recommend using the standard gestures because users are already familiar with them, so take some time to consider if a custom gesture is the right way to go. Custom gestures are usually more present in games so those might be a great place to add your creative gesture recognizers.</p>
<p>There are 2 types of custom gesture recognisers you can define yourself:</p>
<ul>
<li><strong>Discrete</strong> - when your gesture involves a specific pattern of events. Examples: recognising a checkmark, a circle or a chevron (see example below). Those are easier to implement than the continuous ones because of the number of states they can have.</li>
<li><strong>Continuous</strong> - when your gesture does not follow a predefined pattern, when you want to gather user input and there are no failure cases. Example: recognising a user’s drawings on a screen. This usually requires a bit more effort to implement.</li>
</ul>
<h2>Discrete Gesture Recognizer example</h2>
<p>For learning purposes we chose a simple example of recognising a right chevron and open the photo library. In real apps, you might want to look into other standard gestures for an accelerator for opening the photo library.</p>
<h3>State machine</h3>
<p>Before we get into it we should understand the state machine. This will be a recognizer for a discrete gesture (a specific pattern) so we will look into how we manage transitions for this case only. If you want to learn more about the state machine for continuous gestures you can check out <a href="https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/implementing_a_custom_gesture_recognizer/implementing_a_continuous_gesture_recognizer">Apple's documentation</a>.</p>
<p>A gesture recognizer always starts in the <strong>possible</strong> state, that tells us that we are ready to process events. Based on the user's input we can end up in a <strong>failed</strong> (unsuccessful) state or <strong>ended</strong> (successful) state. You have to check if the user is following your pattern and at any deviation from that you should set the state to failed. If the user did indeed follow the expected pattern, UIKit will call the action method associated to the target object.</p>
<p>We also have the <strong>cancelled</strong> state that occurs when the current event is interrupted by a system event like receiving a phone call. You can also customise the cancel situations that can happen in your app. This is to make sure that we don't perform tasks that the user didn't intend.</p>
<p>It is also important to implement a <strong>reset()</strong> method that will make sure we are ready to process new event sequences by returning to starting values.</p>
<h3>Custom gesture recognizer</h3>
<p>The more conditions you have for a gesture to be successful the more precise it will be. For this example we expect that the user will use only one finger for the gesture, makes a down stroke from left to right followed by a down stroke from right to left. Those conditions are leaving open possibilities for edge cases but for the sake of this example we won't look too much into that.</p>
<p>What we have to do is first create the custom gesture recogniser and then add it to our view.</p>
<p><strong>1. Making the custom gesture recognizer</strong></p>
<p>Create a new swift file and import <code>UIKit</code> and <code>UIKit.UIGestureRecognizerSubclass</code> which is a header file that defines the methods and properties we must override to implement our custom gesture recognizer.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> UIKit
<span class="hljs-keyword">import</span> UIKit.UIGestureRecognizerSubclass
</code></pre>
<p>In our case we will have to implement <code>touchesBegin</code>, <code>touchesMoved</code>, <code>touchesEnded</code>, <code>touchesCancelled</code> and <code>reset</code>.</p>
<p>To keep track of the phase of the chevron we can use an <code>enum</code> that we define before the custom gesture recogzizer class. In most cases you would also want to have a <code>notStarted</code> and <code>initialPoint</code> and then you can define the other phases based on your own gesture recognizer. For the chevron we will have a <code>rightDownStroke</code> and a <code>leftDownStroke</code>.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">enum</span> <span class="hljs-title class_">SymbolPhase</span> {
    <span class="hljs-keyword">case</span> notStarted
    <span class="hljs-keyword">case</span> initialPoint
    <span class="hljs-keyword">case</span> rightDownStroke
    <span class="hljs-keyword">case</span> leftDownStroke
}
</code></pre>
<p>Then we start defining our class. We need to declare a <code>strokePhase</code> which has an initial state of <code>notStarted</code>, an initial touch point, which is zero for now and a <code>trackedTouch</code> of type <code>UITouch</code>. UITouch is an object that represents the location, size, movement and force of the touch occurring on the screen, and is an important part of our custom festure recognizers.</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">// Start of the class</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">CustomGestureRecognizer</span> : <span class="hljs-title class_">UIGestureRecognizer</span> {
    <span class="hljs-keyword">var</span> strokePhase : <span class="hljs-type">SymbolPhase</span> <span class="hljs-operator">=</span> .notStarted
    <span class="hljs-keyword">var</span> initialTouchPoint : <span class="hljs-type">CGPoint</span> <span class="hljs-operator">=</span> <span class="hljs-type">CGPoint</span>.zero
    <span class="hljs-keyword">var</span> trackedTouch : <span class="hljs-type">UITouch</span>? <span class="hljs-operator">=</span> <span class="hljs-literal">nil</span>
    <span class="hljs-comment">//Continue below</span>
</code></pre>
<p>The first function we override is the <code>touchesBegan</code>, here we check that we only start our gesture recognizer if there is only a finger on the screen. If there is, then we change the strokePhase to <code>initialPoint</code> and we save the location of the tracked touch into our <code>initialTouchPoint</code>.</p>
<pre><code class="hljs language-swift">    <span class="hljs-keyword">override</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">touchesBegan</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">touches</span>: <span class="hljs-type">Set</span>&#x3C;<span class="hljs-type">UITouch</span>>, <span class="hljs-params">with</span> <span class="hljs-params">event</span>: <span class="hljs-type">UIEvent</span>) {
        <span class="hljs-keyword">super</span>.touchesBegan(touches, with: event)
        <span class="hljs-keyword">if</span> touches.count <span class="hljs-operator">!=</span> <span class="hljs-number">1</span> {
            <span class="hljs-keyword">self</span>.state <span class="hljs-operator">=</span> .failed
        }
        <span class="hljs-comment">// Capture the first touch and store some information about it.</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">self</span>.trackedTouch <span class="hljs-operator">==</span> <span class="hljs-literal">nil</span> {
            <span class="hljs-keyword">self</span>.trackedTouch <span class="hljs-operator">=</span> touches.first
            <span class="hljs-keyword">self</span>.strokePhase <span class="hljs-operator">=</span> .initialPoint
            <span class="hljs-keyword">self</span>.initialTouchPoint <span class="hljs-operator">=</span> (<span class="hljs-keyword">self</span>.trackedTouch<span class="hljs-operator">?</span>.location(in: <span class="hljs-keyword">self</span>.view))<span class="hljs-operator">!</span>
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">// Ignore all but the first touch.</span>
            <span class="hljs-keyword">for</span> touch <span class="hljs-keyword">in</span> touches {
                <span class="hljs-keyword">if</span> touch <span class="hljs-operator">!=</span> <span class="hljs-keyword">self</span>.trackedTouch {
                    <span class="hljs-keyword">self</span>.ignore(touch, for: event)
                }
            }
        }
    }
    <span class="hljs-comment">//Continue below</span>
</code></pre>
<p>Next we implement the <code>touchesMoved</code> which holds the main logic of our gesture tracking. We make sure we are only tracking the first touch and we save the new point and the previous point so we can compare them. We know that after the <code>initialPoint</code> state we should continue with a <code>rightDownStroke</code>, and after the <code>rightDownStroke</code> we should have a <code>leftDownStroke</code>. So we check in what state we are in and if the stroke followed the rules. If it did, we can continue to the next state. You can already see from the code that every time we have a deviation from the rules the state should be set to <code>failed</code>.</p>
<pre><code class="hljs language-swift">    <span class="hljs-keyword">override</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">touchesMoved</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">touches</span>: <span class="hljs-type">Set</span>&#x3C;<span class="hljs-type">UITouch</span>>, <span class="hljs-params">with</span> <span class="hljs-params">event</span>: <span class="hljs-type">UIEvent</span>) {
        <span class="hljs-keyword">super</span>.touchesMoved(touches, with: event)
        <span class="hljs-keyword">let</span> newTouch <span class="hljs-operator">=</span> touches.first
        <span class="hljs-comment">// There should be only the first touch.</span>
        <span class="hljs-keyword">guard</span> newTouch <span class="hljs-operator">==</span> <span class="hljs-keyword">self</span>.trackedTouch <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">self</span>.state <span class="hljs-operator">=</span> .failed
            <span class="hljs-keyword">return</span>
        }
        <span class="hljs-keyword">let</span> newPoint <span class="hljs-operator">=</span> (newTouch<span class="hljs-operator">?</span>.location(in: <span class="hljs-keyword">self</span>.view))<span class="hljs-operator">!</span>
        <span class="hljs-keyword">let</span> previousPoint <span class="hljs-operator">=</span> (newTouch<span class="hljs-operator">?</span>.previousLocation(in: <span class="hljs-keyword">self</span>.view))<span class="hljs-operator">!</span>

        <span class="hljs-keyword">if</span> <span class="hljs-keyword">self</span>.strokePhase <span class="hljs-operator">==</span> .initialPoint {
            <span class="hljs-comment">// Make sure the initial movement is down and to the right.</span>
            <span class="hljs-keyword">if</span> newPoint.x <span class="hljs-operator">>=</span> initialTouchPoint.x <span class="hljs-operator">&#x26;&#x26;</span> newPoint.y <span class="hljs-operator">>=</span> initialTouchPoint.y {
                <span class="hljs-keyword">self</span>.strokePhase <span class="hljs-operator">=</span> .rightDownStroke
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-keyword">self</span>.state <span class="hljs-operator">=</span> .failed
            }
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">self</span>.strokePhase <span class="hljs-operator">==</span> .rightDownStroke {
            <span class="hljs-comment">// Make sure the initial movement is down and to the left.</span>
            <span class="hljs-keyword">if</span> newPoint.y <span class="hljs-operator">>=</span> previousPoint.y {
                <span class="hljs-keyword">if</span> newPoint.x <span class="hljs-operator">&#x3C;=</span> previousPoint.x {
                    <span class="hljs-keyword">self</span>.strokePhase <span class="hljs-operator">=</span> .leftDownStroke
                }
            }  <span class="hljs-keyword">else</span> {
                <span class="hljs-keyword">self</span>.state <span class="hljs-operator">=</span> .failed
            }
        }
    }
    <span class="hljs-comment">//Continue below</span>
</code></pre>
<p>Then we implement what should happen when the touch ended. We verify that the last stroke phase was <code>leftDownStroke</code> and that the final points is below the initial point. If all is good, we set the state to <code>recogznied</code>, if not to <code>failed</code>.</p>
<pre><code class="hljs language-swift">    <span class="hljs-keyword">override</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">touchesEnded</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">touches</span>: <span class="hljs-type">Set</span>&#x3C;<span class="hljs-type">UITouch</span>>, <span class="hljs-params">with</span> <span class="hljs-params">event</span>: <span class="hljs-type">UIEvent</span>) {
        <span class="hljs-keyword">super</span>.touchesEnded(touches, with: event)
        <span class="hljs-keyword">let</span> newTouch <span class="hljs-operator">=</span> touches.first
        <span class="hljs-comment">// There should be only the first touch.</span>
        <span class="hljs-keyword">guard</span> newTouch <span class="hljs-operator">==</span> <span class="hljs-keyword">self</span>.trackedTouch <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">self</span>.state <span class="hljs-operator">=</span> .failed
            <span class="hljs-keyword">return</span>
        }
        <span class="hljs-keyword">let</span> newPoint <span class="hljs-operator">=</span> (newTouch<span class="hljs-operator">?</span>.location(in: <span class="hljs-keyword">self</span>.view))<span class="hljs-operator">!</span>
        <span class="hljs-comment">// If the stroke was down up and the final point is</span>
        <span class="hljs-comment">// below the initial point, the gesture succeeds.</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">self</span>.state <span class="hljs-operator">==</span> .possible <span class="hljs-operator">&#x26;&#x26;</span>
            <span class="hljs-keyword">self</span>.strokePhase <span class="hljs-operator">==</span> .leftDownStroke <span class="hljs-operator">&#x26;&#x26;</span>
            newPoint.y <span class="hljs-operator">></span> initialTouchPoint.y {
            <span class="hljs-keyword">self</span>.state <span class="hljs-operator">=</span> .recognized
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">self</span>.state <span class="hljs-operator">=</span> .failed
        }
    }
    <span class="hljs-comment">//Continue below</span>
</code></pre>
<p>Finally we implement the reset function which sets everything back to initial values so the app can be ready to recognize a new gesture. In the case of a cancelled touch, we reset the values and set the state to <code>cancelled</code>.</p>
<pre><code class="hljs language-swift">    <span class="hljs-keyword">override</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">touchesCancelled</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">touches</span>: <span class="hljs-type">Set</span>&#x3C;<span class="hljs-type">UITouch</span>>, <span class="hljs-params">with</span> <span class="hljs-params">event</span>: <span class="hljs-type">UIEvent</span>) {
       <span class="hljs-keyword">super</span>.touchesCancelled(touches, with: event)
       <span class="hljs-keyword">self</span>.state <span class="hljs-operator">=</span> .cancelled
       reset()
   }

   <span class="hljs-keyword">override</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">reset</span>() {
       <span class="hljs-keyword">super</span>.reset()
       <span class="hljs-keyword">self</span>.initialTouchPoint <span class="hljs-operator">=</span> <span class="hljs-type">CGPoint</span>.zero
       <span class="hljs-keyword">self</span>.strokePhase <span class="hljs-operator">=</span> .notStarted
       <span class="hljs-keyword">self</span>.trackedTouch <span class="hljs-operator">=</span> <span class="hljs-literal">nil</span>
   }

 } <span class="hljs-comment">//End of class</span>
</code></pre>
<p><strong>2. Add it to the view</strong></p>
<pre><code class="hljs language-swift">view.addGestureRecognizer(<span class="hljs-type">CustomGestureRecognizer</span>(target: <span class="hljs-keyword">self</span>, action: <span class="hljs-keyword">#selector</span>(openPhotoLibrary)))
</code></pre>
<p align="center"><img width="40%" src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/custom-gesture-simulator_8LPWcpLqKA.gif"></p>
<h2>Links/Resources</h2>
<ul>
<li><a href="https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/implementing_a_custom_gesture_recognizer/about_the_gesture_recognizer_state_machine">Apple - Gesture Recognizer State Machine</a></li>
<li><a href="https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/implementing_a_custom_gesture_recognizer/implementing_a_discrete_gesture_recognizer">Apple - Implementing a Discrete Gesture Recognizer</a></li>
<li><a href="https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/implementing_a_custom_gesture_recognizer/implementing_a_continuous_gesture_recognizer">Apple - Implementing a Continuous Gesture Recognizer</a></li>
<li><a href="https://developer.apple.com/documentation/uikit/uitouch">Apple - UITouch</a></li>
</ul>
<p>If you are looking for more inspiration you might want to check out those other custom gesture recogniser tutorials:</p>
<ul>
<li><a href="https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/implementing_a_custom_gesture_recognizer/implementing_a_discrete_gesture_recognizer">Checkmark tutorial from Apple</a></li>
<li><a href="https://www.raywenderlich.com/162745/uigesturerecognizer-tutorial-getting-started">Tickle tutorial from raywenderlich.com</a></li>
<li><a href="https://www.raywenderlich.com/104744/uigesturerecognizer-tutorial-creating-custom-recognizers">Circle tutorial from raywenderlich.com</a></li>
</ul>
<p><em>Article Photo by <a href="https://unsplash.com/photos/mfB1B1s4sMc">Christin Hume</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[UIGestures]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2017/12/20/UIGestures</link>
            <guid>https://engineering.monstar-lab.com/en/post/2017/12/20/UIGestures</guid>
            <pubDate>Wed, 20 Dec 2017 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Gestures are the magic behind those accelerators that can make your app more user friendly. We will now show a few examples on how to implement them in your code.</p>
<h2><a href="https://developer.apple.com/documentation/uikit/uiswipegesturerecognizer">Swipe Gestures</a></h2>
<p>You can have many accelerators build with the swipe gesture as it is discrete and you can have many combinations as you can select the direction of the swipe and the number of touches required. Even if we have the possibility to set up a high number of touches, a maximum of 3 is still user friendly. You should also make sure you keep some sort of consistency when defining the gestures, if swipe right with 2 fingers goes to the previous conversation in a messaging app, swiping left with 2 fingers should probably go to the next conversation.</p>
<h3>Using the storyboard</h3>
<p><div class="embed-container">
      <iframe
          src="https://player.vimeo.com/video/244338896"
          width="700"
          height="480"
          frameborder="0"
          webkitallowfullscreen
          mozallowfullscreen
          allowfullscreen>
      </iframe>
    </div></p>
<ol>
<li>Search for the swipe gesture in the objects library</li>
<li>Drag it into your view controller</li>
<li>Set the view of your choice as a delegate to the swipe gesture recognizer (by pressing “Ctrl” on the keyboard and click-drag from the view to the swipe gesture recognizer and selecting Outlet Collections - <code>gestureRecognizers</code> )</li>
<li>Setup your gesture recogniser by selecting the direction and the number of touches required</li>
</ol>
<p><div class="embed-container">
      <iframe
          src="https://player.vimeo.com/video/244338890"
          width="700"
          height="480"
          frameborder="0"
          webkitallowfullscreen
          mozallowfullscreen
          allowfullscreen>
      </iframe>
    </div></p>
<ol start="5">
<li>Connect your Swipe Gesture Recognizer to the code by pressing “Ctrl” in your keyboard and click-drag from the gesture recognizer to the ViewController.</li>
<li>Chose the connection to be an action, name your action and implement the functionality you would want for this specific gesture. In this example we are simply printing in the console to test it all works well.</li>
</ol>
<p><div class="embed-container">
      <iframe
          src="https://player.vimeo.com/video/244338872"
          width="700"
          height="480"
          frameborder="0"
          webkitallowfullscreen
          mozallowfullscreen
          allowfullscreen>
      </iframe>
    </div></p>
<p>Remember that your view should be user interaction enabled. You can either do that from code by setting <code>view.isUserInteractionEnabled = true</code>, or from the storyboard editor - Attributes Inspector - making sure that the <code>User Interaction Enabled</code> is checked.</p>
<h3>Programmatic Swipe Gesture Recognizer</h3>
<p>You can also make gesture recognizers fast only from the code as shown below:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> swipeGestureRecognizer <span class="hljs-operator">=</span> <span class="hljs-type">UISwipeGestureRecognizer</span>(target: <span class="hljs-keyword">self</span>, action: <span class="hljs-keyword">#selector</span>(viewSwipedToLeft))
swipeGestureRecognizer.numberOfTouchesRequired <span class="hljs-operator">=</span> <span class="hljs-number">2</span> <span class="hljs-comment">//The number of fingers required (default is 1)</span>
swipeGestureRecognizer.direction <span class="hljs-operator">=</span> .left <span class="hljs-comment">// The direction of the swipe (default is right)</span>
view.addGestureRecognizer(swipeGestureRecognizer)
</code></pre>
<h2>Other gestures</h2>
<p>Besides the one mentioned above you can also take advantage of other gestures like:</p>
<h3><a href="https://developer.apple.com/documentation/uikit/uitapgesturerecognizer">Tap - UITapGestureRecognizer</a></h3>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> tapGestureRecognizer <span class="hljs-operator">=</span> <span class="hljs-type">UITapGestureRecognizer</span>(target: <span class="hljs-keyword">self</span>, action: <span class="hljs-keyword">#selector</span>(viewTapped))
tapGestureRecognizer.numberOfTapsRequired <span class="hljs-operator">=</span> <span class="hljs-number">2</span> <span class="hljs-comment">// The number of taps required (default is 1)</span>
tapGestureRecognizer.numberOfTouchesRequired <span class="hljs-operator">=</span> <span class="hljs-number">2</span> <span class="hljs-comment">// The number of fingers required (default is 1)</span>
view.addGestureRecognizer(tapGestureRecognizer)
</code></pre>
<h3><a href="https://developer.apple.com/documentation/uikit/uipinchgesturerecognizer">Pinch - UIPinchGestureRecognizer</a></h3>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> pinchGestureRecognizer <span class="hljs-operator">=</span> <span class="hljs-type">UIPinchGestureRecognizer</span>(target: <span class="hljs-keyword">self</span>, action: <span class="hljs-keyword">#selector</span>(viewPinched))
view.addGestureRecognizer(pinchGestureRecognizer)
<span class="hljs-comment">// You can get the scale factor relative to the points of the two touches on the screen (pinchGestureRecognizer.scale)</span>
<span class="hljs-comment">// and the velocity of the pinch in scale factor/second (pinchGestureRecognizer.velocity)</span>
</code></pre>
<h3><a href="https://developer.apple.com/documentation/uikit/uirotationgesturerecognizer">Rotation UIRotationGestureRecognizer</a></h3>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> rotationGestureRecognizer <span class="hljs-operator">=</span> <span class="hljs-type">UIRotationGestureRecognizer</span>(target: <span class="hljs-keyword">self</span>, action: <span class="hljs-keyword">#selector</span>(viewRotated))
view.addGestureRecognizer(rotationGestureRecognizer)
<span class="hljs-comment">// You can get the rotation in radians (rotationGestureRecognizer.rotation)</span>
<span class="hljs-comment">// and the velocity of the rotation in radians/second (rotationGestureRecognizer.velocity)</span>
</code></pre>
<h3><a href="https://developer.apple.com/documentation/uikit/uipangesturerecognizer">Pan (dragging) - UIPanGestureRecognizer</a></h3>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> panGestureRecognizer <span class="hljs-operator">=</span> <span class="hljs-type">UIPanGestureRecognizer</span>(target: <span class="hljs-keyword">self</span>, action: <span class="hljs-keyword">#selector</span>(viewPanned))
panGestureRecognizer.maximumNumberOfTouches <span class="hljs-operator">=</span> <span class="hljs-number">2</span> <span class="hljs-comment">// Max number of fingers required (default is NSUIntegerMax)</span>
panGestureRecognizer.minimumNumberOfTouches <span class="hljs-operator">=</span> <span class="hljs-number">2</span> <span class="hljs-comment">// Min number of fingers required (default is 1)</span>
view.addGestureRecognizer(panGestureRecognizer)
</code></pre>
<h3><a href="https://developer.apple.com/documentation/uikit/uiscreenedgepangesturerecognizer">Screen edge pan - UIScreenEdgePanGestureRecognizer</a></h3>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> screenEdgeGestureRecognizer <span class="hljs-operator">=</span> <span class="hljs-type">UIScreenEdgePanGestureRecognizer</span>(target: <span class="hljs-keyword">self</span>, action: <span class="hljs-keyword">#selector</span>(edgePanGestureRecognized))
screenEdgeGestureRecognizer.edges <span class="hljs-operator">=</span> .left <span class="hljs-comment">//The acceptable starting edges for the gesture (UIRectEdge)</span>
view.addGestureRecognizer(screenEdgeGestureRecognizer)
</code></pre>
<h3><a href="https://developer.apple.com/documentation/uikit/uilongpressgesturerecognizer">Long press - UILongPressGestureRecognizer</a></h3>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> longPressGestureRecognizer <span class="hljs-operator">=</span> <span class="hljs-type">UILongPressGestureRecognizer</span>(target: <span class="hljs-keyword">self</span>, action: <span class="hljs-keyword">#selector</span>(longPressRecognized))
longPressGestureRecognizer.minimumPressDuration <span class="hljs-operator">=</span> <span class="hljs-number">2</span> <span class="hljs-comment">//The minimum period fingers must press on the view (in seconds, default is 0.5 seconds)</span>
longPressGestureRecognizer.numberOfTouchesRequired <span class="hljs-operator">=</span> <span class="hljs-number">2</span> <span class="hljs-comment">// The number of fingers that must be pressed on the view (default is 1)</span>
longPressGestureRecognizer.numberOfTapsRequired <span class="hljs-operator">=</span> <span class="hljs-number">0</span> <span class="hljs-comment">// The number of taps required (default is 0)</span>
longPressGestureRecognizer.allowableMovement <span class="hljs-operator">=</span> <span class="hljs-number">10</span> <span class="hljs-comment">// The maximum movement of the fingers on the view measured in points (default is 10 points)</span>
view.addGestureRecognizer(longPressGestureRecognizer)
</code></pre>
<h2>Links/Resources</h2>
<ul>
<li><a href="https://developer.apple.com/ios/human-interface-guidelines/user-interaction/gestures/">Apple - Gesture guidelines</a></li>
<li><a href="https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/handling_uikit_gestures/handling_pinch_gestures">Apple - Handing pinch gestures documentation</a></li>
</ul>
<p><em>Article Photo by <a href="https://unsplash.com/photos/Y3LGWCsrgmg">Daniel Korpai</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Vapor code generation with Sourcery]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2017/12/20/vapor-code-generation-with-sourcery</link>
            <guid>https://engineering.monstar-lab.com/en/post/2017/12/20/vapor-code-generation-with-sourcery</guid>
            <pubDate>Wed, 20 Dec 2017 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Swift is a relatively new language in the server realm that brings a lot of nice changes, such as type safety and compilation. But it's not all sunshine and rainbows. Swift's poor metaprogramming can introduce a huge amount of boilerplate into Swift projects, including Vapor projects.</p>
<h2>Creating a model in Vapor without Sourcery</h2>
<p>For some context, let's take a look at a typical model.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">internal</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span>: <span class="hljs-title class_">Model</span>, <span class="hljs-title class_">Timestampable</span>, <span class="hljs-title class_">SoftDeletable</span> {
    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">enum</span> <span class="hljs-title class_">Keys</span> {
        <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> email <span class="hljs-operator">=</span> <span class="hljs-string">"email"</span>
        <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> name <span class="hljs-operator">=</span> <span class="hljs-string">"name"</span>
        <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> password <span class="hljs-operator">=</span> <span class="hljs-string">"password"</span>
        <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> passwordRepeat <span class="hljs-operator">=</span> <span class="hljs-string">"passwordRepeat"</span>
        <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> companyId <span class="hljs-operator">=</span> <span class="hljs-string">"companyId"</span>
    }

    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">let</span> storage <span class="hljs-operator">=</span> <span class="hljs-type">Storage</span>()

    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">var</span> email: <span class="hljs-type">String</span>
    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">var</span> name: <span class="hljs-type">String</span>?
    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">var</span> password: <span class="hljs-type">String</span>
    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">var</span> companyId: <span class="hljs-type">Identifier</span>

    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">init</span>(
        <span class="hljs-params">email</span>: <span class="hljs-type">String</span>,
        <span class="hljs-params">name</span>: <span class="hljs-type">String</span>?,
        <span class="hljs-params">password</span>: <span class="hljs-type">String</span>,
        <span class="hljs-params">companyId</span>: <span class="hljs-type">Identifier</span>
    ) {
        <span class="hljs-keyword">self</span>.email <span class="hljs-operator">=</span> email
        <span class="hljs-keyword">self</span>.name <span class="hljs-operator">=</span> name
        <span class="hljs-keyword">self</span>.password <span class="hljs-operator">=</span> password
        <span class="hljs-keyword">self</span>.companyId <span class="hljs-operator">=</span> companyId
    }

    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">init</span>(<span class="hljs-params">row</span>: <span class="hljs-type">Row</span>) <span class="hljs-keyword">throws</span> {
        name <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> row.get(<span class="hljs-type">Keys</span>.name)
        email <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> row.get(<span class="hljs-type">Keys</span>.email)
        password <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> row.get(<span class="hljs-type">Keys</span>.password)
        companyId <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> row.get(<span class="hljs-type">Keys</span>.companyId)
    }
}

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">User</span>: <span class="hljs-title class_">JSONRepresentable</span> {
    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">enum</span> <span class="hljs-title class_">JSONKeys</span> {
        <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> name <span class="hljs-operator">=</span> <span class="hljs-type">Keys</span>.name
        <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> email <span class="hljs-operator">=</span> <span class="hljs-type">Keys</span>.email
        <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> companyId <span class="hljs-operator">=</span> <span class="hljs-type">Keys</span>.companyId
    }

    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">makeJSON</span>() <span class="hljs-keyword">throws</span> -> <span class="hljs-type">JSON</span> {
        <span class="hljs-keyword">var</span> json <span class="hljs-operator">=</span> <span class="hljs-type">JSON</span>()
        <span class="hljs-keyword">try</span> json.set(idKey, id)
        <span class="hljs-keyword">try</span> json.set(<span class="hljs-type">JSONKeys</span>.name, name)
        <span class="hljs-keyword">try</span> json.set(<span class="hljs-type">JSONKeys</span>.email, email)
        <span class="hljs-keyword">try</span> json.set(<span class="hljs-type">JSONKeys</span>.companyId, companyId)
        <span class="hljs-keyword">return</span> json
    }
}

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">User</span>: <span class="hljs-title class_">NodeRepresentable</span> {
    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">makeNode</span>(<span class="hljs-params">in</span> <span class="hljs-params">context</span>: <span class="hljs-type">Context</span>?) <span class="hljs-keyword">throws</span> -> <span class="hljs-type">Node</span> {
        <span class="hljs-keyword">var</span> node <span class="hljs-operator">=</span> <span class="hljs-type">Node</span>([:])
        <span class="hljs-keyword">try</span> node.set(idKey, id)
        <span class="hljs-keyword">try</span> node.set(<span class="hljs-type">Keys</span>.name, name)
        <span class="hljs-keyword">try</span> node.set(<span class="hljs-type">Keys</span>.email, email)
        <span class="hljs-keyword">try</span> node.set(<span class="hljs-type">Keys</span>.companyId, companyId)
        <span class="hljs-keyword">try</span> node.set(<span class="hljs-type">User</span>.createdAtKey, createdAt)
        <span class="hljs-keyword">try</span> node.set(<span class="hljs-type">User</span>.updatedAtKey, updatedAt)
        <span class="hljs-keyword">try</span> node.set(<span class="hljs-string">"company"</span>, company.get())
        <span class="hljs-keyword">return</span> node
    }
}

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">User</span>: <span class="hljs-title class_">Preparation</span> {
    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">prepare</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">database</span>: <span class="hljs-type">Database</span>) <span class="hljs-keyword">throws</span> {
        <span class="hljs-keyword">try</span> database.create(<span class="hljs-keyword">self</span>) { user <span class="hljs-keyword">in</span>
            user.id()
            user.string(<span class="hljs-type">Keys</span>.email, unique: <span class="hljs-literal">true</span>)
            user.string(<span class="hljs-type">Keys</span>.name, optional: <span class="hljs-literal">true</span>)
            user.string(<span class="hljs-type">Keys</span>.password)
            user.string(<span class="hljs-type">Keys</span>.companyId)
        }


        <span class="hljs-keyword">try</span> database.index(<span class="hljs-type">Keys</span>.email, for: <span class="hljs-type">User</span>.<span class="hljs-keyword">self</span>)
        <span class="hljs-keyword">try</span> database.index(<span class="hljs-type">Keys</span>.companyId, for: <span class="hljs-type">User</span>.<span class="hljs-keyword">self</span>)
    }

    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">revert</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">database</span>: <span class="hljs-type">Database</span>) <span class="hljs-keyword">throws</span> {
        <span class="hljs-keyword">try</span> database.delete(<span class="hljs-keyword">self</span>)
    }
}

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">User</span> {
    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">makeRow</span>() <span class="hljs-keyword">throws</span> -> <span class="hljs-type">Row</span> {
        <span class="hljs-keyword">var</span> row <span class="hljs-operator">=</span> <span class="hljs-type">Row</span>()
        <span class="hljs-keyword">try</span> row.set(<span class="hljs-type">Keys</span>.email, email)
        <span class="hljs-keyword">try</span> row.set(<span class="hljs-type">Keys</span>.name, name)
        <span class="hljs-keyword">try</span> row.set(<span class="hljs-type">Keys</span>.password, password)
        <span class="hljs-keyword">try</span> row.set(<span class="hljs-type">Keys</span>.companyId, companyId)
        <span class="hljs-keyword">return</span> row
    }
}
</code></pre>
<p>As you can see, it's quite a bit of code. The <code>makeRow</code> and <code>init(row:)</code> are for serialization and deserialization of database entries, <code>makeJSON</code> is for JSON serialization, <code>makeNode(context:)</code> is for legacy libraries and rendering views and finally, <code>prepare(database:)</code> and <code>revert(database:)</code> are for setting up and tearing down the database schema. This is the minimum amount of boilerplate required in most cases, but there are many cases where you can conform to more protocols.</p>
<p>After spending most of our days duplicating this relatively static data, we started looking for ways to automate it. This is where <a href="https://github.com/krzysztofzablocki/Sourcery">Sourcery</a> comes in.</p>
<h2>Sourcery</h2>
<p>Taken from the GitHub repository:</p>
<blockquote>
<p>Sourcery is a code generator for Swift language, built on top of Apple's own SourceKit. It extends the language abstractions to allow you to generate boilerplate code automatically.</p>
</blockquote>
<p>Sourcery is a tool that has been adopted well throughout the iOS and macOS community and it seemed like a natural fit for solving our challenges. Sourcery is built around the idea of "meta-programming". In short, this means that we're able to write code at a meta level where the result of executing this code will result in another program. To be more concrete, we're now able to write templates that describes what code we want to create in a generic way. When we then run Sourcery, it will generate all the concrete implementations for our project.</p>
<p>Let's have a look at a small example. Given this small piece of code for a model in Vapor:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span>: <span class="hljs-title class_">Model</span> {
    <span class="hljs-keyword">var</span> name: <span class="hljs-type">String</span>
    <span class="hljs-keyword">var</span> age: <span class="hljs-type">Int</span>?
}
</code></pre>
<p>We can then write a small template for Sourcery, e.g. by using <a href="http://stencil.fuller.li/en/latest/">Stencil</a>, to generate the corresponding initializer:</p>
<pre><code class="hljs language-typescript">{% raw %}
{% <span class="hljs-keyword">for</span> <span class="hljs-keyword">type</span> <span class="hljs-keyword">in</span> types.<span class="hljs-property">based</span>.<span class="hljs-property">Model</span> %}
    <span class="hljs-comment">// sourcery:inline:auto:{{ type.name }}.Models</span>
    internal <span class="hljs-keyword">let</span> storage = <span class="hljs-title class_">Storage</span>()

    internal <span class="hljs-title function_">init</span>(<span class="hljs-params">
        {% <span class="hljs-keyword">for</span> <span class="hljs-keyword">var</span> <span class="hljs-keyword">in</span> <span class="hljs-keyword">type</span>.storedVariables %}
        {{ <span class="hljs-keyword">var</span>.name }}: {{ <span class="hljs-keyword">var</span>.typeName.description }}{% <span class="hljs-keyword">if</span> <span class="hljs-keyword">var</span>.isOptional %} = nil{% endif %}{% <span class="hljs-keyword">if</span> not forloop.last %},{% endif %}
        {% endfor %}
    </span>) {
        {% <span class="hljs-keyword">for</span> <span class="hljs-keyword">var</span> <span class="hljs-keyword">in</span> <span class="hljs-keyword">type</span>.<span class="hljs-property">storedVariables</span> %}
        self.{{ <span class="hljs-keyword">var</span>.<span class="hljs-property">name</span> }} = {{ <span class="hljs-keyword">var</span>.<span class="hljs-property">name</span> }}
        {% endfor %}
    }
    <span class="hljs-comment">// sourcery:end</span>
{% endfor %}
{% endraw %}
</code></pre>
<p>After running Sourcery, we will then end up with an updated <code>User</code> model which now looks like this:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span>: <span class="hljs-title class_">Model</span> {
    <span class="hljs-keyword">var</span> name: <span class="hljs-type">String</span>
    <span class="hljs-keyword">var</span> age: <span class="hljs-type">Int</span>?

<span class="hljs-comment">// sourcery:inline:auto:User.Models</span>
    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">let</span> storage <span class="hljs-operator">=</span> <span class="hljs-type">Storage</span>()

    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">init</span>(
        <span class="hljs-params">name</span>: <span class="hljs-type">String</span>,
        <span class="hljs-params">age</span>: <span class="hljs-type">Int</span>? <span class="hljs-operator">=</span> <span class="hljs-literal">nil</span>
    ) {
        <span class="hljs-keyword">self</span>.name <span class="hljs-operator">=</span> name
        <span class="hljs-keyword">self</span>.age <span class="hljs-operator">=</span> age
    }
<span class="hljs-comment">// sourcery:end</span>
}
</code></pre>
<p>In this case we made Sourcery generate code inline in our existing file, but we could also have chosen to have it generate new files for us. By doing this, it becomes a bit more clear what code is generated by Sourcery and what is not (although it might not always be possible).</p>
<p>Armed with this amazing tool, we built a <a href="https://github.com/nodes-vapor/sourcery-templates">collection of templates</a> that tries to eliminate most of Vapor's boilerplate. The overall guidelines for our templates are:</p>
<ul>
<li>That they should be easy to opt-in and out of.</li>
<li>That it should be easy to mix and match between generated code and user-written code.</li>
<li>That it should be clear what has been generated by Sourcery.</li>
<li>That they should cover the common cases, but not all cases; edge cases should be solved by opting out of Sourcery for the specific case.</li>
</ul>
<p>The templates are mostly focused around generating code for models, but they also include implementations for routes, the <code>LinuxMain.swift</code> file and more.</p>
<h2>Creating a model in Vapor with Sourcery</h2>
<p>Coming back to our previous example of how to create a user model in Vapor, let's have a look at how that looks like now using our Sourcery templates. To define the previously defined model, all you need to write is the following:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span>: <span class="hljs-title class_">Model</span>, <span class="hljs-title class_">Timestampable</span>, <span class="hljs-title class_">SoftDeletable</span> {
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> email: <span class="hljs-type">String</span>
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> name: <span class="hljs-type">String</span>?
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> password: <span class="hljs-type">String</span>
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> companyId: <span class="hljs-type">Identifier</span>
}
</code></pre>
<p>After running <code>sourcery</code> in the project directory, the templates will generate all of the boilerplate for the initializers, <code>RowConvertible</code>, <code>NodeRepresentable</code>, <code>JSONConvertible</code> and <code>Preparation</code>. The templates also include helpers for enumerations, route collections and unit testing. Most of the generated files will be created in a <code>Generated</code> folder like this:</p>
<ul>
<li><code>App/Generated/User/User+JSONConvertible.generated.swift</code></li>
<li><code>App/Generated/User/User+NodeRepresentable.generated.swift</code></li>
<li><code>App/Generated/User/User+Preparation.generated.swift</code></li>
<li><code>App/Generated/User/User+RowConvertible.generated.swift</code></li>
</ul>
<p>Making it very clear what has been generated and that those files shouldn't be manually edited. Further, the templates has a range of options when it comes to customizing the generated code. Please have a look at the <a href="https://github.com/nodes-vapor/sourcery-templates">readme</a> for a full overview of the available configurations.</p>
<h2>Looking ahead</h2>
<p>Templating the Vapor interfaces has some not-so-obvious advantages. For instance, when the Vapor APIs are updated we can, theoretically, update the templates and the rest of the project will get updated automatically. That being said, Vapor 3 looks like it's going to completely redefine the framework and we're not sure how realistic this is. Besides that, we have a couple of ideas for future features such as generating convenience functions for relations, generating type-safe routes and more.</p>
<p>Thankfully, the future of server side Swift looks promising. Swift 4 brings codeable and Vapor 3 will fully embrace it. Until Vapor 3's release and we update all of our projects, we will continue to enjoy the quality of life improvement Sourcery brings to Vapor.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/feXpdV001o4">Anas Alshanti</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How we debug with Charles]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2017/12/18/How-we-debug-with-Charles</link>
            <guid>https://engineering.monstar-lab.com/en/post/2017/12/18/How-we-debug-with-Charles</guid>
            <pubDate>Mon, 18 Dec 2017 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>For those who don't know <a href="https://www.charlesproxy.com">Charles</a>, Charles is an HTTP proxy and monitor that enables testers and developers to view all HTTP and HTTPS traffic between the device and the internet. This includes requests, responses and the HTTP headers. Charles is an incredibly powerful tool in debugging mobile and web applications. This post of the Nodes Engineering Blog explores the debugging capabilities of Charles used daily by our QA and Application Mangement Team.</p>
<h2>Top 5 debugging tools</h2>
<p>Charles has many built in tools that can do various things. This post will aim to describe the top 5 tools we use in Nodes QA for debugging our mobile applications. These are the following:</p>
<ol>
<li>Monitor all traffic between API and application (Recording).</li>
<li>Simulating network conditions (Throttle).</li>
<li>Rewriting the API (Rewrite).</li>
<li>Simulating API responses (Map local).</li>
<li>Interrupting API calls (Breakpoints).</li>
</ol>
<h3>1. Monitor all traffic</h3>
<p>The most common thing you can do with Charles, is to have a peek underneath the hood of the application, also known as 'Recording'. By default in Charles, when you start to record traffic you will be looking at the structure tab, that lists all the executed requests organised by URL. All traffic in Charles is stored in the current session, which acts as a log over all traffic. Each request appears in the session window when they are recorded. The session allows testers to look into all traffic coming from the app: from opening the app for the first time, to creating users, to logging in or logging out. We make sure to keep Charles running in the background while performing all our tests to the application. If the app suddenly fails during testing, we can easily 'go back in time' and investigate why, by looking at the requests in the session. Here are a few scenarios in which Charles is a great help in debugging the application:</p>
<ul>
<li>When the app is making network requests to an API or a third party provider and you are not receiving the expected results in the app.</li>
<li>The app displays an empty view, when the app is supposed to fetch data from the API – you need to make sure to check whether data is received correctly from the API.</li>
<li>If you are making changes in the app and the changes are not applied – you need to check if the app is sending the data correctly or whether you are receiving the processed data from the server unexpectedly.</li>
<li>Any other scenario that involves the app making or receiving network calls – you need to check your Charles!</li>
</ul>
<p>With Charles, we are not only able to monitor the implementation of API, we can also monitor the implementation of third party providers such as push providers, Google Analytics, Hockey and furthermore we can monitor the order in which they are implemented. To ensure an agile workflow and a quick response to the issues that are found, Charles sessions can be exported and shared across the team. This 'comes in handy' when a tester is discovering an issue and wants to show the developer what is happening without the aforementioned steps to reproduce the issue. By exporting the session from Charles and adding it as an attachment in the ticket, it becomes very easy for the developer to reproduce the problem.</p>
<p>Charles also has a built in way to publish gists to Github. The gist will automatically contain all required information in order to reproduce the issue such as auth token, request headers, API path, API environment, etc. This makes the workflow around discovering and reporting bugs in API or API implementation very quick and agile.</p>
<p>The attached example beneath is a screenshot of a Charles session, in which I am recording the traffic in the Apple Store App for iOS. On app open we can for example see that the app calls a Content Delivery Network to get a feature graphic of an iPhone X.</p>
<p>For testers the recording tool in Charles is the most important tool in the Charles toolbox. It gives us an insight into how mobile applications are built and furthermore allows us to identify issues on a lower level than otherwise possible.</p>
<p><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/tool_recording1_vdLLomVrtU.png" alt="Recording1"></p>
<p><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/tool_recording2_8oTLVyS2PB.jpeg" alt="Recording2"></p>
<h3>2. Simulating network conditions</h3>
<p>One of the built-in-tools that we use while testing is 'Throttle'. Throttle is used to adjust the bandwidth and latency of the current internet connection. This enables us to simulate a 3G network condition using a high-speed connection. By simulating poor network conditions, we can make sure that our mobile applications handle poor internet connectivity and that timeouts are correctly implemented in the app. We can check if assets are served to the app in a suited file size as well as checking if assets are being cached correctly. This gives us, developers and testers, an insight into how the user experience will be in that scenario. Testing on slow connection speeds is an often forgotten aspect to test for, since most of the apps out there are developed and maintained on high-speed internet connections. However doing so gives us a more realistic sense of the end user experience.</p>
<p>The throttle functionality is quickly enabled and disabled from the Charles toolbar by clicking on the icon of a turtle. Charles offers a few options for speed restrictions to simulate different network connections, as shown in the screenshots below:</p>
<p><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/tool_throttle1_ZQ2qihmQCZ.png" alt="throttle1"></p>
<h3>3. Rewriting the API</h3>
<p>When developing mobile applications sometimes the requests made by the app or the responses made by the API are not exactly what we need them to be. With the Charles 'Rewrite' tool we can manipulate an url request on the fly (change the header, modify query params, etc). The rewrite tool can be useful for doing various tricks such as rewriting the authentication token, rewriting the API environment (by modifying the API path), as well as rewriting the status codes of the response.</p>
<p>Another known trick is to inject a custom request header to be able to trace our request in the server logs seperately from the rest. We use rewrite as a mean to quickly give us access to view the app as a specific user by rewriting the authentication headers. This will allow us to test the app as that type of user, without having to spent time entering username and password.</p>
<p>During development of the app, the developer might want to quickly change the API environment. He or she is then able to rewrite the API url from development to production environment without having to recompile or reconfigure either client or server. We make sure to always test for error handling when it comes to status codes returned by API. In some projects, it may be difficult to simulate certain status codes no matter how hard you try breaking things. In that case we can use rewrite. In the screenshots below I am rewriting the landing page of the iOS Apple Store App by changing the url of the iPhone X image to a an image of a Samsung Galaxy S8.</p>
<p><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/tool_rewrite1_3CeaIAAvHJ.png" alt="rewrite1"></p>
<p><em>First we create a rewrite rule in order to replace the URL of the iPhone X image</em></p>
<p><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/tool_rewrite2_hljikhsImW.jpeg" alt="rewrite2"></p>
<p><em>We then reopen the app to force the app to make the request again</em></p>
<p><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/tool_rewrite3_UynlzcU5tw.png" alt="rewrite3"></p>
<p><em>Checking in our session, we can now see that the rewrite rule has been applied to the request</em></p>
<p>We can use the rewrite functionality on any requests, and multiple rewrites can be configured at the same time. Rewrites are encapsulated into rule sets, which can be saved in your Charles config for future use. Rewrite rules can be disabled and enabled as you please along the testing. As you can see, the options of rewriting are many. There are various reasons for why you might need to add rewrite rules, but these are the most common use cases for us:</p>
<ul>
<li>Rewriting status codes</li>
<li>Rewriting API url</li>
<li>Rewriting authentication headers</li>
<li>Rewriting headers to alter user agent</li>
<li>Injecting extra headers</li>
<li>Injecting response headers</li>
<li>Adding a find-and-replace rule on the body of a request or the response</li>
</ul>
<h3>4. Simulating API responses</h3>
<p>With the 'Map local' tool testers have the ability to modify the responses on the fly. The 'Map local' tool changes the response of the request, per the configured mappings, so that the response is transparently served from the new location locally as if that was the original request. In other words the map local will allow testers to simulate responses from each request.</p>
<p>We use this tool to quickly modify the response body locally in order to test how app reacts to it. The simplest example could be modifying a response from an API call to see if the app UI can handle longer text values. Another use case, is when testers or developers are not getting the expected response from the API either due to new features not being deployed or bugs in the API. In that case we can modify the API response locally to the expected in order to be able to continue testing or developing. We often see that logic, which depends on certain properties/values in the response from the API, is difficult to test for.
Sometimes, testers will need to go through complicated flows in the app in order to have the response properties updated accordingly. Here the map local comes in handy, as we can quickly modify the response and change the required parameter to what we need, in order to be able to test.</p>
<h3>5. Interrupting API calls</h3>
<p>Another nifty feature of Charles Proxy is called 'Breakpoints'. Breakpoints allows you to specify any host as a breakpoint. Once the app makes a request or receives a response from something that matches your breakpoint, it will pause the request/response to give you an opportunity to examine or edit the request/response. Eventually you can decide whether to allow it to proceed or to block it. We use the breakpoints tools for interrupting the calls to API by aborting them. We do this to see how the app reacts to it, whether the app breaks or an error message is displayed. The breakpoints tool is also useful for modifying the request/response values to try and break the app in order to find security vulnerabilities. The breakpoints can be configured in breakpoints settings and they are quickly turned on or off by clicking the red hexagonal button in the Charles toolbar.</p>
<p><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/tool_breakpoints_1_qMtFVRMWsi.png" alt="Breakpoints1"></p>
<h2>Get started with Charles</h2>
<p>These five tools are all very useful when it comes to developing and testing mobile applications. The biggest advantage for developers is that they won't need to recompile or reconfigure either client or server to make these changes, Charles will act as middleman and make the changes on the fly. During development you might want to quickly change whether app hooks up to development or production API, this is a very quick and easy thing to do in Charles. The biggest advantage for testers is that they can get a peak under the hood of the application and quickly find and identify issues. Using Charles gives you the ability to put on your 'hacker hat' and try to debug or break the app. Charles saves you a lot of time and helps you analyze, debug and fix the issues of your application.</p>
<p>This blog post aimed to give you an overview of some of the most useful debugging capabilities of Charles for mobile developers and testers. It aimed to give you an insight in how we use charles for developing and debugging in Nodes QA. You can start by downloading a free copy of Charles Proxy from the website: <a href="http://www.charlesproxy.com/">http://www.charlesproxy.com/</a>. Charles is supported on Windows, Mac and Linux. Under the section documentation you will find the instructions for installing the tool as well as how to setup and get started.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Adding Peek and Pop to your View Controllers]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2017/11/27/Adding-Peek-and-Pop-to-your-View-Controllers</link>
            <guid>https://engineering.monstar-lab.com/en/post/2017/11/27/Adding-Peek-and-Pop-to-your-View-Controllers</guid>
            <pubDate>Mon, 27 Nov 2017 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>With iOS 9, new iPhone models add a third dimension to the user interface, called force touch, or more commonly 3D Touch.</p>
<p>A user can now press your Home screen icon to immediately access functionality provided by your app.
Within your app, a user can now press certain views to see previews of additional content and gain accelerated access to features.</p>
<h2>Peek and Pop</h2>
<p>iOS 9 lets you configure view controllers to allow the use of peek, which provides a preview of additional content when a user presses on a specified view, and pop, which commits to viewing that content and navigates to it.</p>
<p>This interaction proceeds through three phases.</p>
<ol>
<li>Indication that content preview is available</li>
<li>Display of the preview, known as a peek, with options to act on it directly, known as peek quick actions</li>
<li>Optional navigation to the view shown in the preview, known as a pop</li>
</ol>
<p align="center">
<img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/14217-9579-Screenshot-2015-09-11-034807-l_oCe51n1tRJ.jpg?width=280" style="margin:20px">
</p>
<h2>Setup</h2>
<p>Lets imagine that we have 2 <code>UIViewController</code>s in our app. One has a <code>UICollectionView</code> as its main UI, for example to show a list of images, we shall call it ImageViewController. The other one is a DetailViewController that will be presented if one of the images is pressed. This should be a very common scenario in most apps.</p>
<p>If we want to use Peek and Pop we first have to check wether 3D touch is supported and if it is, register the previewing delegate.</p>
<pre><code class="hljs language-objc"><span class="hljs-keyword">class</span> ImageViewController: <span class="hljs-built_in">UIViewController</span> {

  override func viewDidLoad() {
      <span class="hljs-variable language_">super</span>.viewDidLoad()

      <span class="hljs-comment">// Do your usual setup</span>
      ...

      <span class="hljs-comment">// Register peek and pop if available</span>
      guard traitCollection.forceTouchCapability == .available <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> }
      registerForPreviewing(with: <span class="hljs-keyword">self</span>, sourceView: view)
    }
}
</code></pre>
<h2>Implement The Delegate</h2>
<p>Next we need to implement the above registered delegate, called <code>UIViewControllerPreviewingDelegate</code>. This delegate has 2 methods, 1 for peeking and 1 for popping.</p>
<pre><code class="hljs language-objc"><span class="hljs-keyword">class</span> ImageViewController: <span class="hljs-built_in">UIViewController</span> {
    ...
}

<span class="hljs-comment">// MARK: - UI View Controller Previewing Delegate</span>

extension ImageViewController: <span class="hljs-built_in">UIViewControllerPreviewingDelegate</span> {

    <span class="hljs-comment">/// Peek</span>
    func previewingContext(_ previewingContext: <span class="hljs-built_in">UIViewControllerPreviewing</span>, viewControllerForLocation location: <span class="hljs-built_in">CGPoint</span>) -> <span class="hljs-built_in">UIViewController</span>? {

      <span class="hljs-comment">// Get the index path of the cell we are force touching on</span>
      guard let indexPath = collectionView.indexPathForItem(at: location) <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span> }

      <span class="hljs-comment">// Get the actual cell instance for the index path</span>
      guard let cell = collectionView.cellForItem(at: indexPath) <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span> }

      <span class="hljs-comment">// Instantiate the detail view controller</span>
      guard let detailVC = storyboard?.instantiateViewController(withIdentifier: <span class="hljs-string">"DetailViewController"</span>) as? DetailViewController <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span> }

      <span class="hljs-comment">// Update the detail view controllers data source</span>
      let image = images[indexPath.row]
      detailVC.image = image

      <span class="hljs-comment">// Set the content size for the detail view controller</span>
      detailVC.preferredContentSize = <span class="hljs-built_in">CGSize</span>(width: <span class="hljs-number">0</span>, height: <span class="hljs-number">300</span>)

      <span class="hljs-comment">// Set the source rect of the previewing context</span>
      previewingContext.sourceRect = cell.frame

      <span class="hljs-comment">// Return the view controller for peeking</span>
      <span class="hljs-keyword">return</span> detailVC
    }

    <span class="hljs-comment">/// Pop</span>
    func previewingContext(_ previewingContext: <span class="hljs-built_in">UIViewControllerPreviewing</span>, commit viewControllerToCommit: <span class="hljs-built_in">UIViewController</span>) {
        showViewController(viewControllerToCommit, sender: <span class="hljs-keyword">self</span>)
    }
}
</code></pre>
<p>For peeking, the logic is almost similar to using Segues. We need to get a reference to the cell we are touching and instantiate the DetailViewController and update its data source. We also need to set a preferred content size of the DetailViewController and finally we need to set the <code>sourceRect</code> of the previewing context to the frame of the cell we are touching.</p>
<p>Popping on the other hand is very straightforward, simply show the view controller that your are peeking at.</p>
<h2>Previewing Actions</h2>
<p>If you now run your app and force touch on an image you should be able to get a preview of the DetailViewController.</p>
<p align="center">
<img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/peek-pop_PgLJZtnHgg.jpg?width=280" style="margin:20px;">
</p>
<p>At this stage we can also add some custom button actions to this view, for example a like or delete action. This allows a user to do some action in the DetailViewController while peeking without actually navigating to it.</p>
<p>Implementing these actions is also very easy. Go to your DetailViewController and add your actions, its quite similar to using a <code>UIAlertController</code>.</p>
<pre><code class="hljs language-objc"><span class="hljs-keyword">class</span> DetailViewController: <span class="hljs-built_in">UIViewController</span> {
    ...
}

<span class="hljs-comment">// MARK: - UI Preview Action Items</span>

extension DetailViewController {

      override var previewActionItems: [<span class="hljs-built_in">UIPreviewActionItem</span>] {

          let likeAction = <span class="hljs-built_in">UIPreviewAction</span>(title: <span class="hljs-string">"Like"</span>, style: .default) { (action, viewController) <span class="hljs-keyword">in</span>
              <span class="hljs-comment">// add some like logic</span>
            }

          let deleteAction = <span class="hljs-built_in">UIPreviewAction</span>(title: <span class="hljs-string">"Delete"</span>, style: .destructive) { (action, viewController) <span class="hljs-keyword">in</span>
              <span class="hljs-comment">// add some delete logic</span>
          }

          <span class="hljs-keyword">return</span> [likeAction, deleteAction]
      }
}
</code></pre>
<p align="center">
<img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/peek-previewaction-2_uG9NQSv7Pg.jpg?width=280" style=";margin:20px;">
</p>
<h2>Conclusion</h2>
<p>That is all there is to peek and pop. It is a very powerful feature that should dramatically improve the flow of an app that implements it. It allows you to use the app in such a way where you can get glances and previews of screens without having to navigate to those screens. This should make any app more productive and efficient and therefore is a feature that should be supported by all app makers.</p>
<h2>Resources</h2>
<ul>
<li><a href="https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/Adopting3DTouchOniPhone/3DTouchAPIs.html#//apple_ref/doc/uid/TP40016543-CH4-SW1">https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/Adopting3DTouchOniPhone/3DTouchAPIs.html#//apple_ref/doc/uid/TP40016543-CH4-SW1</a></li>
<li><a href="https://the-nerd.be/2015/10/06/3d-touch-peek-and-pop-tutorial/">https://the-nerd.be/2015/10/06/3d-touch-peek-and-pop-tutorial/</a></li>
</ul>
<p><em>Article Photo by <a href="https://unsplash.com/photos/F6Gk6nhwlmY">Nikita Vantorin</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Challenges when making Today Widgets]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2017/11/22/Challenges-when-making-Today-Widgets</link>
            <guid>https://engineering.monstar-lab.com/en/post/2017/11/22/Challenges-when-making-Today-Widgets</guid>
            <pubDate>Wed, 22 Nov 2017 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Apple introduced the Today extension in iOS 9, more commonly called widgets.</p>
<p>Apple describes widgets like so:</p>
<p>Widgets give users quick access to information that’s important right now. For example, users open the Today view to check current stock prices or weather conditions, see today’s schedule, or perform a quick task such as marking an item as done. Users tend to open the Today view frequently, and they expect the information they’re interested in to be instantly available.</p>
<h2>Setup</h2>
<p>Setting up a Today extension in your iOS app is fairly straight forward. In your Xcode project click File->New->Target and select today extension.</p>
<p>Make sure your widget's bundle id is not the same as your apps bundle id. So if your apps bundle is “com.yourcompany.yourappname” the widgets bundle id should look like this “com.yourcompany.yourappname.widget”.</p>
<p>This will create a new target in your app and a new folder. The content of this folder should be familiar with any iOS developer. It contains</p>
<ol>
<li>TodayExtensionViewController.swift</li>
<li>Storyboard file</li>
<li>Plist</li>
<li>Asset catalogue</li>
</ol>
<p>I like to rename the files from TodayExtension to Widget as that makes more sense to me.</p>
<h2>Widget Creation</h2>
<p>Creating the UI for a widget should also be very simple for any developer that has used storyboards and auto-layout before.
Just add the UI elements you wish to use to the storyboard and create the necessary outlets in the ViewController.</p>
<p>This is what the storyboard could look like.</p>
<p align="center">
<img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/ScreenShot2017-11-22at104935_5ahcPXPuRk.png?width=280" style=";margin:20px;">
</p>
<h2>NSWidgetProviding</h2>
<p>After you have set up your outlets we need to add some logic to our WidgetViewController. This comes mainly in the form of the <code>NCWidgetProviding</code> delegate. This delegate has 1 method which handles the logic of when to update the widget.</p>
<p>This is what it could look like.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">extension</span> <span class="hljs-title class_">WidgetViewController</span>: <span class="hljs-title class_">NCWidgetProviding</span> {

    <span class="hljs-keyword">func</span> <span class="hljs-title function_">widgetPerformUpdate</span>(<span class="hljs-params">completionHandler</span>: <span class="hljs-keyword">@escaping</span> (<span class="hljs-type">NCUpdateResult</span>) -> <span class="hljs-type">Void</span>) {
        <span class="hljs-comment">// Perform any setup necessary in order to update the view.</span>
        <span class="hljs-comment">// If an error is encountered, use NCUpdateResult.failed</span>
        <span class="hljs-comment">// If there's no update required, use NCUpdateResult.noData</span>
        <span class="hljs-comment">// If there's an update, use NCUpdateResult.newData</span>


        <span class="hljs-comment">// Make sure we have new data for the widget</span>
        <span class="hljs-keyword">guard</span> isRequiredToUpdate <span class="hljs-keyword">else</span> {
            completionHandler(.noData)
            <span class="hljs-keyword">return</span>
        }

        <span class="hljs-comment">// Update UI</span>
        updateUI() <span class="hljs-comment">// just a method that updates the widgets labels etc.</span>

        <span class="hljs-comment">// Return completion handler with new data</span>
        completionHandler(.newData)
    }
}
</code></pre>
<p>The <code>isRequiredToUpdate</code> is a simple boolean that I use to see whether the widget needs to be updated with new data (see <a href="#implementing-widget-data-manager">Implementing Widget Data Manager</a>).
If there is no new widget data we should return the completion handler with <code>.noData</code>. Otherwise if we have new data we should return it with <code>.newData</code> after we have updated the UI.</p>
<h2>Use ViewWillAppear</h2>
<p>I like to use <code>ViewWillAppear</code> in the WidgetViewController to manually push an UI update, as I was sometimes having issues with the widget not updating immediately.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">override</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">viewWillAppear</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">animated</span>: <span class="hljs-type">Bool</span>) {
    <span class="hljs-keyword">super</span>.viewWillAppear(animated)

    updateUI()
}
</code></pre>
<h2>Deep Linking</h2>
<p>Widgets can even have buttons that for example will launch the app when pressed. This can be achieved very easily with the following line</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">private</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">didPressButton</span>() {
    <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> url <span class="hljs-operator">=</span> <span class="hljs-type">URL</span>(string: “yourappURL:<span class="hljs-comment">//“) else { return }</span>
    extensionContext<span class="hljs-operator">?</span>.open(url)
}
</code></pre>
<h2>Moving Data Between Targets (the slightly challenging part)</h2>
<p>There is basically 2 ways to provide widgets with data.</p>
<ol>
<li>
<p>Treat them as individual entities, which means they have their own logic to fetch data from a service, update a model and present the UI.</p>
</li>
<li>
<p>More commonly use data that is coming from your iOS target.</p>
</li>
</ol>
<p>The main challenge with widgets is moving existing data across your targets.
Even though an app extension bundle is nested within its containing app’s bundle, the running app extension and containing app have no direct access to each other’s containers.</p>
<h2>App Groups</h2>
<p>So how do we move data between the two targets?. The answer is app groups and <code>NSUserDefaults</code>.</p>
<p>To set up app groups for both targets we need to do the following</p>
<ol>
<li>
<p>Click on your Xcode iOS target and go to capabilities. Turn on app groups and give it a unique identifier. Also make sure the checkbox is selected and that all entitlements are set. For best practices the identifier should look something like this “group.com.yourcompany.yourappname”.</p>
</li>
<li>
<p>Repeat step 1 but this time for your widget target. Make sure you use the same app group identifier created above and that its checkmark is also selected.</p>
</li>
</ol>
<h2>Widget Data Manager</h2>
<p>I like to create a basic WidgetDataManager that I can use to share my data between targets. Create a new swift file and call it whatever you would like. Make sure that it has both iOS and TodayExtension/Widget selected as its target membership.</p>
<p>The actual logic and coding style is up to you but this is how it could look. Lets imagine our widget only has 1 UILabel for simplicity sake.</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">enum</span> <span class="hljs-title class_">WidgetDataManager</span> {

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> userDefaults <span class="hljs-operator">=</span> <span class="hljs-type">UserDefaults</span>(suiteName: “group.com.yourcompany.yourappname”)

    <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> label1Text: <span class="hljs-type">String</span> {
        <span class="hljs-keyword">get</span> { <span class="hljs-keyword">return</span> userDefaults<span class="hljs-operator">?</span>.value(forKey: .label1) <span class="hljs-keyword">as?</span> <span class="hljs-type">String</span> <span class="hljs-operator">??</span> <span class="hljs-string">"-"</span> }
        <span class="hljs-keyword">set</span> { userDefaults<span class="hljs-operator">?</span>.setValue(newValue, forKey: .label1) }
    }
}

<span class="hljs-comment">// MARK: - Keys</span>

<span class="hljs-keyword">private</span> <span class="hljs-keyword">extension</span> <span class="hljs-title class_">String</span> {
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> label1 <span class="hljs-operator">=</span> <span class="hljs-string">"WidgetLabel1"</span>
}
</code></pre>
<p>You first need to create a new <code>NSUserDefaults</code> object. As you probably noticed we cannot use the default singleton instance, we have to create a custom suite instance for this to work correctly. The name is what you have set up in your app group.
I then simply added a getter/setter property to set/fetch the latest string from this <code>NSUserDefaults</code> object.</p>
<h2>Implementing Widget Data Manager</h2>
<p>Remember the <code>isRequiredToUpdate</code> property from the <code>NSWidgetProviding</code> step above? The actual logic for this boolean would be the following</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">WidgetViewController</span> {

    <span class="hljs-keyword">@IBOutlet</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">weak</span> <span class="hljs-keyword">var</span> label1: <span class="hljs-type">UILabel</span>!

    <span class="hljs-comment">/// Check if update is needed</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> isRequiredToUpdate: <span class="hljs-type">Bool</span> {
        <span class="hljs-keyword">return</span> label1.text <span class="hljs-operator">!=</span> <span class="hljs-type">WidgetDataManager</span>.label1Text
    }
}

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">WidgetViewController</span>: <span class="hljs-title class_">NCWidgetProviding</span> {
    <span class="hljs-operator">…</span>
}
</code></pre>
<p>I am simply checking if the text of the widgets label(s) matches the data from the WidgetDataManger and if they don’t it means we need to update the widget.</p>
<h2>Update Widget Data Manager</h2>
<p>The last step is to make sure that every time you are changing data that is to be displayed in your widget that you update the WidgetDataManager.</p>
<p>For example in a game you might want to show the latest score in the widget. So in your code where you update the score</p>
<pre><code class="hljs language-swift"><span class="hljs-type">GameDataManager</span>.score <span class="hljs-operator">=</span> newScore
</code></pre>
<p>simply update the WidgetDataManager as well.</p>
<pre><code class="hljs language-swift"><span class="hljs-type">GameDataManager</span>.score <span class="hljs-operator">=</span> newScore
<span class="hljs-type">WidgetDataManager</span>.label1Text <span class="hljs-operator">=</span> newScore
</code></pre>
<h2>Full Sample Code</h2>
<pre><code class="hljs language-swift"><span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">WidgetViewController</span> {

    <span class="hljs-comment">// MARK: - Properties</span>

    <span class="hljs-comment">/// Outlets</span>
    <span class="hljs-keyword">@IBOutlet</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">weak</span> <span class="hljs-keyword">var</span> label1: <span class="hljs-type">UILabel</span>!

    <span class="hljs-comment">/// Check if update is needed</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> isRequiredToUpdate: <span class="hljs-type">Bool</span> {
        <span class="hljs-keyword">return</span> label1.text <span class="hljs-operator">!=</span> <span class="hljs-type">WidgetDataManager</span>.label1Text
    }

    <span class="hljs-comment">// MARK: - View Controller Life Cycle</span>

    <span class="hljs-keyword">override</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">viewDidLoad</span>() {
        <span class="hljs-keyword">super</span>.viewDidLoad()
        <span class="hljs-comment">// Do any additional setup after loading the view</span>
    }

    <span class="hljs-keyword">override</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">viewWillAppear</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">animated</span>: <span class="hljs-type">Bool</span>) {
        <span class="hljs-keyword">super</span>.viewWillAppear(animated)

        updateUI()
    }
}

<span class="hljs-comment">// MARK: - NC Widget Providing</span>

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">WidgetViewController</span>: <span class="hljs-title class_">NCWidgetProviding</span> {

    <span class="hljs-keyword">func</span> <span class="hljs-title function_">widgetPerformUpdate</span>(<span class="hljs-params">completionHandler</span>: <span class="hljs-keyword">@escaping</span> (<span class="hljs-type">NCUpdateResult</span>) -> <span class="hljs-type">Void</span>) {
        <span class="hljs-comment">// Perform any setup necessary in order to update the view.</span>
        <span class="hljs-comment">// If an error is encountered, use NCUpdateResult.failed</span>
        <span class="hljs-comment">// If there's no update required, use NCUpdateResult.noData</span>
        <span class="hljs-comment">// If there's an update, use NCUpdateResult.newData</span>


        <span class="hljs-comment">// Make sure we have new data for the widget</span>
        <span class="hljs-keyword">guard</span> isRequiredToUpdate <span class="hljs-keyword">else</span> {
            completionHandler(.noData)
            <span class="hljs-keyword">return</span>
        }

        <span class="hljs-comment">// Update UI</span>
        updateUI()

        <span class="hljs-comment">// Return completion handler with new data</span>
        completionHandler(.newData)
    }
}

<span class="hljs-comment">// MARK: - Update UI</span>

<span class="hljs-keyword">private</span> <span class="hljs-keyword">extension</span> <span class="hljs-title class_">WidgetViewController</span> {

    <span class="hljs-keyword">func</span> <span class="hljs-title function_">updateUI</span>() {
        label1.text <span class="hljs-operator">=</span> <span class="hljs-type">WidgetDataManager</span>.label1Text
    }
}

</code></pre>
<h2>Conclusion</h2>
<p>Widgets are a great addition to any iOS app. They are very easy to setup and use and most applications should support them in some form or the other.</p>
<p align="center">
<img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/IMG_02842_4tRqdM5GAQ.jpg?width=280" style=";margin:20px;">
</p>
<h2>Resources</h2>
<ul>
<li><a href="https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/Today.html">https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/Today.html</a></li>
<li><a href="https://www.raywenderlich.com/150953/today-extension-tutorial-getting-started">https://www.raywenderlich.com/150953/today-extension-tutorial-getting-started</a></li>
</ul>
<p><em>Article Photo by <a href="https://unsplash.com/photos/aso6SYJZGps">Hannah Wei</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Searchable Items]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2017/10/20/searchable-items</link>
            <guid>https://engineering.monstar-lab.com/en/post/2017/10/20/searchable-items</guid>
            <pubDate>Fri, 20 Oct 2017 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>Motivation</h2>
<p>App search has been here since iOS9. This feature seems to go largely unused. Maybe this is because both developers and people selling features have forgotten it exists. This is a quick guide for less technical people, and a semi quick guide for developers.</p>
<h2>What you need to know (TL;DR for everyone)</h2>
<ul>
<li>An app can index content in your app, such that it will show up in search results when the user searches in spotlight</li>
<li>Content in the app can either be indexed by user activity (e.g. opening an article or detail page of a contact), by indexing fetched data (e.g. a newsfeed), or combining the two.</li>
<li>For an app that has a web counterpart it is possible to add markup to the website, such that the marked items may show up in Spotlight on your phone. This in conjunction with Universal Links, can add the ability to deep link to content in your app without ever having opened the app or manually indexed any content (Not described further here)</li>
<li>There are several ways of displaying these search results e.g. they can come with a call button if you're displaying an entity that has a phone number (e.g. people)</li>
<li>Items can be deleted again from index or be set to expire. They expire after one month per default.</li>
<li>When user taps search results appropriate action should be taken, which usually means taking the user to the detail view of the object displayed in the search. This navigation has to be manually implemented, but can likely be reused for deep linking from pushes.</li>
</ul>
<h4>Real world examples:</h4>
<ul>
<li>
<p>Making favorite employees searchable when favoriting -> Clicking employee in Spotlight search takes user to Employee detail view</p>
</li>
<li>
<p>Indexing the main feed to make items searchable and deep link to detail page</p>
</li>
<li>
<p>Making an article searchable when opening detail view from list -> Clicking article in spotlight search takes user to the article (Called UserActivity)</p>
<ul>
<li>
<p>This use case comes with handoff to web almost for free, provided the app has the URL to the content. This means that if you have the article open on your phone, you can continue reading on your mac</p>
<p align="center">
    <img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/SimulatorScreenShot-iPhoneX-2017-10-31at102233_clWkOWGxvQ.png?width=180" style="margin:20px;">
    <img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/SimulatorScreenShot-iPhoneX-2017-10-31at103135_WElQkNH0XO.png?width=180" style="margin:20px;">
    <img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/SimulatorScreenShot-iPhoneX-2017-10-31at103934_YrsevZcwRv.png?width=180" style="margin:20px;">
</p>
</li>
</ul>
</li>
</ul>
<h2>Implementation details</h2>
<h4>Indexing data without user interaction (CSSearchableItem)</h4>
<ol>
<li>Make CSSearchableAttributeSet. I did it in an extension to the entity I wanted to make searchable to have data on hand.</li>
</ol>
<pre><code class="hljs language-swift"><span class="hljs-keyword">extension</span> <span class="hljs-title class_">MySearchableStruct</span> {
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> attributeSet: <span class="hljs-type">CSSearchableItemAttributeSet</span> {

       <span class="hljs-keyword">let</span> attributeSet <span class="hljs-operator">=</span> <span class="hljs-type">CSSearchableItemAttributeSet</span>(
           itemContentType: kUTTypeText <span class="hljs-keyword">as</span> <span class="hljs-type">String</span>)
       attributeSet.title <span class="hljs-operator">=</span> name
       attributeSet.contentDescription <span class="hljs-operator">=</span> abstract
       attributeSet.keywords <span class="hljs-operator">=</span> [ticker, name, description]
       <span class="hljs-keyword">return</span> attributeSet
   }
}
</code></pre>
<ol start="2">
<li>Make Domain identifier. This makes it possible to wipe a whole type of entries at once.</li>
</ol>
<pre><code class="hljs language-swift"><span class="hljs-keyword">extension</span> <span class="hljs-title class_">MySearchableStruct</span> {
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> domainIdentifier <span class="hljs-operator">=</span> <span class="hljs-string">"com.TheCompany.TheApp.MySearchableStruct"</span>
}
</code></pre>
<ol start="3">
<li>Make a unique identifier for the searchableItem. This is used for getting the exact item if you need to deep link or delete it again. I also put this in the extension.</li>
</ol>
<pre><code class="hljs language-swift"><span class="hljs-keyword">extension</span> <span class="hljs-title class_">MySearchableStruct</span> {
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> uniqueSearchIdentifier: <span class="hljs-type">String</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-type">MySearchableStruct</span>.domainIdentifier <span class="hljs-operator">+</span> <span class="hljs-string">":"</span> <span class="hljs-operator">+</span> <span class="hljs-type">String</span>(identifier)
  }
}
</code></pre>
<ol start="4">
<li>Index your data. Done by creating a CSSearchableItem and adding it to index, like so:</li>
</ol>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> item <span class="hljs-operator">=</span> <span class="hljs-type">CSSearchableItem</span>(uniqueIdentifier: mySearchableStructInstance.uniqueSearchIdentifier,
 domainIdentifier: <span class="hljs-type">MySearchableStruct</span>.domainIdentifier,
 attributeSet: mySearchableStructInstance.attributeSet)
</code></pre>
<pre><code class="hljs language-swift"><span class="hljs-type">CSSearchableIndex</span>.default().indexSearchableItems([item], completionHandler: <span class="hljs-literal">nil</span>)
</code></pre>
<ol start="5">
<li>Remember to set thumbnailData for your CSSearchableItemAttributeSet if you have a specific image for the entity you're making searchable. If you are loading image data asynchronously, it seems you need to wait until data is done loading to add the item to the index. I had to add multiple at once, this is a full implementation using DispatchGroup:</li>
</ol>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">addItemsToIndex</span>(<span class="hljs-params">dataItems</span>: [<span class="hljs-type">MySearchableStruct</span>]) {
    <span class="hljs-keyword">let</span> group <span class="hljs-operator">=</span> <span class="hljs-type">DispatchGroup</span>()
    <span class="hljs-keyword">var</span> searchableItems <span class="hljs-operator">=</span> [<span class="hljs-type">CSSearchableItem</span>]()

    <span class="hljs-keyword">for</span> dataItem <span class="hljs-keyword">in</span> dataItems {

        group.enter()

        <span class="hljs-keyword">let</span> attributeSet <span class="hljs-operator">=</span> dataItem.attributeSet
        <span class="hljs-keyword">let</span> searchableItem <span class="hljs-operator">=</span> <span class="hljs-type">CSSearchableItem</span>(uniqueIdentifier: dataItem.uniqueSearchIdentifier,
                                              domainIdentifier: <span class="hljs-type">MySearchableStruct</span>.domainIdentifier,
                                              attributeSet: attributeSet)
        searchableItems.append(item)

        <span class="hljs-comment">// Load the image data in the background</span>
        <span class="hljs-type">DispatchQueue</span>.global(qos: .background).async {

            <span class="hljs-keyword">let</span> data <span class="hljs-operator">=</span> imageData(fromUrlString: dataItem.thumbnailUrlString)

            <span class="hljs-type">DispatchQueue</span>.main.async {

                <span class="hljs-comment">// Set the image data and notify DispatchGroup</span>
                attributeSet.thumbnailData <span class="hljs-operator">=</span> data
                group.leave()

            }
        }
    }

    <span class="hljs-comment">// When all images have loaded add all searchable items to index</span>
    group.notify(queue: .global()) {
        <span class="hljs-type">CSSearchableIndex</span>.default().indexSearchableItems(searchableItems, completionHandler: <span class="hljs-literal">nil</span>)
    }
}
</code></pre>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">imageData</span>(<span class="hljs-params">fromUrlString</span> <span class="hljs-params">urlString</span>: <span class="hljs-type">String</span>) -> <span class="hljs-type">Data</span>? {
   <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> url <span class="hljs-operator">=</span> <span class="hljs-type">URL</span>(string: urlString) {
       <span class="hljs-keyword">do</span> {
           <span class="hljs-keyword">let</span> data <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> <span class="hljs-type">Data</span>.<span class="hljs-keyword">init</span>(contentsOf: url)
           <span class="hljs-keyword">return</span> data
       } <span class="hljs-keyword">catch</span> {
           <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
       }

   }
   <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}
</code></pre>
<h4>Indexing data from user activity</h4>
<ol>
<li>Again in an extension to the data item make a NSUserActivity. Make sure isEligibleForSearch is set to true.</li>
</ol>
<ul>
<li>
<p>contentAttributeSet is the attributeSet variable already created in the extension from before.</p>
</li>
<li>
<p><strong>Only</strong> set the relatedUniqueIdentifier for the attributeSet <strong>if you already indexed the item e.g. from the feed api call</strong>. Otherwise the userActivity won't be indexed. The relatedUniqueIdentifier is to avoid duplicates when using both the CSSearchableIndex api and NSUserActivity</p>
</li>
<li>
<p>webpageURL is for handoff to other devices</p>
</li>
<li>
<p>userInfo is for deeplinking to the desired object</p>
</li>
</ul>
<pre><code class="hljs language-swift"><span class="hljs-keyword">extension</span> <span class="hljs-title class_">MySearchableStruct</span> {
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> userActivity: <span class="hljs-type">NSUserActivity</span> {
      <span class="hljs-keyword">let</span> activity <span class="hljs-operator">=</span> <span class="hljs-type">NSUserActivity</span>(activityType: <span class="hljs-type">MySearchableStruct</span>.domainIdentifier)
      activity.title <span class="hljs-operator">=</span> name
      activity.userInfo <span class="hljs-operator">=</span> [<span class="hljs-string">"id"</span>: someUniqueIdentifierOfTheDataEntity]
      activity.keywords <span class="hljs-operator">=</span> [ticker]
      activity.isEligibleForSearch <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>
      activity.contentAttributeSet <span class="hljs-operator">=</span> attributeSet
      activity.webpageURL <span class="hljs-operator">=</span> websiteLink.flatMap({ <span class="hljs-type">URL</span>(string: <span class="hljs-variable">$0</span>) })
      activity.contentAttributeSet <span class="hljs-operator">=</span> attributeSet
      activity.contentAttributeSet<span class="hljs-operator">?</span>.relatedUniqueIdentifier <span class="hljs-operator">=</span> uniqueSearchIdentifier
      <span class="hljs-keyword">return</span> activity
  }
}
</code></pre>
<ol start="2">
<li>To get the activity indexed your activity needs to 'become current'. Would happen e.g. when showing the detail view for an entity. This happens in either of two ways. If you have your data object on hand in your UIViewController or another class that inherits from UIResponder, the instance has a variable 'userActivity'. Set it like so:</li>
</ol>
<pre><code class="hljs language-swift">userActivity <span class="hljs-operator">=</span> myDataItemInstance.userActivity
</code></pre>
<p>The item is now searchable
If you don't have a UIResponder at hand you need to make a userActivity variable yourself, as a strong reference is needed to add the activity to the indexed</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">var</span> userActivity: <span class="hljs-type">NSUserActivity</span>?
</code></pre>
<p>And when showing the data in your view:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">self</span>.userActivity <span class="hljs-operator">=</span> mySearchableItemInstance.userActivity
<span class="hljs-keyword">self</span>.userActivity.becomeCurrent()
</code></pre>
<p>The item is now searchable and as a bonus handoff is now also activated for the item.
Remember to make the activity resign current to turn off handoff for the item when leaving the detail view:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">self</span>.userActivity<span class="hljs-operator">?</span>.resignCurrent()
</code></pre>
<h4>Implement callback for click on searchable item in Spotlight</h4>
<ol>
<li>Searchable item clicks are caught in the AppDelegate, in the continueUserActivity callback.</li>
</ol>
<ul>
<li>
<p>Items that were indexed without user interaction have the userActivity type CSSearchableItemActionType. To get the uniqueSearchIdentifier of the CSSearchableItemActionType, access the dictionary userActivity.userInfo with key <code>CSSearchableItemActivityIdentifier</code></p>
</li>
<li>
<p>NSUserActivity has the type it was init'ed with, which in this case was <code>MySearchableStruct.domainIdentifier</code>. To get the uniqueSearchIdentifier of the userActivity, look in the userInfo dictionary with the key for the identifier for the UserActivity created earlier, in this case 'id'</p>
</li>
</ul>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">application</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">application</span>: <span class="hljs-type">UIApplication</span>, <span class="hljs-params">continue</span> <span class="hljs-params">userActivity</span>: <span class="hljs-type">NSUserActivity</span>, <span class="hljs-params">restorationHandler</span>: <span class="hljs-keyword">@escaping</span> ([<span class="hljs-keyword">Any</span>]<span class="hljs-operator">?</span>) -> <span class="hljs-type">Void</span>) -> <span class="hljs-type">Bool</span> {

        <span class="hljs-keyword">switch</span> userActivity.activityType {
        <span class="hljs-keyword">case</span> <span class="hljs-type">CSSearchableItemActionType</span>:

            <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> identifier <span class="hljs-operator">=</span> userActivity.userInfo<span class="hljs-operator">?</span>[<span class="hljs-type">CSSearchableItemActivityIdentifier</span>] <span class="hljs-keyword">as?</span> <span class="hljs-type">String</span>,
                identifier.contains(<span class="hljs-string">":"</span>), <span class="hljs-keyword">let</span> type <span class="hljs-operator">=</span> identifier.split(separator: <span class="hljs-string">":"</span>).first,
                <span class="hljs-keyword">let</span> id <span class="hljs-operator">=</span> identifier.split(separator: <span class="hljs-string">":"</span>).last,
                type <span class="hljs-operator">!=</span> id <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span> }

            <span class="hljs-keyword">switch</span> <span class="hljs-type">String</span>(type) {

            <span class="hljs-keyword">case</span> <span class="hljs-type">MySearchableStruct</span>.domainIdentifier:
                showDetail(forItemWith: <span class="hljs-type">Int</span>(id))
                <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>
            <span class="hljs-keyword">default</span>:
                <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
            }

        <span class="hljs-keyword">case</span> <span class="hljs-type">MySearchableStruct</span>.domainIdentifier:

            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> id <span class="hljs-operator">=</span> userActivity.userInfo<span class="hljs-operator">?</span>[<span class="hljs-string">"id"</span>] <span class="hljs-keyword">as?</span> <span class="hljs-type">Int</span> {
                showDetail(forItemWith: <span class="hljs-type">Int</span>(id))
                <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>
            }

        <span class="hljs-keyword">default</span>:
            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
        }
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
    }
</code></pre>
<h2>Links</h2>
<p><a href="https://www.raywenderlich.com/116608/ios-9-app-search-tutorial-introduction-to-app-search">App search guide by Ray Wenderlich</a></p>
<p><a href="https://developer.apple.com/library/content/documentation/General/Conceptual/AppSearch/index.html#//apple_ref/doc/uid/TP40016308-CH4-SW1">Apple documentation</a></p>
<p><a href="https://www.hackingwithswift.com/read/32/4/how-to-add-core-spotlight-to-index-your-app-content">Hacking with Swift search tutorial</a></p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/oaQ2mTeaP7o">Tanner Van Dera</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Preparing for iPhone X]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2017/09/19/preparing-for-iphonex</link>
            <guid>https://engineering.monstar-lab.com/en/post/2017/09/19/preparing-for-iphonex</guid>
            <pubDate>Tue, 19 Sep 2017 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>iPhone X was announced on September 12, 2017, and with that came a lot of questions about how apps will look and behave with this new phone. We as iOS developers are now accustomed to supporting a variety of screen sizes, but there are still some new challenges to face.</p>
<p>When the new phone launches in early November, all apps that have not been linked against the iOS 11 SDK will be displayed in a letterboxed layout, so they will continue to function as normal and look the same as they always have. Linking against the iOS 11 SDK enables the full-screen layout, which is when you will want to make sure that your app still looks great on the new screen.</p>
<p align="center">
<img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/letterboxPortrait_7xOrDIE9HO.png?width=200" style=";margin:20px;">
<img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/letterboxLandscape_GDBO2iIboy.png?height=200" style=";margin:20px;"></p>
<p>Note that linking against an older SDK is not a simple process, so if you need more time before optimizing for iPhone X, it would be best to continue working in Xcode 8. Also note that if you develop in Swift, you cannot link to an older SDK in Xcode 9.</p>
<h2>What to plan for</h2>
<p>All but the simplest of apps will likely need some optimization to be displayed properly. Here are some of the most common issues:</p>
<p align="center">
<img align="right" src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/fullPortrait_feEUZoUYRr.png?width=150" style="margin:20px;float:right">
</p>
<h4>Rounded corners</h4>
<p>Due to the edge-to-edge screen, the corners of applications are now rounded. Important data or UI elements should be inset properly to account for less space in the corners. The top corners will generally be occupied by the new status bar, but if your app has content in the bottom, you will need to pay attention to the corners.</p>
<h4>Bottom Indicator</h4>
<p>A new swipe indicator now sits at the bottom of the screen. If your app has content there as well, it will be covered by the indicator.</p>
<h4>Top status area</h4>
<p>The status bar is larger on iPhone X than other phones, so ensure that your app is not relying on a hardcoded status bar size. Also, if you present custom views that do not have default UINavigationBars, your fullscreen content will likely be cut off by the status bar area.</p>
<h4>Landscape support</h4>
<p>If your app supports landscape orientation, you will notice that your app tends to be cut off by the notch area, and possibly not extend all the way to the other side of the screen.</p>
<p align="center">
<img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/fullscreenPortrait_C5LfF0zicC.png?width=200" style="margin:20px;">
<img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/fullscreenLandscape_HGYRbZ5q81.png?height=200" style="margin:20px;">
</p>
<h3>Enter Safe Area Layout Guide</h3>
<p>You can solve all of the above issues by adhering to margins and the new "Safe Area" layout guides in autolayout. If your content was previously constrained to <code>superview</code>, it should now be constrained to <code>safeAreaLayoutGuide</code>. The safe area layout guide is essentially the rectangle of space that does not interfere with any of the corners or indicators.</p>
<p align="center">
<img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/fixedMain_zoBzp8sQl9.png?width=200" style="margin:20px;">
<img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/fixedDetail_r47iRUBbKM.png?width=200" style="margin:20px;">
</p>
<p><strong>Note:</strong> Safe area layout guide is a new feature of iOS 11. Apps targeting older versions of iOS will still use the top and bottom layout guides. Storyboards are backwards compatible and can support safe areas in older iOS versions, <em>however</em>, this only extends to iOS 9. If your app supports versions older than 9.0, you can not use safe areas in Storyboards.</p>
<h3>Other issues</h3>
<ul>
<li>
<p>Custom toolbars, fake navigation bars, and similar elements will need to be updated so that their layout matches the behavior of the native elements.</p>
</li>
<li>
<p>The safe area layout guides define a sub-rectangle within the screen, so you will need to ensure that backgrounds extend beyond the safe area, otherwise you will have empty white space around your app. In the above example, the Details top bar had to be extended so that it fills out the status bar area.</p>
</li>
<li>
<p>Full screen images will need letterboxing, pillarboxing, or cropping to fit properly in the new aspect ratio.</p>
</li>
</ul>
<h4>A note about Face ID</h4>
<p>This is not layout related, but the new Face ID feature will work right away the same as Touch ID does. Face ID is included in the <code>deviceOwnerAuthenticationWithBiometrics</code> authentication policy, so no code change is required.</p>
<p>If your app needs to be aware of specifically which authentication method is being used, there is a new <code>LABiometryType</code> enum.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/xdLXPic3Wfk">Ben Kolde</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Droidcon Berlin and GDD Krakow]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2017/09/14/Droidcon-Berlin-and-GDD-Krakow</link>
            <guid>https://engineering.monstar-lab.com/en/post/2017/09/14/Droidcon-Berlin-and-GDD-Krakow</guid>
            <pubDate>Thu, 14 Sep 2017 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Being one of the biggest Android conferences in Europe, this was my first time in Berlin. The conference was taking part in partnership with IFA Berlin. As a perk Droidcon tickets also allowed access to it. The downside was the big amount of people commuting to the venue and particularly the accommodation prices during those days.</p>
<h3>Keynote: How to be an Android Expert</h3>
<p>by Chiu-ki Chan</p>
<p>A very eye-opening talk where Chiu-ki shared her views on what an Android expert is and how to become one by sharing knowledge on different ways and tips and tricks to become an Android Expert including how to take sketch notes in a talk or how to write a blog post efficiently. Find the slides <a href="https://www.slideshare.net/chiuki/how-to-be-an-android-expert-droidcon-berlin">here</a>.</p>
<h3>Data Persistence in Android: There’s Room for Improvement</h3>
<p>by Florina Muntenescu</p>
<p>One of the talks that I was more interested about. Unfortunately, the “Room” in this case was too small for the number of people who wanted to attend so I could not enjoy it this time. Nevertheless, I had another chance as you will see below.</p>
<h3>Flutter – 60fps UI of the Future</h3>
<p>by Pascal Welsh and Albrecht Noll</p>
<p><a href="https://flutter.io">Flutter</a> is a new mobile SDK which aim is to build modern and beautiful UIs for both Android and iOS. Pascal and Albrecht gave an excellent intro to Flutter that even seems capable of running smoothly at 120fps. Impressive!
Flutter is currently in alpha version and open source with Google working actively on it. It provides a thin layer of C/C++ code and requires to use Dart for implementing the UI. All elements in Flutter are considered widgets that connect in a Matryoshka type hierarchy and uses Composition over Inheritance.
For more info on the presentation check out the <a href="https://speakerdeck.com/passsy/flutter-60-fps-ui-of-the-future-droidcon-berlin-17">slides</a>. Also, if you want to try an app build using Flutter, you can try Hamilton (<a href="https://play.google.com/store/apps/details?id=com.hamilton.app">Android</a>, <a href="https://itunes.apple.com/us/app/hamilton-the-official-app/id1255231054?mt=8">iOS</a>).</p>
<h3>Home Improvement: Architecture &#x26; Kotlin</h3>
<p>by Jorge D. Ortiz-Fuentes</p>
<p>Being Android Architecture Components and Kotlin the two biggest announcements in the last Google I/O this was one of my top picks for day one. Interestingly, I had the chance to meet Jorge early in the morning before the opening keynote.
During the talk, Jorge shared some insight of real and relevant scenarios where he explained how to improve our apps using Kotlin. Similarly to Florina Muntenescu’s talk, the room was not big enough, but this time I managed to get a sweet spot.</p>
<h3>Keynote: Designing for Trust: User Experience + Privacy</h3>
<p>by Ame Elliott</p>
<p>Ame is Design Director at <a href="https://simplysecure.org">Simply Secure</a>, a nonprofit organisation that provides professional education for user experience designers, researchers, and developers working on privacy, security, transparency, and ethics. She structured her talk into three different main topics: Understand the risks to users, Lead through design and Practical advice.
Check the slides <a href="https://simplysecure.org/resources//Elliott-DroidCon-UXandPrivacy.pdf">here</a>.</p>
<h4>Developing a Streaming Android App for TV in Minutes</h4>
<p>by Mario Viviani</p>
<p>I had the pleasure of meeting Mario at my first Android meetup in London back in 2015. He is a Technology Evangelist at Amazon and former GDE, so I was looking very forward to attending his talk and learning more about everything that Amazon is doing.
I was particularly impressed to see how things have evolved during the last years and how they have listened to the developers to make things easier with integrations that are incredibly simple and straight forward.</p>
<h3>Reactive, Clean Architecture and Android Architecture Components, a Solid Formula for a MVVM Based Architecture</h3>
<p>by Lucia Payo</p>
<p>My top pick for the day. Here again, I had the opportunity to meet Lucia before her talk as she was in N26 stand. We had the chance to discuss the new Android Architecture Components library and share different views on MVP and MVVM.
In the talk, she presented a clean and well-structured reactive architecture based on MVVM. I would recommend having a look at her latest articles <a href="https://mag.n26.com/reactive-clean-architecture-with-android-architecture-components-685a6682e0ca">here</a> and <a href="https://mag.n26.com/practical-example-using-reactive-clean-architecture-approach-8a2436ea76b4">here</a>.</p>
<h2>Google Developer Days Europe - Krakow</h2>
<p>This event overlapped in its first day with day two in Droidcon Berlin, so I missed the first half of it. Still, it was a very nice event. The ICE Congress Center in Krakow is a four floors event space, and with so many things going on at the same time in different areas it was easy to miss something.</p>
<p>My day started meeting for breakfast with some of the Android students at Udacity that I have been mentoring during the last few months. During the day I continuously meet many more as there were around 150 of them in the conference.</p>
<h3>Day 2 Keynote</h3>
<p>by Ewa Macias, Behshad Behzadi, Mícheál Ó Foghlú</p>
<p>Probably the most impressive part of the keynote was the demo on Artificial Intelligence and Google Assistant. We had the opportunity to see in action some of the most complex interactions with the Assistant that are still in the works including an incredible understanding in noisy environments.</p>
<h3>Architecture Components</h3>
<p>by Florina Muntenescu</p>
<p>Finally, I had the chance to attend Florina’s talk. There she talked about Architecture and best practice. She also introduced some of the latest additions to the Android Architecture Components library such as the new Paging library that is not yet released by the time I am writing these lines.
I also had the chance to talk with Florina about architecture with MVP, MVVM and how the new ViewModel works with different architectures plus some insights in Room. Overall, an outstanding and valuable know-how for our future projects.</p>
<h3>Deep dive into Constraint Layout</h3>
<p>by Dan Galpin</p>
<p>Training sessions during the event were completely packed, and some people found that there was not space enough for such high demand. For this one, I had the pleasure to be invited to help Dan as a TA solving questions and doubts from the attendees. If you feel interested in playing with Constraint Layout this is the <a href="https://codelabs.developers.google.com/codelabs/constraint-layout/index.html">code lab</a> that we used for the session.</p>
<h3>Modularizing Your App</h3>
<p>by Ben Weiss</p>
<p>During his talk, Ben showed how to improve our app discoverability using Instant Apps where the key is modularising our apps. The primary constraint here seems to be the size of our modules which should be smaller than four megabytes. Also recommended tools to deal with this are the apk analyzer and configuration splits on top of the usual minification, shrinking and proguard obfuscation.</p>
<h2>Final thoughts</h2>
<p>Droidcon Berlin was great to find out more about a wide range of topics related to Android and meet interesting people. GDD Krakow was a very big event where I have the opportunity to help in a training session, meet some of my mentees at Udacity and discuss different topics with Google Developer Advocates.</p>
<p>Taking three flights for two conferences in a row in different countries is probably as tiring as it seems. Nevertheless, it was an awesome experience and I could not be happier about it.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Getting stack traces in Swift on Linux]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2017/08/24/Getting-Stack-Traces-in-Swift-on-Linux</link>
            <guid>https://engineering.monstar-lab.com/en/post/2017/08/24/Getting-Stack-Traces-in-Swift-on-Linux</guid>
            <pubDate>Thu, 24 Aug 2017 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><a href="https://www.bugsnag.com">Bugsnag</a> is a convenient service for reporting and catching bugs and crashes that Nodes has been using for a long time. When we moved our backend stack over to Swift, we noticed that there was not a Linux-friendly package. So, we built <a href="https://github.com/nodes-vapor/bugsnag">our own</a>! Doing so led us down quite a deep rabbit hole: stack traces.</p>
<p>From our stack traces, we wanted a system with clean output that gracefully handles the differences between macOS and Linux. We started our search with Foundation.</p>
<p>Foundation in Swift provides a convenient <code>Thread.callStackReturnAddresses()</code>, but, like most things in Foundation, this API is not available on Linux. To get around this, we have to write the implementation in C and then expose the implementation to Swift using a <a href="https://clang.llvm.org/docs/Modules.html">module map</a>. Doing so is not that complicated, but we ended up hitting a few roadblocks on the Linux side.</p>
<p>We started out the implementation by taking advantage of <a href="http://man7.org/linux/man-pages/man3/backtrace.3.html">backtrace(3)</a> that's available to *nix systems. Backtrace is a convenient function that just takes a buffer and its size and spits out an array of pointers that is <code>&#x3C;= BUFF_LEN</code> in length. There is even an equally convenient function <code>backtrace_symbols </code> that can take this array of pointers and translate them into strings. The downside of <code>backtrace_symbols</code> is that its output is not defined by the standard. So, the resulting stack traces aren't the same across all implementations. On top of that, the output contains a lot of extra noise, such as address and offset information.</p>
<h2>Getting a stack trace</h2>
<p>Our implementation of <code>backtrace_symbols</code> is relatively straightforward. We just map over the buffer using <a href="http://man7.org/linux/man-pages/man3/dladdr.3.html">dladdr(3)</a> to get information about the address. If there is no metadata for the given symbol, the name defaults to <code>???</code>. The implementation could be more performant by iterating over the collection twice and using the first pass to calculate how much memory we need and then allocate a single buffer big enough to fit all of this information. Then iterate over the buffer again <code>memcpy</code>ing the strings into our pool. We did not use this approach because extracting strings from a single, pool-allocated, buffer is much more involved than extracting them from a nested pointer.</p>
<pre><code class="hljs language-c"><span class="hljs-type">char</span> ** <span class="hljs-title function_">get_symbols_for_backtrace</span><span class="hljs-params">(<span class="hljs-type">void</span> * <span class="hljs-type">const</span> *buffer, <span class="hljs-type">int</span> size)</span> {
    <span class="hljs-type">int</span> i;
    <span class="hljs-type">char</span> ** result;

    result = <span class="hljs-built_in">malloc</span>(<span class="hljs-keyword">sizeof</span>(<span class="hljs-type">char</span> *) * size);
    <span class="hljs-keyword">if</span> (result == <span class="hljs-literal">NULL</span>) <span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>;

    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &#x3C;= size; ++i) {
        Dl_info info;
        dladdr(buffer[i], &#x26;info);

        <span class="hljs-type">char</span> *name = <span class="hljs-string">"???"</span>;
        <span class="hljs-keyword">if</span> (info.dli_sname) {
            name = (<span class="hljs-type">char</span> *)info.dli_sname;
        }

        result[i] = strdup(name);
    }

    <span class="hljs-keyword">return</span> result;
}
</code></pre>
<p>On Linux the symbol <code>dladdr</code> is a non-standard extension; therefore, it is only exposed by the feature test macro <code>_GNU_SOURCE</code>. Sadly, Swift imports glibc before you have the opportunity to define it (<a href="https://bugs.swift.org/browse/SR-328">SR-328</a>). This can be worked around by invoking the Swift compiler with <code>-Xcc -D_GNU_SOURCE</code>. We ran into some issues when trying defining this macro on some versions of Linux and decided to build the stack trace library independent of the Swift package and then dynamically link it.</p>
<h2>Distributing our stack trace library</h2>
<p>We started building the dynamic library by pulling the stack module out of SPM and gave it its own git repo. Then, we added a <a href="https://github.com/nodes-vapor/stack/blob/master/Makefile">simple Makefile</a> that has a target for both macOS and Linux. Now that we are able to build stack independent of the Swift module, we need to distribute the binaries we just built and a header. We use APT to distribute on Debian-based systems and Homebrew on macOS. By default Homebrew does not install the libraries and headers in a place that is in Swift's search path. To resolve this, we added a <a href="https://en.wikipedia.org/wiki/Pkg-config">pkg-config</a> on the macOS target that points to Homebrew's install location.</p>
<p>Now we are ready to get our first stack trace!</p>
<h2>Demangling symbols</h2>
<p><strong>And...</strong></p>
<p><code>__T04Core15BuiltinFunctionC4makeACSS_SayAA4Type_pG2inAF3outSb10isVariadic4LLVM7IRValue _pAC_SayAA4Expr_pGAA11IRGeneratorVztc3genAaE_pAA7CheckerVz_AA4CallCtcSg02 onP5ChecktFZfA4_</code></p>
<p><img src="https://www.filepicker.io/api/file/P0yc1BheSwgKi6GE6Iys" alt="eek!"></p>
<p>She's a beauty, ain't she?</p>
<p>What you are looking at is a mangled symbol. It is a common way for modern languages/features to disambiguate names and preserve a symbol's meta-information. Here is an example of a simple struct and how it and its members are mangled.</p>
<pre><code class="hljs language-swift">$ xcrun swiftc <span class="hljs-operator">-</span>o example <span class="hljs-operator">-</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">User</span> {
	<span class="hljs-keyword">let</span> name: <span class="hljs-type">String</span>

	<span class="hljs-keyword">func</span> <span class="hljs-title function_">kick</span>() -> <span class="hljs-type">Bool</span> { <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span> }
}
<span class="hljs-operator">^</span><span class="hljs-type">D</span>
$ nm example <span class="hljs-operator">|</span> grep example
00000001000010a0 t __T07example4UserV4kickSbyF
<span class="hljs-number">0000000100001070</span> t __T07example4UserV4nameSSfg
<span class="hljs-operator">...</span>
<span class="hljs-number">0000000100001160</span> t __T07example4UserVwxx
</code></pre>
<blockquote>
<p>If you wish to learn more about mangling in programming languages, check out <a href="https://en.wikipedia.org/wiki/Name_mangling">this</a> wikipedia article.</p>
</blockquote>
<p>The process of extracting metadata from a mangled symbol is known as demangling (<a href="https://mikeash.com/pyblog/friday-qa-2014-08-15-swift-name-mangling.html">Swift Name Demangling</a>). The way Swift mangles symbols is not well documented and is subject to change across versions of Swift. Matt Gallagher <a href="https://github.com/mattgallagher/CwlDemangle">translated</a> Swift's internal version of the mangler to Swift and open-sourced it. This tool works quite well for Swift 3, but Swift 4 changed the way most of the mangling works. For Swift 4 support we moved to the internal implementation using a <a href="https://github.com/nodes-vapor/stacked/pull/12">little linker hack</a> (thanks <a href="https://twitter.com/harlanhaskins">@harlanhaskins</a>). This implementation is quite brittle, but, until we get a version that is exposed to Swift, it is our only option.</p>
<p>Now, when we try to run again, we are faced with another issue.</p>
<h2>Swift symbols not being exported on Linux</h2>
<p>By default, dynamic symbols are not exported on Linux. That means that all Swift functions in the stack trace will not have their name displayed, just like in the table below.</p>






























<table><thead><tr><th></th><th>Symbol</th><th>Address</th></tr></thead><tbody><tr><td>0</td><td><code>.build/release/libFrameAddress.so(get_stack_trace+0x2d)</code></td><td><code>0x7f03ad5ca6ad</code></td></tr><tr><td>1</td><td><code>.build/release/App()</code></td><td><code>0x7f17b0</code></td></tr><tr><td>2</td><td><code>.build/release/App()</code></td><td><code>0x7fb3fa</code></td></tr><tr><td>...</td><td>...</td><td>...</td></tr></tbody></table>
<pre><code class="hljs language-c">libFrameAddress.so(get_stack_trace+<span class="hljs-number">0x2d</span>)
</code></pre>
<p><code>libFrameAddress.so</code> is a C binary and <code>get_stack_trace</code> is our stack trace implementation.</p>
<pre><code class="hljs language-c">App()
</code></pre>
<p><code>App</code> is the Swift binary and the name and address of the function are missing. In order to export this information, we need to pass the following flag to the linker:</p>
<p><code>-Xlinker --export-dynamic</code></p>
<p>We now have our beautiful stack traces!</p>





























<table><thead><tr><th></th><th>Symbol</th></tr></thead><tbody><tr><td>0</td><td><code>get_stack_trace</code></td></tr><tr><td>1</td><td><code>static Stacked.FrameAddress.getStackTrace(maxStackSize: Swift.Int) -> Swift.Array&#x3C;Swift.String></code></td></tr><tr><td>2</td><td><code>StackedTests.StackedTests.anotherExample() -> Swift.Array&#x3C;Swift.String></code></td></tr><tr><td>3</td><td><code>StackedTests.StackedTests.testExample() -> ()</code></td></tr><tr><td>4</td><td><code>@objc StackedTests.StackedTests.testExample() -> ()</code></td></tr></tbody></table>
<h2>Conclusion</h2>
<p>Getting stack traces in Linux was much more involved than we had expected. We ended up touching nearly every part of the system and were plagued with issues. That being said, it was worth the effort and will greatly ease debugging. Hopefully Foundation and Vapor will add native support for stack traces and remove the burden of having to install a 3rd party repository and passing in build flags on Linux. If you are interested in getting stack traces in your Swift application, check out our <a href="https://github.com/nodes-vapor/stacked">Stacked</a> package. As for bug reporting with server-side-Swift, check out our <a href="https://github.com/nodes-vapor/bugsnag">Bugsnag</a> package.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/Jb1ca3NO2f0">Vojtech Bruzek</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What this WWDC means for us]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2017/06/21/What-this-WWDC-means-for-us</link>
            <guid>https://engineering.monstar-lab.com/en/post/2017/06/21/What-this-WWDC-means-for-us</guid>
            <pubDate>Wed, 21 Jun 2017 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>WWDC 2017 happened at the beginning of June in San Jose, California, and Apple presented lots of exciting changes. We already covered in <a href="https://www.nodesagency.com/ios-11-top-new-features-additions/">this article</a> the most important iOS additions from a user's perspective, but what exactly did WWDC 2017 bring for an iOS developer? Continuing the trend from the last year, the WWDC Keynote was less targeted to developers, and more towards media and users. The interesting things for developers are covered mostly in the Platforms State of the Union, and in the following sessions.</p>
<h2>Swift 4</h2>
<p>As was expected, iOS 11 comes with Swift 4. However, since Swift is open source, and all the development is done in the open, there were no big surprises here. <a href="https://twitter.com/olebegemann">Ole Begemann</a> made a playground a few weeks before WWDC showing <a href="https://oleb.net/blog/2017/05/whats-new-in-swift-4-playground/">what's new in Swift 4</a>, that also got a mention during one of the WWDC sessions.</p>
<p>There's a new <code>Codable</code> protocol, which hopes to end the war of the JSON parsers; there are a lot of changes to the <code>String</code> type, which is now a collection again; Swift 4 supports multi-line literal strings (an announcement that was greeted with lots of applause from the developers), and support for KVC was added (and to make it better, the key paths are strongly typed). However, the best part about Swift 4 is that it is source compatible with Swift 3. Upgrading codebases from Swift 3 to 4 is going to be very easy (<a href="https://github.com/nodes-ios/Codemine/commit/17cab6b9a3260357ba80c38ff091484ed1d7f80b">here</a>'s an example of updating <a href="https://github.com/nodes-ios/Codemine">Codemine</a> to the latest Swift version).</p>
<h2>Xcode 9</h2>
<p>But it's not Swift 4 that got developers excited at WWDC. The greatest news, for me, at least, was Xcode 9.</p>
<p>Xcode 9 finally brings refactoring for Swift code. Not only that, but the whole source editor was rewritten (in Swift). The source control integration has been improved, there are updates to the build system and Xcode Server comes by default with Xcode 9.</p>
<p align="center"><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/xcode-3_oHKU9h4bxf.gif"></p>
<p>Other noteworthy features are named colours in the asset catalogue, pure vector image support, a main thread checker, a new unexpected behaviour sanitizer, and wireless debugging.</p>
<h2>iOS 11</h2>
<p>With regards to iOS 11, the two new APIs that excited me the most are the new <code>PDFView</code> and the Drag and Drop. Drag and Drop is really interesting, especially for iPads. There is support for Drag and Drop on iPhones too, but that's only in the same app. For iPad, however, this opens up a lot of possibilities. The whole way of interacting with files and data across multiple applications has been updated. There are a lot of new possibilities for app developers, and I can't wait to see iPad-optimised apps in autumn.</p>
<p align="center"><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/ios_11_drag_and_drop_pltFnWyKjL.gif"></p>
<p>Regarding the new <code>PDFViewer</code>, I'm looking forward to not having to use a web view for displaying pdfs; even though that's fast, convenient and it worked, that always seemed a bit like a sub-optimal solution.</p>
<h2>What's next</h2>
<p>At WWDC we've been given a preview of what our next year will be like. We'll get new betas every approx. 2 weeks with the latest updates and changes. In the autumn iOS 11 will be released, and everyone will be able to use the apps we developed during the summer with the latest APIs and features.</p>
<p><br><br>
Image sources:</p>
<ul>
<li>Apple</li>
<li><a href="http://www.macworld.co.uk/review/ios-apps/ios-11-vs-ios-10-3660096/">Macworld</a></li>
<li><a href="https://www.raywenderlich.com/163940/wwdc-2017-initial-impressions">Ray Wenderlich</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Nodes Wars - My experience as a Vapor intern at Nodes in Copenhagen]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2017/06/01/Nodes-Wars-My-experience-as-a-Vapor-intern-at-Nodes-in-Copenhagen</link>
            <guid>https://engineering.monstar-lab.com/en/post/2017/06/01/Nodes-Wars-My-experience-as-a-Vapor-intern-at-Nodes-in-Copenhagen</guid>
            <pubDate>Thu, 01 Jun 2017 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Hello! My name is Valen, I am studying in Spain and the last subject of my training consist in an internship in a company. They gave me the chance to do this abroad and it seemed like a good opportunity. So, here I am, in <a href="https://www.nodesagency.com/">Nodes</a>, a digital agency in Copenhagen where I am staying for three months to do backend development in Swift using <a href="https://vapor.codes/">Vapor</a>.
They asked me to talk about my experience as an intern so, here I go:</p>
<h2>Episode 1: The Mac menace</h2>
<p>My first day was a day of discoveries. I was new to everything; new to working in a company, new to using a Mac, new to Swift and new to Vapor. At this point, it became very apparent to me that I had much to learn.<br>
It didn't take much time until I had acquired my first major lesson. A piece of knowledge that would accompany me for the rest of this journey I call "internship": how to use the coffee machine.</p>
<h2>Episode 2: Attack of the code</h2>
<p>Vapor is an interesting framework to work with. From the perspective of a newbie in backend-development it felt friendly and somehow "easy" to learn. The best thing is that you don't need to be an expert in Swift to make your first website using Vapor (example below).</p>
<p>After grabbing a few coffees, watching tons of tutorials from the internet and reading documentation, I was finally ready to start my first project: a "Rocket League" tournament system.</p>
<p>Once the name of the project was settled (just took me two hours), I started with the famous <code>vapor new scorecket</code></p>
<p><img src="https://pbs.twimg.com/media/CjeXpZQXEAA0pS9.png" alt=""></p>
<p>I started by doing the authentication and saving users into the database. Doing this with Vapor is really easy. You just need to have a Model, initialize it with the values you need and then save it:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">try</span> user.save()
</code></pre>
<p>So all I had to do was to get the data from the form to create a user and then save it. Just like that. Easy, right?
Because I didn't like the default HTML style, I added this tool called <a href="http://getskeleton.com/#examples">skeleton</a>. A lightweight CSS framework that helped me make my app more stylish. Here are how things look so far.</p>
<p><img src="http://i66.tinypic.com/op440z.png" alt=""></p>
<p>Now, we have added users, but what should they do?<br>
Well, the next step was to create Teams and to allow users to enroll in a team or to create their own. Also, to add the ability for a team to signup for a tournament.</p>
<p><img src="http://i63.tinypic.com/alosol.png" alt=""></p>
<p>Tournaments can only be created and started by the admin. And, once started, it will create all of the games automatically, with their respective dates and teams. After that, all that is left is for a user of one of the teams to introduce the results of the game. Following, the adversary team will get a message, asking to confirm the results of the game. Once accepted, the score of both teams will be updated.</p>
<p><img src="http://i66.tinypic.com/nmb2ja.png" alt=""></p>
<p>The project was really fun to work on and gave me a chance to learn the basics of Vapor. Giving me a better understanding of how websites work. Including the relationship between all of their components.</p>
<p>You can see all the full code <a href="https://github.com/valen90/scoreket">@Github</a></p>
<h2>Episode 3: Revenge of the bug</h2>
<p>While working on this project, I found something strange. I used one of the functions in the <a href="https://github.com/nodes-vapor/sugar">Sugar</a> package to generate the foreign keys for my database, but, when trying to reference the same table twice, an error appeared. So, Steffen (Lead Vapor developer at Nodes and the one responsible for giving me work to do and withstanding all the intern's incessant questions) told me to "fix that and submit a PR." That was a great idea! It made perfect sense. But... There was, at that moment, one major problem: I didn't know what a PR was.</p>
<p>Previously, I had only used Github to upload projects for my teachers to grade. I didn't really know anything about these pull requests (PRs), merging and branching. Luckily, my team helped me a lot and I finally managed to make the pull request. I can only describe the feeling of a pull request being merged as... addictive.</p>
<p><img src="http://i64.tinypic.com/339niw7.png" alt=""></p>
<p>That wasn't enough. I wanted more. I began adding features to some of the packages and fixing bugs that I found along the way. But then something unexpected happened.</p>
<h2>Episode 4: A new version</h2>
<p><a href="https://github.com/vapor/vapor/tree/master">Vapor 2</a> was released and that meant that all of the packages needed to be updated. It seemed like a good exercise for me to improve, so I got the task of updating some of them. Once you update one you start to see that the same things are repeated in the others. Because of this, I got better-and-better at updating these packages. That being said, some of them weren't that simple and required more time and effort. Of course, more help from my teammates and their infinite patience.<br>
Here is the list of some of the packages that I worked on:</p>
<ul>
<li><a href="https://github.com/nodes-vapor/sugar">sugar</a></li>
<li><a href="https://github.com/nodes-vapor/error-extended">error-extended</a></li>
<li><a href="https://github.com/nodes-vapor/paginator">paginator</a></li>
<li><a href="https://github.com/nodes-vapor/meta">meta</a></li>
<li><a href="https://github.com/nodes-vapor/flash">flash</a></li>
<li><a href="https://github.com/nodes-vapor/bugsnag">bugsnag</a></li>
<li><a href="https://github.com/nodes-vapor/aws">aws</a></li>
<li><a href="https://github.com/nodes-vapor/nstack">nstack</a></li>
<li><a href="https://github.com/nodes-vapor/gatekeeper">gatekeeper</a></li>
<li><a href="https://github.com/nodes-vapor/storage">storage</a></li>
<li><a href="https://github.com/nodes-vapor/data-uri">data-uri</a></li>
<li><a href="https://github.com/nodes-vapor/sanitized">sanitized</a></li>
<li><a href="https://github.com/nodes-vapor/push-urban-airship">push-urban-airship</a></li>
<li><a href="https://github.com/nodes-vapor/stacked">stacked</a></li>
</ul>
<p>After some updates and pull requests into GitHub (108 contributions :grin:), I became the first Swift programmer in my city. So, yeah, I am kind of a legend now.</p>
<p><img src="http://i65.tinypic.com/r9rds8.png" alt=""></p>
<p>Ok, ok, there are just five people in the ranking. But! It still counts!</p>
<h2>Episode 5: The test strikes back</h2>
<blockquote>
<p>Tests? I don't write tests. Only people who write bad code write tests - Brett</p>
</blockquote>
<p>I start this episode with a joke from Brett (developer at the Vapor team and also winner of the "teasing the intern" award).</p>
<p>Not only updating, but also writing tests to check that the code is correct is an important part of development. Also, I think that it gives you the chance to really know how the program works and to think about where the errors could appear. It's a good exercise to learn.</p>
<p><img src="http://i63.tinypic.com/vxdw1j.png" alt=""></p>
<p>In order to cover as much code as possible, I started to add some test to the existing projects,. It is sometimes hard to write tests because you want to avoid all of the external influences. To achieve this, you need to make these simulated objects, that mimic the behavior of real objects, called Mocks. This way, you can test everything in a more controlled, hopefully stateless, way.</p>
<p>After making the Mock Objects and thinking about how to test the different functions, if everything went well, you should get something that looks like this:</p>
<p><img src="http://i68.tinypic.com/1043vx1.png" alt=""></p>
<h2>Episode 6: Return of the Intern</h2>
<p>This internship only lasts for three months. So, sadly, I have to say goodbye. I want to say thanks to all the backend team for always giving a moment to helping me out, even with the stupidest questions. And thanks to the whole company for treating me like one of their own, trying to speak in Spanish sometimes, not just treating me like the "intern" and for making this experience an unforgettable one.</p>
<p>I will leave Copenhagen with some experience. Having learned something about Swift and Vapor; how to use a Mac; how to contribute to bigger projects making branches, forks and pull requests; how to make tests with as much coverage as possible and, of course, how to open a beer with just a lighter.</p>
<p>If you are considering doing an internship here at Nodes, I cannot imagine a better place to learn, to collaborate on real projects used by lots of people and be surrounded by people always willing to help you out. Not only inside, but also outside the working environment. Oh, have I mentioned the free coffee?</p>
<p>Well, that is all! I hope you enjoyed reading this post!</p>
<p>So long and thanks for all the fish.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Our experience at iOSCon 2017]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2017/05/10/iOSCon2017</link>
            <guid>https://engineering.monstar-lab.com/en/post/2017/05/10/iOSCon2017</guid>
            <pubDate>Wed, 10 May 2017 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>iOSCon is an annual conference in London, focusing mainly on Swift development for iOS, but with talks also spreading this year to as diverse subjects as Accessibility and making the most of the time you have with no regrets.
The conference was hosted at Skills Matter, a very well suited venue for this kind of small conference, including several rooms of varied sizes, good quality PA systems, a bar and a ping pong table. Also everything had a geeky edge to it with the names of the bar and rooms being space bar, Ctrl and Alt/Tab.</p>
<p align="center"><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/34273312331_ac5e8cefeb_z_Lb4ULKU1jY.jpg"></p>
<p>The conference had two tracks, which meant participants had to pick their favourite and vote to influence room allocations. Luckily this choice was pretty easy most of the time, as it was pretty clear which would be the most applicable to the kind of development we do. Nevertheless, I will make sure to watch the videos from the talks I didn’t attend, to get the complete exposure to the vision of the conference.</p>
<p>Some talks that stood out for me:</p>
<p><strong>The Grand Tour of iOS Architectures</strong> by <a href="https://twitter.com/dcutting">Dan Cutting</a></p>
<p>The Nodes iOS team is currently in the process of implementing a version of the Clean Architecture/VIP/VIPER pattern, and as such some confusion about which is which often ensues. This talk explained an array of architectures on a conceptual level, which succeeded in highlighting the respective differences and similarities in an easy-to-understand graphical way.</p>
<p><strong>Architecting Alive Apps</strong> by <a href="https://twitter.com/jdortiz">Jorge Ortiz Fuentes</a></p>
<p>At NSSpain some members of the Nodes iOS team were introduced to Clean Architecture in a workshop with Jorge Ortiz. This talk elaborated a little bit further on how to apply this architecture, by making abstractions between layers by use of protocols. Sometimes it is hard to immediately see the advantages of the Clean Architecture approach over the abundance of files and boilerplate code one has to deal with, but while observing Jorge's work it really starts to make sense.</p>
<p><strong>Workshop: Natural Swift: Write Swift the way it was meant to be written</strong> by <a href="https://twitter.com/twostraws">Paul Hudson</a></p>
<p>This was a 90 minute tour de force of immediately applicable Swift by Paul Hudson from <a href="http://hackingwithswift.com">HackingWithSwift.com</a>. The majority of the talk was spent explaining different uses of swift methods map, flatmap, filter and reduce. One would think this was quickly done, but in the hands of the speaker their uses ranged very broadly. He also touched on reference types vs value types as well as functional programming. Both his coding skills as well as talking skills were highly impressive.</p>
<p align="center"><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/34403918755_df0591066c_z_eO7EL8BVax.jpg"></p>
<p>Among others there was a talk about how to debug native Accessibility features in iOS by <a href="http://twitter.com/mostgood">Sally Shepherd</a>, which made me think about how avoiding to add the little extra to the benefit of people with disabilities is not a very inclusive approach to app development.
Also a talk about Minimum Viable tooling by <a href="http://twitter.com/miguelquinon">Miguel Angel Quinones</a> had me inspired by amongst other things talking about debugging shortcuts and using a static json response for test users for an app in debug mode, thus avoiding to have to enter credentials all the time for different users.</p>
<p align="center"><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/32939761313_103420004f_z_l3Dv9E18Mf.jpg"></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Haptic Feedback makes you vibrate!]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2017/04/21/Haptic-feedback-makes-you-vibrate</link>
            <guid>https://engineering.monstar-lab.com/en/post/2017/04/21/Haptic-feedback-makes-you-vibrate</guid>
            <pubDate>Fri, 21 Apr 2017 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>The iPhone could vibrate ever since it was launched. And so did almost all the other phones, even from before smartphones were a thing. But Apple took this function further than anyone else, by introducing the Taptic Engine in the Apple Watch, and afterwards in many other products, like the new MacBooks and iPhones. The Taptic Engine provides haptic feedback, more similar to real-life touches than standard vibration.</p>
<p>Together with iOS 10, Apple introduced an API for the Taptic Engine, which the developers can use to give haptic feedback to their users. To fully experience the haptic feedback you need an iPhone 7, 7 Plus or newer. Even though the API is introduced in iOS 10, it's only the iPhone 7 and iPhone 7 Plus that support this. On all previous devices, nothing happens when you try to do that.</p>
<h2>How to create haptic feedback</h2>
<p>Using the new API is super easy. With the <code>UIFeedbackGenerator</code> you can create haptic feedback with only two lines of code.</p>
<p>There are 7 different types of haptic feedback a developer can use. The only difference between those 7 types of feedback is the vibration pattern. Some of them are subtle, like the selection change, and others are more powerful, like the notification error.</p>
<p>The <code>UIFeedbackGenerator</code> has 3 subclasses, <code>UISelectionFeedbackGenerator</code>, <code>UIImpactFeedbackGenerator</code> and <code>UINotificationFeedbackGenerator</code>, and those are the ones a developer should work with. We will showing how to use them further below.</p>
<p>Due to the inherent difficulties of showing vibrations through a blog post, we are using the official videos of each type of feedback from Apple's Human Interface Guidelines. You can also run the code <a href="https://github.com/mariusc/Vibrate">from this repo</a> on your iPhone 7 or iPhone 7 Plus to try it out.</p>
<h3>Selection</h3>
<p>This one can be used when the user has to select from multiple discrete values, as he/she scrolls through them. For example, it's used in the <code>UIPickerView</code>. It feels like a light tap with each selection change. This is not to be used for example when selecting one of 4 radio button choices.</p>
<p><div class="html5-video-container">
        <video width="400" controls>
          <source src="<a href="https://developer.apple.com/design/human-interface-guidelines/ios/images/haptics/retarget.mp4">https://developer.apple.com/design/human-interface-guidelines/ios/images/haptics/retarget.mp4</a>" type="video/mp4">
        Your browser does not support HTML5 video.
        </video>
      </div>
<em>Source: <a href="https://developer.apple.com/design/human-interface-guidelines/ios/user-interaction/haptics/">Apple</a></em></p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> selectionFeedbackGenerator <span class="hljs-operator">=</span> <span class="hljs-type">UISelectionFeedbackGenerator</span>()
selectionFeedbackGenerator.selectionChanged()
</code></pre>
<h3>Impact</h3>
<p>The impact type of feedback is to be used to enhance a visual experience. For example, it can be used in a game when a collision occurs. I couldn't find this documented, but I'm almost sure that Impact - heavy is the type of feedback that the user feels when a peeked view pops full screen.</p>
<p><strong>Light</strong></p>
<p><div class="html5-video-container">
        <video width="400" controls>
          <source src="<a href="https://developer.apple.com/design/human-interface-guidelines/ios/images/haptics/impact_light.mp4">https://developer.apple.com/design/human-interface-guidelines/ios/images/haptics/impact_light.mp4</a>" type="video/mp4">
        Your browser does not support HTML5 video.
        </video>
      </div>
<em>Source: <a href="https://developer.apple.com/design/human-interface-guidelines/ios/user-interaction/haptics/">Apple</a></em></p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> lightImpactFeedbackGenerator <span class="hljs-operator">=</span> <span class="hljs-type">UIImpactFeedbackGenerator</span>(style: .light)
lightImpactFeedbackGenerator.impactOccurred()
</code></pre>
<p><strong>Medium</strong></p>
<p><div class="html5-video-container">
        <video width="400" controls>
          <source src="<a href="https://developer.apple.com/design/human-interface-guidelines/ios/images/haptics/impact_medium.mp4">https://developer.apple.com/design/human-interface-guidelines/ios/images/haptics/impact_medium.mp4</a>" type="video/mp4">
        Your browser does not support HTML5 video.
        </video>
      </div>
<em>Source: <a href="https://developer.apple.com/design/human-interface-guidelines/ios/user-interaction/haptics/">Apple</a></em></p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> mediumImpactFeedbackGenerator <span class="hljs-operator">=</span> <span class="hljs-type">UIImpactFeedbackGenerator</span>(style: .medium)
mediumImpactFeedbackGenerator.impactOccurred()
</code></pre>
<p><strong>Heavy</strong></p>
<p><div class="html5-video-container">
        <video width="400" controls>
          <source src="<a href="https://developer.apple.com/design/human-interface-guidelines/ios/images/haptics/impact_heavy.mp4">https://developer.apple.com/design/human-interface-guidelines/ios/images/haptics/impact_heavy.mp4</a>" type="video/mp4">
        Your browser does not support HTML5 video.
        </video>
      </div>
<em>Source: <a href="https://developer.apple.com/design/human-interface-guidelines/ios/user-interaction/haptics/">Apple</a></em></p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> heavyImpactFeedbackGenerator <span class="hljs-operator">=</span> <span class="hljs-type">UIImpactFeedbackGenerator</span>(style: .heavy)
heavyImpactFeedbackGenerator.impactOccurred()

</code></pre>
<h3>Notification</h3>
<p>The notification type of feedback should be used to indicate to the user that a task has completed. Depending on the outcome of the task (success, error, warning), the feedback pattern differs.</p>
<p><strong>Success</strong></p>
<p><div class="html5-video-container">
        <video width="400" controls>
          <source src="<a href="https://developer.apple.com/design/human-interface-guidelines/ios/images/haptics/success.mp4">https://developer.apple.com/design/human-interface-guidelines/ios/images/haptics/success.mp4</a>" type="video/mp4">
        Your browser does not support HTML5 video.
        </video>
      </div>
<em>Source: <a href="https://developer.apple.com/design/human-interface-guidelines/ios/user-interaction/haptics/">Apple</a></em></p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> successNotificationFeedbackGenerator <span class="hljs-operator">=</span> <span class="hljs-type">UINotificationFeedbackGenerator</span>()
successNotificationFeedbackGenerator.notificationOccurred(.success)
</code></pre>
<p><strong>Warning</strong></p>
<p><div class="html5-video-container">
        <video width="400" controls>
          <source src="<a href="https://developer.apple.com/design/human-interface-guidelines/ios/images/haptics/warning.mp4">https://developer.apple.com/design/human-interface-guidelines/ios/images/haptics/warning.mp4</a>" type="video/mp4">
        Your browser does not support HTML5 video.
        </video>
      </div>
<em>Source: <a href="https://developer.apple.com/design/human-interface-guidelines/ios/user-interaction/haptics/">Apple</a></em></p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> warningNotificationFeedbackGenerator <span class="hljs-operator">=</span> <span class="hljs-type">UINotificationFeedbackGenerator</span>()
warningNotificationFeedbackGenerator.notificationOccurred(.warning)
</code></pre>
<p><strong>Failure</strong></p>
<p><div class="html5-video-container">
        <video width="400" controls>
          <source src="<a href="https://developer.apple.com/design/human-interface-guidelines/ios/images/haptics/error.mp4">https://developer.apple.com/design/human-interface-guidelines/ios/images/haptics/error.mp4</a>" type="video/mp4">
        Your browser does not support HTML5 video.
        </video>
      </div>
<em>Source: <a href="https://developer.apple.com/design/human-interface-guidelines/ios/user-interaction/haptics/">Apple</a></em></p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">let</span> errorNotificationFeedbackGenerator <span class="hljs-operator">=</span> <span class="hljs-type">UINotificationFeedbackGenerator</span>()
errorNotificationFeedbackGenerator.notificationOccurred(.error)
</code></pre>
<p>Using the <code>UIFeedbackGenerator</code> may have a little latency. If you want to use it to match some sound or visual effects, you can call the <code>prepare()</code> method on the generator to put the Taptic Engine in a prepared state and reduce the latency. It will stay prepared a few seconds, or until the next feedback is triggered.</p>
<h2>When to use haptic feedback</h2>
<p>We saw that creating haptic feedback is super easy. However, the complicated part is knowing when and where to use it.</p>
<p>As usual, Apple helps with this, by adding a section on it to the <a href="https://developer.apple.com/design/human-interface-guidelines/ios/user-interaction/haptics/">Human Interface Guidelines</a>. And we can also learn a lot by looking at where the haptic feedback is used across iOS.</p>
<p>We all know by now that the Home button isn't an actual button anymore, it's just some very smartly done and well-placed haptic feedback. But that specific pattern doesn't come by default with the new <code>UIFeedbackGenerator</code>. Other parts of iOS though use the exact types of feedback that the developers can access through the new APIs.</p>
<p>For example, when you set an alarm or a timer, you'll notice a light tap whenever the selected time changes. Or when you type, and you hold down a letter on the keyboard to get to the various accents, the same light tap will inform you when you selected a different character.</p>
<p>Some controls, such as the <code>UIRefreshControl</code> have the feedback built in, and you will feel a small "knock" when you pull to refresh a scroll view. Same for zooming in or out a <code>UIScrollView</code> - zooming too much (either in or out) will also create a "knock".</p>
<p>If you dig deeper, you'll find even more creative use cases of the haptic feedback. For example, sending or viewing an iMessage with fireworks effects will send create "thuds" for every exploding firework.</p>
<p>To do haptic feedback right, you need to use it together with some animation or sound effects. Haptic feedback alone will probably feel out of place most of the time. Imagine getting a <code>UINotificationFeedbackType.error</code> feedback when you show an alert saying that the password you entered is wrong. That would be weird.</p>
<p>The Taptic Engine, together with the haptic feedback API has huge potential, and we are looking forward to seeing more creative usages of it.</p>
<h3>Resources</h3>
<ul>
<li><a href="https://developer.apple.com/design/human-interface-guidelines/ios/user-interaction/haptics/">iOS Human Interface Guidelines</a></li>
<li><a href="https://developer.apple.com/reference/uikit/uifeedbackgenerator">UIFeedbackGenerator API Reference</a></li>
<li><a href="https://github.com/mariusc/Vibrate">Demo project</a></li>
</ul>
<p><em>Article Photo by <a href="https://unsplash.com/photos/tMI2_-r5Nfo">Corinne Kutz</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Serpent - more than just another JSON mapping framework]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2017/03/01/Serpent-more-than-just-another-JSON-mapping-framework</link>
            <guid>https://engineering.monstar-lab.com/en/post/2017/03/01/Serpent-more-than-just-another-JSON-mapping-framework</guid>
            <pubDate>Wed, 01 Mar 2017 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p align="center"><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/Serpent_icon_MKPKLwKrTM.png" width="30%"></p>
<p>At <a href="https://nodesagency.com">Nodes</a>, we develop apps that rely a lot on complex REST APIs. The iOS team needed a fast and fully featured JSON mapping framework.
We had been using our own internal parsing framework for Objective-C for a long time, but when Swift came out we decided to create a new one, in Swift, taking advantage of protocol-oriented programming.</p>
<p>After many iterations and a renaming (our JSON mapping framework was previously known as Serializable), we are proud to announce that <a href="https://github.com/nodes-ios/Serpent">Serpent</a> has reached version 1.0.</p>
<h3>Another JSON mapping framework?</h3>
<p>More than that. Serpent is <a href="https://github.com/nodes-ios/SerpentPerformanceComparison#-the-results">one of the fastest JSON mappers</a> out there and has the most features, according to <a href="https://github.com/nodes-ios/SerpentPerformanceComparison#-feature-comparison">our analysis</a>.</p>
<p align="center"><img class="d-inline" src="https://raw.githubusercontent.com/nodes-ios/SerpentPerformanceComparison/master/chart.png"></p>
<p>And maybe you only have to parse and map small JSONs, and you don't care that much about the time it takes. But what about the time it takes to write the mapping code?</p>
<p>We made a tool that goes hand in hand with Serpent: with <img class="d-inline" src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/ModelBoiler_1281_Hkq7ey3ZGo.png" width="4%"> <strong>Model Boiler</strong> you only have to declare the properties on your model, and the parsing code will be generated for you, potentially saving you hours of work.</p>
<p align="center"><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/ModelBoiler_5S0fTmSARI.gif"></p>
<h3>But why build a new one?</h3>
<p>Yes, there are lots of other JSON mapping frameworks out there. But when we started developing Serpent, there was only SwiftyJSON, which wasn't exactly what we wanted. So we needed to build our own.</p>
<p>In February 2016, we moved the project to a <a href="https://github.com/nodes-ios/Serpent">public GitHub repo</a> and began developing in the open. At the same time, we started using this framework in all our apps, and at the moment, we can say that <strong>Serpent is used in apps with more than 1.3 million monthly users</strong>.</p>
<h3>Working with Serpent</h3>
<p>We built Serpent to make developers' life as easy as possible when creating models. So we also built a few goodies around Serpent, which allow the developer to save a lot of time when creating the models for the app.</p>
<h4><img class="d-inline" src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/TemplateIcon_ma6gVMOOZ7.png" width="4%"> Serpent Xcode File Template</h4>
<p>We made the <a href="https://github.com/nodes-ios/SerpentXcodeFileTemplate">Serpent Xcode File Template</a>, which adds a new file template for Xcode. No more typing <code>import Serpent</code> manually. It sounds small, but when you have to create 10-20 models, it saves you from a lot of annoyance.</p>
<p align="center"><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/687474703a2f2f692e696d6775722e636f6d2f6a75647a614a5a2e706e67_JSsVywYm5H.png" width="50%"></p>
<h4><img class="d-inline" src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/ModelBoiler_1281_Hkq7ey3ZGo.pgn" width="4%"> Model Boiler</h4>
<p>The most annoying part when working with JSONs in Swift is typing all the parsing code. We made a tool that works with Serpent that does that for you. The <a href="https://github.com/nodes-ios/ModelBoiler">Model Boiler</a> is a small macOS app that lives in your mac's menu bar. In Xcode (or your favourite editor), select the code for the model and its properties, and press a keyboard shortcut, and the Model Boiler will generate the necessary code for parsing to your Clipboard. You can just paste the parsing code in your model. And that's it.</p>
<p align="center"><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/modelboiler_QFktJAlXOv.gif" width="40%"></p>
<p>Together with the Xcode file template and with the Model Boiler, Serpent is (in our opinion) the easiest and most pleasant to use JSON mapping framework for Swift.</p>
<h3>Why Serpent?</h3>
<p>Here's a short list of some of the advantages that Serpent has:</p>
<ul>
<li>Its own <a href="https://github.com/nodes-ios/SerpentXcodeFileTemplate">Xcode file template</a></li>
<li>Its own code generator: <a href="https://github.com/nodes-ios/ModelBoiler"><img class="d-inline" src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/ModelBoiler_1281_Hkq7ey3ZGo.png" width="4%"> Model Boiler</a></li>
<li>The best balance between speed and features. We did <a href="https://github.com/nodes-ios/SerpentPerformanceComparison">the tests</a> and we compared it to 0some of the most popular JSON mapping frameworks out there.</li>
<li><a href="https://codecov.io/github/nodes-ios/Serpent">100% test coverage</a></li>
<li>Carthage, CocoaPods and Swift Package Manager support</li>
<li>Different versions of it are used in apps used by more than 1.3 million users every month (based on analytics data for some of our apps).</li>
</ul>
<p>We're really proud to be able to release the 1.0 version of Serpent. The biggest thanks go to all the <a href="https://github.com/nodes-ios/Serpent/graphs/contributors">contributors that made it possible</a>. We hope more developers find Serpent useful and give it a try in their apps.</p>
<p>Serpent is <a href="https://github.com/nodes-ios/Serpent">open source</a>. If you find bugs or have ideas for new features, you're more than welcome to contribute to Serpent. And if you really like what we're doing, check out the <a href="https://www.nodesagency.com/careers/">Nodes careers page</a> and join us to make awesome things together.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/hCb3lIB8L8E">Annie Spratt</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Android Bottom Navigation]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2016/11/15/Android-bottom-navigation</link>
            <guid>https://engineering.monstar-lab.com/en/post/2016/11/15/Android-bottom-navigation</guid>
            <pubDate>Tue, 15 Nov 2016 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>This blog post will give a quick introduction on the new Android Bottom Navigation component, how to setup your project to use it and a few tips on how to utilise it to get the best results.</p>
<p>Despite being part of the Material Design recommendations for a while, Google only recently introduced this on Version 25 of Design Support Library, meaning you no longer have to create your own custom implementations.</p>
<p>I created a sample project on GitHub with all the source code for this article. You can check it out <a href="https://github.com/jcmsalves/bottomNavigationSample">here</a> later if you want.</p>
<h2>1. How and when to use the Bottom Navigation</h2>
<p>Let's start with a quick reminder on <a href="https://material.google.com/components/bottom-navigation.html">Google and Material Design recommendations</a> when using the bottom navigation:</p>
<ul>
<li>
<p>Bottom navigation should be used to provide quick navigation between top-level views of an app.</p>
</li>
<li>
<p>Three to five top-level destinations: this component shouldn't be used if you have less than 3 or more than 5 options to show.</p>
<ul>
<li>2 items: recommend to use a regular tab layout on the top of the page</li>
<li>5+ items: recommend to use a navigation drawer, or as an alternative you could have one of your 5 items being a <em>More</em> option that takes the user to a separate navigation screen</li>
</ul>
</li>
<li>
<p>Bottom navigation and tabs: think twice if you want to use both widgets at the same time. If you really have to, take extra caution with navigation and animation to avoid confusing the user</p>
</li>
<li>
<p>Menu items style: this is how it works by default</p>
<ul>
<li>3 items: if using 3 items all of them should have Icon and Text visible all the time</li>
<li>3+ items: if using more than 3 items only the active item should have visible text</li>
</ul>
</li>
</ul>













<table><thead><tr><th align="center">3 menu items</th><th align="center">4 menu items</th></tr></thead><tbody><tr><td align="center"><img src="https://cdn-laravel.vapor.cloud/image/nstack/default/3buttonsscreenshot1_XToNZrXuvc.png" width="300"></td><td align="center"><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/4buttonsscreenshot1_ePCWRdtsm9.png" width="300"></td></tr></tbody></table>
<h2>2. Now for the fun part. Let's build this!</h2>
<p>We’ve covered some of the most important recommendations when using the bottom navigation widget, let's move on to the interesting part and implement it in our app.</p>
<p>As with the other support library components you firstly need to add the dependency to your app Gradle file.</p>
<pre><code class="hljs language-gradle">compile ‘com.android.support:design:25.0.0’
</code></pre>
<p>Now we can start using the component straight away. Here’s a quick list on what we'll need to do:</p>
<ol>
<li>Add the widget to the layout file where we want to show it</li>
<li>Create a menu layout file with our options</li>
<li>Create a state drawable file to control the menu item different states</li>
<li>Implement a listener to detect when we tap each item</li>
</ol>
<h3>2.1 Add the bottom navigation widget:</h3>
<p>Let's add the widget as follows to the MainActivity layout file:</p>
<pre><code class="hljs language-xml"><span class="hljs-tag">&#x3C;<span class="hljs-name">android.support.design.widget.BottomNavigationView</span>
        <span class="hljs-attr">android:id</span>=<span class="hljs-string">"@+id/bottomNavigation"</span>
        <span class="hljs-attr">android:layout_width</span>=<span class="hljs-string">"match_parent"</span>
        <span class="hljs-attr">android:layout_height</span>=<span class="hljs-string">"wrap_content"</span>
        <span class="hljs-attr">app:itemBackground</span>=<span class="hljs-string">"@color/colorPrimary"</span>
        <span class="hljs-attr">app:itemIconTint</span>=<span class="hljs-string">"@drawable/menu_item_selector"</span>
        <span class="hljs-attr">app:itemTextColor</span>=<span class="hljs-string">"@drawable/menu_item_selector"</span>
        <span class="hljs-attr">app:menu</span>=<span class="hljs-string">"@menu/bottom_navigation_menu"</span>></span>
    <span class="hljs-tag">&#x3C;/<span class="hljs-name">android.support.design.widget.BottomNavigationView</span>></span>
</code></pre>
<p>Remember to make it aligned at the bottom of the screen. You can check the full layout file <a href="https://github.com/jcmsalves/bottomNavigationSample/blob/master/app/src/main/res/layout/activity_main.xml">here</a> for reference.</p>
<p>As you can see, there are some properties on the widget. Let's take a look at them in a bit more detail:</p>
<ul>
<li><strong>app:itemBackground -</strong> the background colour to be used in the bottom navigation widget. In our case, this is Nodes pink</li>
<li><strong>app:itemIconTint -</strong> the tint to be used in the menu item icons</li>
<li><strong>app:itemTextColor -</strong> the colour to be used in the menu item text</li>
<li><strong>app:menu -</strong> reference to the menu layout file that we're creating in the next step</li>
</ul>
<h3>2.2 Create a menu layout:</h3>
<p>There are no hidden tricks here, we can simply create a menu file like we would usually for any other Android menu. The menu file should have id, a reference for the drawable and the text to display.</p>
<pre><code class="hljs language-xml"><span class="hljs-meta">&#x3C;?xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"utf-8"</span>?></span>
<span class="hljs-tag">&#x3C;<span class="hljs-name">menu</span> <span class="hljs-attr">xmlns:android</span>=<span class="hljs-string">"http://schemas.android.com/apk/res/android"</span>></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">item</span>
        <span class="hljs-attr">android:id</span>=<span class="hljs-string">"@+id/action_home"</span>
        <span class="hljs-attr">android:enabled</span>=<span class="hljs-string">"true"</span>
        <span class="hljs-attr">android:icon</span>=<span class="hljs-string">"@drawable/ic_home_white_24dp"</span>
        <span class="hljs-attr">android:title</span>=<span class="hljs-string">"@string/menu_item_home"</span> /></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">item</span>
        <span class="hljs-attr">android:id</span>=<span class="hljs-string">"@+id/action_car"</span>
        <span class="hljs-attr">android:enabled</span>=<span class="hljs-string">"true"</span>
        <span class="hljs-attr">android:icon</span>=<span class="hljs-string">"@drawable/ic_directions_car_white_24dp"</span>
        <span class="hljs-attr">android:title</span>=<span class="hljs-string">"@string/menu_item_car"</span> /></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">item</span>
        <span class="hljs-attr">android:id</span>=<span class="hljs-string">"@+id/action_bike"</span>
        <span class="hljs-attr">android:enabled</span>=<span class="hljs-string">"true"</span>
        <span class="hljs-attr">android:icon</span>=<span class="hljs-string">"@drawable/ic_directions_bike_white_24dp"</span>
        <span class="hljs-attr">android:title</span>=<span class="hljs-string">"@string/menu_item_bike"</span> /></span>

    <span class="hljs-tag">&#x3C;<span class="hljs-name">item</span>
        <span class="hljs-attr">android:id</span>=<span class="hljs-string">"@+id/action_walk"</span>
        <span class="hljs-attr">android:enabled</span>=<span class="hljs-string">"true"</span>
        <span class="hljs-attr">android:icon</span>=<span class="hljs-string">"@drawable/ic_directions_walk_white_24dp"</span>
        <span class="hljs-attr">android:title</span>=<span class="hljs-string">"@string/menu_item_walk"</span> /></span>
<span class="hljs-tag">&#x3C;/<span class="hljs-name">menu</span>></span>
</code></pre>
<p>Easy right?</p>
<h3>2.3 Create a state drawable:</h3>
<p>At this stage we have created a functional bottom navigation, but we all know the importance of great UX/UI, so let’s customise this.</p>
<p>Right now all the icons and corresponding text are the same colour, no matter if they are selected or not.</p>
<p>You may have noticed that in the Widget xml definition in part 1 there was already a reference for <code>@drawable/menu_item_selector</code> in the itemIconTint and itemTextColor properties.</p>
<p>So, we just need to create this to make everything work as expected. Create the file with the following content:</p>
<pre><code class="hljs language-xml"><span class="hljs-meta">&#x3C;?xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"utf-8"</span>?></span>
<span class="hljs-tag">&#x3C;<span class="hljs-name">selector</span> <span class="hljs-attr">xmlns:android</span>=<span class="hljs-string">"http://schemas.android.com/apk/res/android"</span>></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">item</span> <span class="hljs-attr">android:color</span>=<span class="hljs-string">"@color/colorWhite"</span> <span class="hljs-attr">android:state_checked</span>=<span class="hljs-string">"true"</span> /></span>
    <span class="hljs-tag">&#x3C;<span class="hljs-name">item</span> <span class="hljs-attr">android:color</span>=<span class="hljs-string">"@color/colorPrimaryDark"</span> <span class="hljs-attr">android:state_checked</span>=<span class="hljs-string">"false"</span> /></span>
<span class="hljs-tag">&#x3C;/<span class="hljs-name">selector</span>></span>
</code></pre>
<p>As you can see it's a really simple selector returning 2 different colours for state checked and non-checked. Feel free to extended it to cover other states.</p>
<h3>2.4 Implement NavigationItemSelected listener:</h3>
<p>To complete our sample we need to listen to the tap events on each menu item. The activity layout from the sample contains a simple Frame Layout with a centered text view emulating the Fragment title.</p>
<p>Add this code to your Activity:</p>
<pre><code class="hljs language-java"><span class="hljs-type">BottomNavigationView</span> <span class="hljs-variable">bottomNavigationView</span> <span class="hljs-operator">=</span> (BottomNavigationView)
                findViewById(R.id.bottomNavigation);

        <span class="hljs-keyword">final</span> <span class="hljs-type">TextView</span> <span class="hljs-variable">fragmentNameTv</span> <span class="hljs-operator">=</span> (TextView)
                findViewById(R.id.fragmentName);

        fragmentNameTv.setText(getResources().getString(R.string.fragment_home_title));

        bottomNavigationView.setOnNavigationItemSelectedListener(<span class="hljs-keyword">new</span> <span class="hljs-title class_">BottomNavigationView</span>.OnNavigationItemSelectedListener() {
            <span class="hljs-meta">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">onNavigationItemSelected</span><span class="hljs-params">(<span class="hljs-meta">@NonNull</span> MenuItem item)</span> {

                <span class="hljs-keyword">switch</span> (item.getItemId()) {
                    <span class="hljs-keyword">case</span> R.id.action_home:
                        fragmentNameTv.setText(getResources().getString(R.string.fragment_home_title));
                        <span class="hljs-keyword">break</span>;

                    <span class="hljs-keyword">case</span> R.id.action_car:
                        fragmentNameTv.setText(getResources().getString(R.string.fragment_car_title));
                        <span class="hljs-keyword">break</span>;

                    <span class="hljs-keyword">case</span> R.id.action_bike:
                        fragmentNameTv.setText(getResources().getString(R.string.fragment_bike_title));
                        <span class="hljs-keyword">break</span>;

                    <span class="hljs-keyword">case</span> R.id.action_walk:
                        fragmentNameTv.setText(getResources().getString(R.string.fragment_walk_title));
                        <span class="hljs-keyword">break</span>;
                }
                <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
            }
        });
</code></pre>
<p>First we get references to the BottomNavigation Widget and the TextView from the layout.</p>
<p>Then we add the OnNavigationItemSelectedListener to the widget and use a switch to detect with menu item was tapped and update the TextView with the corresponding fragment title.</p>
<p>And it's done. Super simple, right? Here's the final result:</p>













<table><thead><tr><th align="center">3 menu items</th><th align="center">4 menu items</th></tr></thead><tbody><tr><td align="center"><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/3buttonsgif_VtILFZWqWD.gif" width="300"></td><td align="center"><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate_values/4buttonsgif_n1Avgls13h.gif" width="300"></td></tr></tbody></table>
<p>Having implemented a custom bottom navigation view myself in previous projects, I think the new Android Bottom Navigation component is a brilliant addition to the support library. As well as not having to do any boilerplate code, there are numerous benefits you get for free from this new component.</p>
<p>If you don't have any specific requirements or custom UI you need to implement for bottom navigation in an app, give the new Bottom Navigation a try and you'll see how quickly and easy it is to implement.</p>
<hr>
<p>Take a look at some of our Android Libraries <a href="https://github.com/nodes-android">here</a></p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/Mkh2La9fEDY">Rohit Tandon</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Droidcon London 2016]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2016/11/15/DroidCon-London-2016</link>
            <guid>https://engineering.monstar-lab.com/en/post/2016/11/15/DroidCon-London-2016</guid>
            <pubDate>Tue, 15 Nov 2016 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Last year we attended DroidCon London and it was my first Android conference ever. Before that I attended CocoaConf in Las Vegas, as I mostly did iOS back then. DroidCon London 2015 was exciting because at that point I had been doing Android exclusively for a long time - and a lot of interesting speakers were attending. Chet Haase, Google employee, and Jake Wharton to name a few. Overall I liked it very much and the travel time back to Denmark weren't as long, heh.</p>
<p>This year Jake and Chet were attending again and the overall program looked more interesting than last year. Security and patterns seems to be the craze this year, and it's really some of the things that has been a focus area internally here at Nodes as well.</p>
<p>DroidCon London is located super close to our new London office at St. John Street, so we had the pleasure to see our Android developer Mario attending as a volunteer this year! Awesome!</p>
<h3>Day 1</h3>
<p><strong>What's NNNNNNNNew in Android Security?</strong> <em>by Scott Alexander-Bown</em><br>
Turned out to be a great talk! Very obvious that Scott have spent some time with the nitpicks and dark corners of implementing security and encryption on Android. Followed him on twitter after this talk(<a href="https://twitter.com/scottyab">@scottyab</a>)! Also the first time I heard about the SafetyNet API. A Google API that checks for a device's security state and whether it's rooted or not.<br>
Scott created a helper around that: <a href="https://github.com/scottyab/safetynethelper">https://github.com/scottyab/safetynethelper</a>
Further info: <a href="https://developer.android.com/training/safetynet/index.html">Android Training docs</a></p>
<p>In Android 7.0 Google introduced Network Security Configuration, which is a way to describe what protocols and endpoints are allowed to connect to. <a href="https://developer.android.com/training/articles/security-config.html">Google has excellent info here.</a> Commonsguy have backported this tool to Android 4.2 here, which is super awesome:
<a href="https://github.com/commonsguy/cwac-netsecurity">https://github.com/commonsguy/cwac-netsecurity</a></p>
<p><strong>Scaling Android @Facebook</strong> <em>by Marco Cova &#x26; Balazs Balazs</em><br>
Impressive talk about the insane numbers behind the Facebook app and what kind of process it takes to support that. If I remember correctly Facebook commits up to 3500 times a week, and due to the massive codebase - everything is split into modules to avoid unnecessary recompiles. To further improve on that, Facebook has created a custom build tool called <em>Buck</em> around the module structure they depend on for fast deployment.</p>
<p>Going over all the tools Facebook uses, Infer got a mention as well. I head about this a year or so ago, but now that we at Nodes are beginning to integrate CI on client apps, this is super relevant. <a href="http://fbinfer.com/">Infer</a> is a static code analyzer and will correct and point out issues and possible runtime crashes. Definitely something we want in our CI toolchain.</p>
<p>Fun side story at the lunch tables: Facebook apparently had so many commits on their repo that they had a hash collision between two commits.</p>
<h3>Day 2</h3>
<p><strong>What's New in Android</strong> <em>by Chet Haase</em><br>
(Chet Haase is one of the main public facing Google employees and Lead on the Android UI Toolkit team)</p>
<p>This talk was kind of funny, not because Chet is a funny guy - but because Chet was <em>really</em> pitching the Google Pixel phone. It almost felt like too much. I also got the feeling he was told to do it. Nevertheless, the Android 7.1 walk through was pretty good and it was good with some context around the different new features.</p>
<p><strong>Android Application Security, The Right Way</strong> <em>by Dario Incalza</em><br>
I didn't really intend to watch this one, I wanted to see the Spotify talk <em>"Breaking Spotify’s release cycle by using the backend to drive the UI and feature releases"</em>. I'm glad I did though, because it turned out to be the best talk around security this year. The talk was marked as a beginner talk, but gave so much info and reflection on every library, really valuable info.
I've been reading a lot of documentation on encryption via the KeyStore, but Dario gave a lot pros and cons about every API level and what it gives us. In short (if I remember correctly):</p>
<ul>
<li><em>Android 7</em>: iOS level security (on the Google Pixel at least)</li>
<li><em>Android 6</em>: Almost iOS level, but you cant besure that encryption keys are protected in hardware. Also new API's for file encryption/device protected storage are not available.</li>
<li><em>Android 5</em>: First fingerprint authentication API and KeyStore API supporting user authentication.</li>
</ul>
<p><strong>Radical RecyclerView</strong> <em>by Lisa Wray</em><br>
Most of the time we implement basic RecyclerViews as simple ListViews - and that tends to be a bit annoying. So it was kind of fun to see all the nifty stuff you can do with RecyclerViews. We were also shown a few patterns around common use cases. Really surprised by this talk.</p>
<p><strong>Android Architecture Blueprints</strong> <em>by David Gonzalez &#x26; Jose Alcerreca</em><br>
MVP is all the craze and having the perfect architecture is the target these days. The guys have implemented a simple To Do-app in 7 different ways: <a href="https://github.com/googlesamples/android-architecture">https://github.com/googlesamples/android-architecture</a></p>
<p>The actual talk was kind of quick, because David and Jose wanted to focus on questions and having a discussion around patterns in general - perfect. Strong opinions were voiced and since David and Jose are funny guys, the talk ended up being the perfect closing for the conference for us.</p>
<p>Side note: Chet Haase were attending this talk, and the guys received a couple of very Google specific questions - like <em>"Is the platform team considering any of the new patterns?"</em> - and everytime they were going to answer, they looked at Chet for confirmation. You probably had to be there.</p>
<h3>Summary</h3>
<p>Great talks this year, and since it was located so close to our London office, we walked over there and watched the Apple MacBook keynote!</p>
<p>All the security talks kind of gave me the impression that we need a wrapper or a framework around the encryption/decryption on Android. We see more and more demand for it on client apps, and having all that code floating around on different projects is annoying.</p>
<p>I looked into facebook's conceal library - but it seems to priotize speed too much over security. We'll see, maybe material for another blog post 🔥</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[6 simple steps to get your first Vapor JSON response]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2016/11/08/6-simple-steps-to-setup-vapor</link>
            <guid>https://engineering.monstar-lab.com/en/post/2016/11/08/6-simple-steps-to-setup-vapor</guid>
            <pubDate>Tue, 08 Nov 2016 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>We will cover the initial setup and how to make both POST and GET with controllers and models.</p>
<p>This tutorial will assume that you already have installed Vapor and <a href="https://developer.apple.com/xcode/">Xcode 8</a> and are familiar with Swift 3.</p>
<p>### 1. Creating your project</p>
<p>Now open your terminal and cd into the place you want your project.</p>
<p>Now run (also in the terminal, of course change the ProjectName to your actual project name)</p>
<pre><code class="hljs language-bash">vapor new ProjectName
</code></pre>
<p>You should now see an ASCII art of the Vapor logo. Like so:</p>
<pre><code class="hljs language-bash">

                                                 **~~**
                                               **~~~~~~**
                                             **~~~~~~~~~~**
                                           **~~~~~~~~~~~~~~**
                                         **~~~~~~~~~~~~~~~~~~**
                                       **~~~~~~~~~~~~~~~~~~~~~~**
                                      **~~~~~~~~~~~~~~~~~~~~~~~~**
                                     **~~~~~~~~~~~~~~~~~~~~~~~~~~**
                                    **~~~~~~~~~~~~~~~~~~~~~~~~~~~~**
                                    **~~~~~~~~~~~~~~~~~~~~~~~~~~~~**
                                    **~~~~~~~~~~~~~~~~~~~~~++++~~~**
                                     **~~~~~~~~~~~~~~~~~~~++++~~~**
                                      ***~~~~~~~~~~~~~~~++++~~~***
                                        ****~~~~~~~~~~++++~~****
                                           *****~~~~~~~~~*****
                                              *************

                                     _       __    ___   ___   ___
                                    \ \  /  / /\  | |_) / / \ | |_)
                                     \_\/  /_/--\ |_|   \_\_/ |_| \
                                       a web framework <span class="hljs-keyword">for</span> Swift

                                 Project <span class="hljs-string">"ProjectName"</span> has been created.
                          Type `<span class="hljs-built_in">cd</span> ProjectName ` to enter the project directory.
                                                 Enjoy!

</code></pre>
<p>Congratulations, you have succesfully created your first Vapor project. Let's now try to run the project. First cd into your project (<code>cd ProjectName</code>) and after that run:</p>
<pre><code class="hljs language-bash">vapor xcode
</code></pre>
<p>Vapor will then fetch the dependencies and run the project. Vapor will now ask you if you want to open the project, and yes, yes we do! (So type y to open)</p>
<pre><code class="hljs language-bash">Select the `App` scheme to run.
Open Xcode project?
y/n>
</code></pre>
<p>You should now have your Xcode project open. Let's check if we have everything running correctly.
So in Xcode press the 'Run' button in the top left corner of the UI (or Cmd + R). You should now see this in your console in Xcode (Make sure that you switch target from the framework to App, just next to the stop button)</p>
<pre><code class="hljs language-bash">No <span class="hljs-built_in">command</span> supplied, defaulting to serve...
No preparations.
Server <span class="hljs-string">'default'</span> starting at 0.0.0.0:8080
</code></pre>
<p>Now c+p <code>0.0.0.0:8080</code> into your browser. If you see the 'It works.' screen then you have successfully run your first local Vapor.</p>
<p>Note: Beacuse Vapor is constantly updating and new versions pushed out frequently then you might have this error in your Xcode project after running for the first time.</p>
<pre><code class="hljs language-bash">ld: library not found <span class="hljs-keyword">for</span> -lCLibreSSL <span class="hljs-keyword">for</span> architecture x86_64
clang: error: linker <span class="hljs-built_in">command</span> failed with <span class="hljs-built_in">exit</span> code 1 (use -v to see invocation)
</code></pre>
<p>If you didn't get that error then just go to section two of this article. If you did then open your terminal and first run
<code>vapor build --clean</code>, after that run a <code>vapor xcode</code> that should do the trick. (This basically cleans the project and compiles it again)</p>
<p>And hopefully you should be ready to rock and roll.</p>
<p>### 2. Creating a model</p>
<p>Open the terminal again, and run:</p>
<pre><code class="hljs language-groovy">touch Sources<span class="hljs-regexp">/App/</span>Models/Car.swift
</code></pre>
<p>After that run <code>vapor xcode</code> this will make sure that you get the correct path for the model. If you open Xcode then you should be able to see your model in the Models folder.</p>
<p>Fill the model with the code below:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Car</span> {

    <span class="hljs-comment">//Add your properties like you normally would in iOS</span>
    <span class="hljs-keyword">var</span> name: <span class="hljs-type">String</span>
    <span class="hljs-keyword">var</span> color: <span class="hljs-type">String</span>
    <span class="hljs-keyword">var</span> milesDriven: <span class="hljs-type">Int</span>

    <span class="hljs-comment">//Convinience method for instanciation of our object</span>
    <span class="hljs-keyword">init</span>(<span class="hljs-params">name</span>: <span class="hljs-type">String</span>, <span class="hljs-params">color</span>: <span class="hljs-type">String</span>, <span class="hljs-params">milesDriven</span>: <span class="hljs-type">Int</span>) {
        <span class="hljs-keyword">self</span>.name <span class="hljs-operator">=</span> name
        <span class="hljs-keyword">self</span>.color <span class="hljs-operator">=</span> color
        <span class="hljs-keyword">self</span>.milesDriven <span class="hljs-operator">=</span> milesDriven
    }
}
</code></pre>
<p>### 3. Returning some JSON baby</p>
<p>Vapor is using a class called <code>Droplet</code>, the Droplet is the main entry point on the server (If you are familiar with iOS development then this would be our AppDelegate class or your entry point in Storyboard). The Droplet instance will contain all providers, routes and makes sure that everything has been linked correctly to run the different functionalities in the run time. The line <code>drop.run()</code> in the <code>main.swift</code> is where the server actually gets booted, so everything you need to add has to be before this line.</p>
<p>Now open the <code>main.swift</code> file and add the following code right above the <code>drop.run()</code></p>
<pre><code class="hljs language-swift"><span class="hljs-comment">//Declaring our first route, this will be a 'get' (could also be 'post' etc..)</span>
drop.get(<span class="hljs-string">"get_a_car"</span>) { request <span class="hljs-keyword">in</span>

    <span class="hljs-comment">//Creating our object</span>
    <span class="hljs-keyword">let</span> car <span class="hljs-operator">=</span> <span class="hljs-type">Car</span>(name: <span class="hljs-string">"Toyota"</span>, color: <span class="hljs-string">"Red"</span>, milesDriven: <span class="hljs-number">0</span>)

    <span class="hljs-comment">//Formating it into JSON</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> <span class="hljs-type">JSON</span>(node: [
        <span class="hljs-string">"name"</span> : car.name,
        <span class="hljs-string">"color"</span> : car.color,
        <span class="hljs-string">"miles_driven"</span> : car.milesDriven
	])
}

</code></pre>
<p>Now Run your Xcode project. You should now see this in your console in Xcode (Make sure that you switch target from the framework to App, just next to the stop button)</p>
<pre><code class="hljs language-bash">No <span class="hljs-built_in">command</span> supplied, defaulting to serve...
No preparations.
Server <span class="hljs-string">'default'</span> starting at 0.0.0.0:8080
</code></pre>
<p>Now open your browser and type in <code>0.0.0.0:8080/get_a_car</code>, you
should now have returned your first JSON feed!
Of course, Vapor is way smarter than this. So please go ahead an delete the code you added in step 3.</p>
<p>### 4. Creating a Controller</p>
<p>Now go back to your terminal and run <code>touch Sources/App/Controllers/CarController.swift</code> and again <code>vapor xcode</code>, the Controllers folder should now contain your newly created CarController.swift</p>
<p>Before we can make any good use of our new Controller then we have to update our <code>Car.swift</code> model to actually get some of Vapors awesome magic.</p>
<p>So jump back into the <code>Car.swift</code> file and update the file to have the following:</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">//Importing the Vapor framework to make the model support dependencies</span>
<span class="hljs-keyword">import</span> Vapor
<span class="hljs-comment">//Importing Fluent which is Vapors powering of databases and tables</span>
<span class="hljs-keyword">import</span> Fluent

<span class="hljs-comment">//Subclassing our Car model from 'Model' (Vapors magic model object)</span>
<span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Car</span>: <span class="hljs-title class_">Model</span> {

    <span class="hljs-comment">//Vapor uses 'Node' as their Model ids. This is the datatype that they use to make lookup and look at the primary key in the DB's.</span>
    <span class="hljs-keyword">var</span> id:<span class="hljs-type">Node</span>?

    <span class="hljs-keyword">var</span> name: <span class="hljs-type">String</span>
    <span class="hljs-keyword">var</span> color: <span class="hljs-type">String</span>
    <span class="hljs-keyword">var</span> milesDriven: <span class="hljs-type">Int</span>

    <span class="hljs-keyword">init</span>(<span class="hljs-params">name</span>: <span class="hljs-type">String</span>, <span class="hljs-params">color</span>: <span class="hljs-type">String</span>, <span class="hljs-params">milesDriven</span>: <span class="hljs-type">Int</span>) {
        <span class="hljs-keyword">self</span>.name <span class="hljs-operator">=</span> name
        <span class="hljs-keyword">self</span>.color <span class="hljs-operator">=</span> color
        <span class="hljs-keyword">self</span>.milesDriven <span class="hljs-operator">=</span> milesDriven
    }

    <span class="hljs-comment">//Adding one of Vapors protocols to conform to the 'Model' object. This basically makes sure that data is mapped correctly when getting extracted from a data source such as a DB.</span>
    <span class="hljs-keyword">init</span>(<span class="hljs-params">node</span>: <span class="hljs-type">Node</span>, <span class="hljs-params">in</span> <span class="hljs-params">context</span>: <span class="hljs-type">Context</span>) <span class="hljs-keyword">throws</span> {
        id <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> node.extract(<span class="hljs-string">"id"</span>)
        name <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> node.extract(<span class="hljs-string">"name"</span>)
        color <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> node.extract(<span class="hljs-string">"color"</span>)
        <span class="hljs-comment">//We are making this underscored because that's how we normally name attributes in a database table</span>
        milesDriven <span class="hljs-operator">=</span> <span class="hljs-keyword">try</span> node.extract(<span class="hljs-string">"miles_driven"</span>)
    }

    <span class="hljs-comment">//makeNode makes sure that data can be saved into the given database (this is made super dynamic)</span>
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">makeNode</span>(<span class="hljs-params">context</span>: <span class="hljs-type">Context</span>) <span class="hljs-keyword">throws</span> -> <span class="hljs-type">Node</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> <span class="hljs-type">Node</span>(node: [
            <span class="hljs-string">"id"</span>: id,
            <span class="hljs-string">"name"</span>: name,
            <span class="hljs-string">"color"</span>: color,
            <span class="hljs-comment">//We are making this underscored because that's how we normally name attributes in a database table</span>
            <span class="hljs-string">"miles_driven"</span>: milesDriven
        ])
    }
}

<span class="hljs-comment">//This is Vapor way of making migrations, leave them empty for now. We will add it later</span>
<span class="hljs-keyword">extension</span> <span class="hljs-title class_">Car</span>: <span class="hljs-title class_">Preparation</span> {
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">prepare</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">database</span>: <span class="hljs-type">Database</span>) <span class="hljs-keyword">throws</span> {
        <span class="hljs-comment">//</span>
    }

    <span class="hljs-keyword">static</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">revert</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">database</span>: <span class="hljs-type">Database</span>) <span class="hljs-keyword">throws</span> {
        <span class="hljs-comment">//</span>
    }
}
</code></pre>
<p>So now our model should be ready for some cool controller action. Please now open our <code>CarController.swift</code> and add the following code:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> Vapor
<span class="hljs-comment">//Import HTTP for getting all our response types, codes, etc..</span>
<span class="hljs-keyword">import</span> HTTP

<span class="hljs-comment">//Adopt the ResourceRepresentable protocol in our Controller</span>
<span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">CarController</span>: <span class="hljs-title class_">ResourceRepresentable</span> {

    <span class="hljs-comment">//This will get called if the index in 'makeResource()' below will be called.</span>
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">index</span>(<span class="hljs-params">request</span>: <span class="hljs-type">Request</span>) <span class="hljs-keyword">throws</span> -> <span class="hljs-type">ResponseRepresentable</span> {

        <span class="hljs-comment">//Create an object</span>
        <span class="hljs-keyword">let</span> car <span class="hljs-operator">=</span> <span class="hljs-type">Car</span>(name: <span class="hljs-string">"Fiat"</span>, color: <span class="hljs-string">"SpaceGrey"</span>, milesDriven: <span class="hljs-number">1000</span>)
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> car.converted(to: <span class="hljs-type">JSON</span>.<span class="hljs-keyword">self</span>)
    }

    <span class="hljs-comment">//This is the function the figure out what method that should be called depending on the HTTP request type. We will here start with the get.</span>
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">makeResource</span>() -> <span class="hljs-type">Resource</span>&#x3C;<span class="hljs-type">Car</span>> {
        <span class="hljs-keyword">return</span> <span class="hljs-type">Resource</span>(
            index: index
        )
    }
}
</code></pre>
<p>This is how some of the simplest controllers will look in Vapor. Now let's try to see how we will use them in our <code>Droplet</code>.</p>
<p>### 5. Grouped routes and Controller bindings</p>
<p>Are you ready to see some magic? Ok, now open the <code>main.swift</code> let's start by doing some house cleaning, delete the following lines from the file:</p>
<pre><code class="hljs language-swift">drop.get { req <span class="hljs-keyword">in</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> drop.view.make(<span class="hljs-string">"welcome"</span>, [
    	<span class="hljs-string">"message"</span>: drop.localization[req.lang, <span class="hljs-string">"welcome"</span>, <span class="hljs-string">"title"</span>]
    ])
}

drop.resource(<span class="hljs-string">"posts"</span>, <span class="hljs-type">PostController</span>())
</code></pre>
<p>and add the following lines of code:</p>
<pre><code class="hljs language-swift"><span class="hljs-comment">//Creating a route group, in this way you won't have to add the same slugs over and over again</span>
drop.group(<span class="hljs-string">"api"</span>) { api <span class="hljs-keyword">in</span>
    <span class="hljs-comment">//Adding a sub slug to our URL and redirecting all requests to the CarController we just built.</span>
    api.resource(<span class="hljs-string">"cars"</span>, <span class="hljs-type">CarController</span>())
}
</code></pre>
<p>Now try to open your browser and run <code>http://0.0.0.0:8080/api/cars</code> you should now see:</p>
<pre><code class="hljs language-bash">{
	color: <span class="hljs-string">"SpaceGrey"</span>,
	id: null,
	milesDriven: 1000,
	name: <span class="hljs-string">"Fiat"</span>
}
</code></pre>
<p>The id will still be null because the object wasn't instantiated from the db.</p>
<p>### 6. Adding some more magic</p>
<p>Ok cool, let's make our API actually hold some data (remember this will only be held until you have run the project again. Open your <code>CarController.swift</code> file and update the code to this:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> Vapor
<span class="hljs-keyword">import</span> HTTP

<span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">CarController</span>: <span class="hljs-title class_">ResourceRepresentable</span> {

    <span class="hljs-comment">//Adding our session container object</span>
    <span class="hljs-keyword">var</span> cars: [<span class="hljs-type">Car</span>] <span class="hljs-operator">=</span> []

    <span class="hljs-keyword">func</span> <span class="hljs-title function_">index</span>(<span class="hljs-params">request</span>: <span class="hljs-type">Request</span>) <span class="hljs-keyword">throws</span> -> <span class="hljs-type">ResponseRepresentable</span> {

        <span class="hljs-comment">//Update method to return the container object instead of the static object</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> <span class="hljs-type">JSON</span>(node: cars)
    }

    <span class="hljs-comment">//This is where the 'post' request gets redirected to</span>
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">create</span>(<span class="hljs-params">request</span>: <span class="hljs-type">Request</span>) <span class="hljs-keyword">throws</span> -> <span class="hljs-type">ResponseRepresentable</span> {

        <span class="hljs-comment">//Guard statement to make sure we are validating the data correct (we of course should also later guard for the color etc)</span>
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> name <span class="hljs-operator">=</span> request.data[<span class="hljs-string">"name"</span>]<span class="hljs-operator">?</span>.string <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">//Throw a Abort response, I like using the custom status to make sure the frontends have the correct message and response code</span>
            <span class="hljs-keyword">throw</span> <span class="hljs-type">Abort</span>.custom(status: <span class="hljs-type">Status</span>.preconditionFailed, message: <span class="hljs-string">"Missing name"</span>)
        }

        <span class="hljs-comment">//Create a car</span>
        <span class="hljs-keyword">let</span> car <span class="hljs-operator">=</span> <span class="hljs-type">Car</span>(name: name, color: <span class="hljs-string">"Red"</span>, milesDriven: <span class="hljs-number">0</span>)
        <span class="hljs-comment">//Add it to our container object</span>
        cars.append(car)
        <span class="hljs-comment">//Return the newly created car</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> car.converted(to: <span class="hljs-type">JSON</span>.<span class="hljs-keyword">self</span>)
    }

    <span class="hljs-comment">//Add the store: create to tell Vapor that if a 'post' http requst comes in to redirect it there.</span>
    <span class="hljs-keyword">func</span> <span class="hljs-title function_">makeResource</span>() -> <span class="hljs-type">Resource</span>&#x3C;<span class="hljs-type">Car</span>> {
        <span class="hljs-keyword">return</span> <span class="hljs-type">Resource</span>(
            index: index,
            store: create
        )
    }
}
</code></pre>
<p>Now Run your Xcode project again. If you start by opening your browser at our 'get' endpoint <code>http://0.0.0.0:8080/api/cars</code> for the cars then you will of course be able to see that it's returning an empty array.</p>
<p>So let's now try to make a post request to our 'post endpoint' which is basically the same slug <code>http://0.0.0.0:8080/api/cars</code> (you can use whatever API testing tool, I normally use <a href="https://www.getpostman.com/">Postman</a>. If you don't have any tools like that then just use this cURL:</p>
<pre><code class="hljs language-bash">curl -H <span class="hljs-string">"Content-Type: application/json"</span> -X POST -d <span class="hljs-string">'{}'</span> http://0.0.0.0:8080/api/cars
</code></pre>
<p>This should return our error response (response code 412):</p>
<pre><code class="hljs language-bash">{<span class="hljs-string">"error"</span>:<span class="hljs-literal">true</span>,<span class="hljs-string">"message"</span>:<span class="hljs-string">"Missing name"</span>}
</code></pre>
<p>Let's now add our name parameter to the cURL:</p>
<pre><code class="hljs language-bash">curl -H <span class="hljs-string">"Content-Type: application/json"</span> -X POST -d <span class="hljs-string">'{"name":"Fiat"}'</span> http://0.0.0.0:8080/api/cars
</code></pre>
<p>And voila! We have stored our first (only in session object (and should ofc see our stored car object as JSON response). Try running the curl (or postman action) a couple of times with different names, and then run the 'get' in the browser.</p>
<pre><code class="hljs language-bash">[
	{
		color: <span class="hljs-string">"Red"</span>,
		id: null,
		milesDriven: 0,
		name: <span class="hljs-string">"Fiat"</span>
	},
	{
		color: <span class="hljs-string">"Red"</span>,
		id: null,
		milesDriven: 0,
		name: <span class="hljs-string">"Tesla"</span>
	},
	{
		color: <span class="hljs-string">"Red"</span>,
		id: null,
		milesDriven: 0,
		name: <span class="hljs-string">"BMW"</span>
	}
]
</code></pre>
<p>### What's next</p>
<p>So as you could see it's pretty easy to get up running with Vapor. In the next blog post, I will add a MySQL database and show how you CRUD objects. (If you have followed this tutorial then it's only a small amount of lines of code that needs to be added). Sign up to the blog to make sure you get the latest tutorials and tech posts from our engineering team.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/mP7aPSUm7aE">Dmitry Chernyshov</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Adding MySQL to your Vapor project]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2016/11/08/Adding-MySQL-to-your-Vapor-project</link>
            <guid>https://engineering.monstar-lab.com/en/post/2016/11/08/Adding-MySQL-to-your-Vapor-project</guid>
            <pubDate>Tue, 08 Nov 2016 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>In this tutorial we will walk through how to add a database layer to a <a href="http://vapor.codes">Vapor</a> project. We will be using MySQL and will assume that you have set it up on your local machine.</p>
<p>Follow these 3 steps if you don't already have MySQL on your machine.</p>
<ol>
<li>
<p>Install <a href="http://brew.sh/">Homebrew</a>:</p>
<pre><code class="hljs language-bash">/usr/bin/ruby -e <span class="hljs-string">"<span class="hljs-subst">$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)</span>"</span>
</code></pre>
</li>
<li>
<p>Install MySQL:</p>
<pre><code class="hljs language-bash">brew install mysql
</code></pre>
</li>
<li>
<p>Start the MySQL server:</p>
<pre><code class="hljs language-bash">mysql.server start
</code></pre>
</li>
</ol>
<p>More info about MySQL setup <a href="https://dev.mysql.com/doc/refman/5.6/en/osx-installation-pkg.html">here</a></p>
<p>We are continuing on our 'Car' project. You don't necessary have to have read that tutorial if you already are a Vapor rock star. If not then please find it <a href="/en/post/2021-04-30-NestJS-Starter-Kit-Monstar-Lab-Edition">here</a>.</p>
<h3>Adding the MySQL package</h3>
<p>Open your Vapor project and open the <code>Package.swift</code> file and add the extra dependency under the main Vapor dependency:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">import</span> PackageDescription

<span class="hljs-keyword">let</span> package <span class="hljs-operator">=</span> <span class="hljs-type">Package</span>(
    name: <span class="hljs-string">"Drive"</span>,
    dependencies: [
        .<span class="hljs-type">Package</span>(url: <span class="hljs-string">"https://github.com/vapor/vapor.git"</span>, majorVersion: <span class="hljs-number">1</span>, minor: <span class="hljs-number">1</span>),

        <span class="hljs-comment">//Package for MySQL provider for Vapor</span>
        .<span class="hljs-type">Package</span>(url: <span class="hljs-string">"https://github.com/vapor/mysql-provider"</span>, majorVersion: <span class="hljs-number">1</span>, minor: <span class="hljs-number">0</span>)
    ],
    exclude: [
        <span class="hljs-string">"Config"</span>,
        <span class="hljs-string">"Database"</span>,
        <span class="hljs-string">"Localization"</span>,
        <span class="hljs-string">"Public"</span>,
        <span class="hljs-string">"Resources"</span>,
        <span class="hljs-string">"Tests"</span>,
    ]
)
</code></pre>
<p>Now open your terminal (make sure you are cd into the project) and run <code>vapor xcode --mysql</code>, this will make sure everything is compiled correctly with Vapor and linked with MySQL on your local machine.</p>
<p>You should now be able to see some of the new dependencies in the <code>Dependencies</code> folder in your project. (MySQL, VaporMySQL, FluentMySQL etc).</p>
<h3>Setting up the Droplet to support the provider</h3>
<p>Now open your <code>main.swift</code> file and add the following lines of code:</p>
<pre><code class="hljs language-swift"><span class="hljs-operator">...</span>
<span class="hljs-comment">//Importing our new MySQL provider</span>
<span class="hljs-keyword">import</span> VaporMySQL

<span class="hljs-keyword">let</span> drop <span class="hljs-operator">=</span> <span class="hljs-type">Droplet</span>()

<span class="hljs-comment">// Add providers. This tells Vapor that we are using the VaporMySQL provider, this will bind the data to the database and the models automatically down the line</span>
<span class="hljs-keyword">try</span> drop.addProvider(<span class="hljs-type">VaporMySQL</span>.<span class="hljs-type">Provider</span>.<span class="hljs-keyword">self</span>)

<span class="hljs-comment">//Making sure that Vapor runs our migrations / preperations for our model(s)</span>
drop.preparations.append(<span class="hljs-type">Car</span>.<span class="hljs-keyword">self</span>)

<span class="hljs-operator">....</span>

drop.run()
</code></pre>
<p>Now let's jump into our <code>Car.swift</code> file (and down in the extension of the model) add some actual preparation code. Preparation is also know as migrations in other server side coding frameworks. (More info on how Laravel uses it <a href="https://laravel.com/docs/5.3/migrations">here</a>)</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">extension</span> <span class="hljs-title class_">Car</span>: <span class="hljs-title class_">Preparation</span> {
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">prepare</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">database</span>: <span class="hljs-type">Database</span>) <span class="hljs-keyword">throws</span> {

        <span class="hljs-comment">//Adding our actual migration table and attributes. We are first defining the name of the database table and afterwards what attributes the table should have.</span>
        <span class="hljs-keyword">try</span> database.create(<span class="hljs-string">"cars"</span>) { cars <span class="hljs-keyword">in</span>
            cars.id()
            cars.string(<span class="hljs-string">"name"</span>)
            cars.string(<span class="hljs-string">"color"</span>)
            cars.int(<span class="hljs-string">"miles_driven"</span>)
        }
    }

    <span class="hljs-comment">//This makes sure it gets deleted when reverting the projects database</span>
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">revert</span>(<span class="hljs-keyword">_</span> <span class="hljs-params">database</span>: <span class="hljs-type">Database</span>) <span class="hljs-keyword">throws</span> {
        <span class="hljs-keyword">try</span> database.delete(<span class="hljs-string">"cars"</span>)
    }
}
</code></pre>
<p>If you now Run the project then it will compile correctly but crash right after it has started. This is because we are missing a MySQL config file.</p>
<p>So now open terminal again and run:</p>
<p><code>touch Config/mysql.json</code></p>
<p>Open the newly added file and add the following: (Of course depending on your local MySQL settings)</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
  <span class="hljs-attr">"host"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"127.0.0.1"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"user"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"root"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"password"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">""</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"database"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"drive"</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<p>If you try to Run your Xcode project now and get the following:</p>
<pre><code class="hljs language-bash">Failed to start preparation
Can\<span class="hljs-string">'t connect to MySQL server on \'</span>127.0.0.1\<span class="hljs-string">' (61)
</span></code></pre>
<p>Then run <code>mysql.server start</code> in your terminal, this makes sure that you have MySQL running on your local machine. You will see a success message when it's up running. (You can't continue the tutorial before you have this setup)</p>
<p>When you have a MySQL database setup on your local machine, then try to Run the Xcode project. You should then see the following:</p>
<pre><code class="hljs language-bash">Preparing Car
Prepared Car
Database prepared
Server <span class="hljs-string">'default'</span> starting at 0.0.0.0:8080
</code></pre>
<p>The console is telling you that the model Car is getting setup in the database as a table. You will only get this message(s) the first time you Run the project where you don't have the tables setup in the database.</p>
<p>And boom, we are up and running. Now check your database interface if you are using any (I'm using <a href="https://www.sequelpro.com/">SequelPro</a>), you should now be able to see our Car model as a database table (cars). You will also see a table called 'fluent', this is Vapor's way of keeping track of what tables should be created and which already have been created.</p>
<h3>Saving and retrieving our models</h3>
<p>Ok, the fun part starts now. Open your controller <code>CarController.swift</code> and update our <code>create</code> function to this:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">create</span>(<span class="hljs-params">request</span>: <span class="hljs-type">Request</span>) <span class="hljs-keyword">throws</span> -> <span class="hljs-type">ResponseRepresentable</span> {

	<span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> name <span class="hljs-operator">=</span> request.data[<span class="hljs-string">"name"</span>]<span class="hljs-operator">?</span>.string <span class="hljs-keyword">else</span> {
		<span class="hljs-keyword">throw</span> <span class="hljs-type">Abort</span>.custom(status: <span class="hljs-type">Status</span>.preconditionFailed, message: <span class="hljs-string">"Missing name"</span>)
	}

	<span class="hljs-comment">//Create a car object</span>
	<span class="hljs-keyword">var</span> car <span class="hljs-operator">=</span> <span class="hljs-type">Car</span>(name: name, color: <span class="hljs-string">"Red"</span>, milesDriven: <span class="hljs-number">0</span>)

	<span class="hljs-comment">//Asking the car to save itself, the Car model can do that because it's subclassed from Vapors Model</span>
	<span class="hljs-keyword">try</span> car.save()
	<span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> car.converted(to: <span class="hljs-type">JSON</span>.<span class="hljs-keyword">self</span>)

}
</code></pre>
<p>Now try to run the 'post' cURL (or use Postman, still an awesome tool).</p>
<pre><code class="hljs language-bash">curl -H <span class="hljs-string">"Content-Type: application/json"</span> -X POST -d <span class="hljs-string">'{"name":"Fiat"}'</span> http://0.0.0.0:8080/api/cars
</code></pre>
<p>You should now be able to see that your first entry in a database with Vapor as been stored! Congratulations, huge day.</p>
<p>Now let's return our stored data, by updating the index function to the following:</p>
<pre><code class="hljs language-swift"><span class="hljs-keyword">func</span> <span class="hljs-title function_">index</span>(<span class="hljs-params">request</span>: <span class="hljs-type">Request</span>) <span class="hljs-keyword">throws</span> -> <span class="hljs-type">ResponseRepresentable</span> {

	<span class="hljs-comment">//So get all our car objects from the database (Of course we should in the real world add some pagination, sorting, filtering etc), we are chaining our formatter method after our query method, this automatically converts the whole thing into JSON</span>
	<span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> <span class="hljs-type">Car</span>.all().makeNode().converted(to: <span class="hljs-type">JSON</span>.<span class="hljs-keyword">self</span>)
}
</code></pre>
<p>(You can also remove the <code>let cars:[Cars] = []</code> session property if you have that in from earlier tutorial)</p>
<p>Now try to run the 'get' endpoint in your browser (or Postman, or cURL, etc). <code>http://0.0.0.0:8080/api/cars</code></p>
<p>And voila!, you should now see a JSON response of objects from the database.</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">[</span>
  <span class="hljs-punctuation">{</span>
    <span class="hljs-attr">"color"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Red"</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"miles_driven"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Fiat"</span>
  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
  <span class="hljs-punctuation">{</span>
    <span class="hljs-attr">"color"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Red"</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"miles_driven"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"BMW"</span>
  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
  <span class="hljs-punctuation">{</span>
    <span class="hljs-attr">"color"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Red"</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"miles_driven"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Audi"</span>
  <span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">]</span>
</code></pre>
<p>### So what's next?</p>
<p>Well, you now know how to create routes for controllers and work with models, you know how to store and retrieve from the database. Now go explore and maybe get a job at <a href="https://www.nodesagency.com/careers/">Nodes</a> if you think you have the skills and what it takes.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/fPkvU7RDmCo">Caspar Camille Rubin</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Understanding code signing for iOS apps]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2016/10/27/Understanding-code-signing-for-iOS-apps</link>
            <guid>https://engineering.monstar-lab.com/en/post/2016/10/27/Understanding-code-signing-for-iOS-apps</guid>
            <pubDate>Thu, 27 Oct 2016 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>If you're an iOS developer, most likely you've had to deal with code signing. And if you're a junior iOS developer, you might've felt a bit overwhelmed by everything going on in the "Certificates, Identifiers &#x26; Profiles" section of the developer portal.</p>
<p align="center"><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate-values/hbikk_ospmSpNLyW_X9dvSRTF8j.gif"></p>
<p>The goal of this article is to help junior iOS developers understand code signing on a higher level. This will not be a step-by-step tutorial on how to code sign your app. Ideally, after reading this article, you will be able to code sign your app without following any tutorials.</p>
<p>I don't plan to go into lower level details, but we will talk a bit about asymmetric cryptography.</p>
<h3>Asymmetric cryptography</h3>
<p>The minimum you need to know is that asymmetric cryptography uses a <strong>public key</strong> and a <strong>private key</strong>. The users have to keep their private key for themselves, but they can share the public key. And using those public and private keys, a user can prove that he is indeed himself.</p>
<p>A good high-level explanation of asymmetric cryptography can be found <a href="https://blog.vrypan.net/2013/08/28/public-key-cryptography-for-non-geeks/">here</a>. If you want to know implementation details or the math behind this, you can find them online.</p>
<h3>App ID</h3>
<p>The App ID is the unique identifier of your app. It consists of a team id, generated by Apple (you don't have any control over it) and your app's bundle id (<code>com.youcompany.yourapp</code>, for example).</p>
<p>There can also be wildcard App IDs: <code>com.yourcompany.*</code>. Those will match on multiple bundle ids.</p>
<p>Generally, you will have an explicit App ID, not a wildcard one.</p>
<h3>Certificates</h3>
<p>You have probably already noticed that in order to create a certificate in Apple's developer portal, you need to upload a Certificate Signing Request. You can generate this CSR from the Keychain, and this CSR contains a private key.</p>
<p>Then on the developer portal, you can create a certificate using this CSR.</p>
<p>There can be multiple types of certificates. The most common are:</p>
<ul>
<li>Development (iOS App Development) - You need those to run your app on a device from Xcode.</li>
<li>Distribution (App Store and Ad Hoc) - You need those to be able to distribute your app through the App Store or Ad Hoc</li>
<li>APNS (Apple Push Notification Service) - You need those to be able to send push notifications to your app. Unlike Development or Distribution certificates, APNS certificates are associated with an App ID. There can be 2 types of APNS certificates, for development - Apple Push Notification service SSL (Sandbox), and for production - Apple Push Notification service SSL (Sandbox &#x26; Production). You need to create both of them if you want push notifications to work on both debug and distribution builds.</li>
</ul>
<h3>Devices</h3>
<p>You can add up to 100 devices per product family per membership year to your account. 100 iPhones, 100 iPads, 100 iPod Touch, 100 Apple Watches and 100 Apple TVs. To add a device to your account, you need to add its unique device ID. You can easily find that in Xcode, or (a bit more complicated) in iTunes. A detailed guide on how to add devices to your account can be found <a href="https://developer.apple.com/library/content/documentation/IDEs/Conceptual/AppDistributionGuide/MaintainingProfiles/MaintainingProfiles.html#//apple_ref/doc/uid/TP40012582-CH30-SW10">here</a>.</p>
<h3>Provisioning profiles</h3>
<p>The provisioning profile is what associates an App ID with a certificate and, for development or ad hoc distribution, with some devices. You create the provisioning profiles on the Apple developer portal and you download them in Xcode.</p>
<h3>Usage</h3>
<p>After you have created all these, you can then go to Xcode, add your certificates, refresh your provisioning profiles and then select the provisioning profile you want. You can then select the desired signing identity (based on the certificate associated with it) from that provisioning profile.</p>
<h3>F.A.Q.</h3>
<p>Over the years working in iOS development, I've asked and I've been asked many questions about code signing. Here are some of them.</p>
<ul>
<li>
<p><strong>Q</strong>: I have downloaded the provisioning profiles and certificates from the developer portal, but I can't sign the app.<br>
<strong>A</strong>: Yes, because you don't have the private key, the one that was in the certificate signing request. Most likely, another team member created those certificates and provisioning profiles before. You can get the private key from the original developer, revoke the certificate and generate a new one (which will break all provisioning profiles associated with that certificate, but not any App Store apps using those) or create a new one if possible (currently, there's a maximum of 3 distribution certificates allowed per account).</p>
</li>
<li>
<p><strong>Q</strong>: What about the push certificates? I want my app to receive push notifications. Shouldn't I create a provisioning profile that uses the APNS certificate?<br>
<strong>A</strong>: No. When you create an APNS (Apple Push Notification Service) certificate, you associate that with an App ID. So, first you have your CSR, then you create a new APNS certificate with that CSR, download it, open it in Keychain and export it as .p12, which you later upload to your push notification provider. The .p12 file will know that it's associated with that app, and it will send pushes to that app only. That's also a reason why you can't associate an APNS certificate with a wildcard App ID (com.youcompany.*). The push notification server needs to know to which app it sends the notifications.</p>
</li>
<li>
<p><strong>Q</strong>: I bought a new mac, what should I export from the keychain of my old mac for all the code signing to work on the new one?<br>
<strong>A</strong>: You would probably want all your keychain to be exported to the new mac. You can do that by following <a href="https://support.apple.com/kb/PH20120?locale=en_US">these steps</a>. But if you want to export one certificate, make sure you also export its private key. In the Keychain, you have to be able to expand the certificate by pressing on the arrow next to it, and you should see a key. Those certificates are the ones that can be exported as .p12 files. Otherwise, they'll be exported as .cer, without a private key, and they'll be pretty much useless.</p>
</li>
<li>
<p><strong>Q</strong>: My iOS distribution certificate expired, will my app still work?<br>
<strong>A</strong>: When your certificate expires, the provisioning profiles using that certificate will become invalid. On App Store, the app will still work as long as you're enrolled in the development program. All the ad hoc builds signed with that certificate won't work anymore.</p>
</li>
<li>
<p><strong>Q</strong>: My APNS certificate expired, what happens now?<br>
<strong>A</strong>: You won't be able to send push notifications to the app anymore. This can be fixed by creating a new APNS certificate associated with that App ID, downloading it, exporting its .p12 and uploading it to your push notification service provider. No need for an app update.</p>
</li>
</ul>
<h3>Summary</h3>
<p>The key points I want to highlight about code signing are:</p>
<ul>
<li>Each app has an <strong>App ID</strong>.</li>
<li>You need to have the <strong>private keys</strong> for all the certificates you use.</li>
<li>A <strong>debug provisioning profile</strong> associates your development certificate with your App ID and the devices.</li>
<li>An <strong>ad hoc provisioning profile</strong> associates your distribution certificate with your App ID and the devices.</li>
<li>An <strong>app store provisioning profile</strong> associates your distribution certificate with your App ID.</li>
<li>For push notifications, create an <strong>APNS certificate</strong> associated with your App ID, then download it, export it as .p12 and upload the .p12 to your push notification service provider; if you want push notifications both on debug and production builds, you have to create 2 APNS certificates, one for development and one for production.</li>
</ul>
<p>Understanding these will help you understand code signing and save you a lot of time in the end.</p>
<p align="center"><img src="https://cdn-laravel.vapor.cloud/image/nstack/translate-values/success_YGu5HHLDK6_HD2ZEvLVhV.jpg" width="50%">
</p>
<h3>Further reading</h3>
<ul>
<li><a href="https://www.objc.io/issues/17-security/inside-code-signing/">Inside code signing</a></li>
<li><a href="https://developer.apple.com/support/code-signing/">Code Signing</a></li>
<li><a href="https://medium.com/ios-os-x-development/ios-code-signing-provisioning-in-a-nutshell-d5b247760bef">iOS Code Signing &#x26; Provisioning in a Nutshell</a></li>
</ul>
<p><em>Article Photo by <a href="https://unsplash.com/photos/9e9PD9blAto">William Hook</a></em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Insights from NSSpain]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2016/10/01/Insights-from-NSSpain</link>
            <guid>https://engineering.monstar-lab.com/en/post/2016/10/01/Insights-from-NSSpain</guid>
            <pubDate>Sat, 01 Oct 2016 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>We've recently attended NSSpain, a developer conference focusing mostly on iOS. This was our experience, in three different perspectives.</p>
<h2>Jakob Mygind</h2>
<p>The main points that stuck out for me during the NSSpain conference were the increased focus on and need for testable code in App development. The pattern Apple has encouraged is not necessarily the best for writing decoupled, testable code and in turn employing controlled dependency injection. It seems it may be time for taking a different approach in order to attain these traits. Several speakers touched on this subject and had suggestions as to how these goals might be accomplished.</p>
<p>Also an eye-opening talk about app security made clear how in how many ways an app and the networked communication can be tampered with and that it probably is something we need to take steps to remedy.</p>
<p><strong>Favorite Talk</strong><br>
I can’t single out any specific talk as there were a lot of good ones, which put together paint a picture of where iOS development is going, however the ones about Clean Architecture and security are the ones that made the biggest lasting impression.</p>
<p><strong>Favorite Wine</strong><br>
Whichever one it was at lunch. Every day.</p>
<h2>Marius Constantinescu</h2>
<p>What I really enjoy about conferences is the atmosphere. You get to meet so many people that are in the same line of work as you are, and the amount of inspiration you can get is incredible. Probably every other developer attending the conference has had a different experience than you or does things in a different way than you do. You can gain so much just by talking to people.</p>
<p>I'll shortly write about 2 talks that I found interesting.</p>
<p><strong>The Design of Everyday Language Apps</strong> by Natasha Nazari (<a href="https://speakerdeck.com/natashanazari/the-design-of-everyday-language-apps">slides</a>)</p>
<p>A talk about designing a language learning app. The talk was very informative and it drew attention towards some things that developers don't usually give much thought to: making your app usable by people everywhere. A short story she told stuck with me. She was learning Chinese through a language learning app. And although she mastered 2000 Chinese characters, when she got to Taipei she couldn't recognise most of them, because the handwriting used in Taipei different than the font in the language learning app she was using. It's minor things like varying the font in the app which make a big difference in this case. I really enjoyed the talk.</p>
<p><strong>Burnout</strong> by <a href="https://twitter.com/icanzilb">Marin Todorov</a> (<a href="https://medium.com/@marin.todorov/burnout-awareness-at-nsspain-6b852b1222d4">blogpost</a>)</p>
<p>The organisers also prepared a round of lightning talks and I was really impressed by Marin Todorov's. Marin is a successful iOS developer, instructor and book author, probably one of the most appreciated in our community. He gave a talk about burnout. He showed that burnout can happen to everyone, and we need to be better at preventing it and at helping others who are dealing with it. Burnout is more common than we want to admit in our industry, and seeing well-known developers draw attention to it and talk about it will hopefully make us more aware of burnout and help us prevent it for ourselves and for our friends and colleagues.</p>
<h2>Dominik Hadl</h2>
<p>The power of Swift is clearly visible everywhere on the internet, but now it is even more obvious since 90% of talks at conferences contain Swift code examples. What's even more, serious talks about server-side Swift and people using it proves how fast is the language moving forward and gaining attention even from non-iOS developers.</p>
<p>What was interesting to me was that there were also quite a few talks showing various problems or issues without solutions - which seemed like a very good idea both for us to be able to avoid getting into a situation like that or the speakers who could get advice from other developers present in the audience.</p>
<p>Workshop and talk by <a href="https://twitter.com/jdortiz">Jorge Ortiz</a> was especially enlightening and made it very easy to grasp a difficult topic like <strong>Clean Architecture</strong> and proper dependency injection in Swift. We've gained some great ideas and with some modifications we are applying them to our current process and projects.</p>
<p><strong>Karaoke talks</strong> were a unique and extremely entertaining concept which I have never seen at any other conference. Very similar to lightning talks except for one major difference - the speaker did not know nor see his slides beforehand and had to come up with the topic as they were being displayed. This resulted in very funny and non-sensical talks with a bit of geeky developer humour.</p>
<p>And last but not least, it was in Spain - land of tapas, vino and mañana! ☀️🇪🇸</p>
<h2>Summary</h2>
<p>NSSpain was a great conference. What sets it apart from other conferences is that the city it's in, Logroño, is the capital of the Rioja region, a world famous wine region. And the day after NSSpain starts the 1-week long San Mateo wine festival 🍷, where NSSpain participants can join the festivities and enjoy the local products and atmosphere.</p>
<p>Probably the best part about conferences is that when you get back, you just want to do things, try all the stuff you learned, play with new frameworks and SDKs, you feel full of energy, very motivated and very productive. We feel that our team has gained a lot by taking part in NSSpain, and we'll try to keep the practice of attending conferences for the following years.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Continuous Integration With Deployment to TestFlight and HockeyApp]]></title>
            <link>https://engineering.monstar-lab.com/en/post/2016/08/26/Continuous-Integration-on-iOS-with-HockeyApp-and-Testflight-Deployment</link>
            <guid>https://engineering.monstar-lab.com/en/post/2016/08/26/Continuous-Integration-on-iOS-with-HockeyApp-and-Testflight-Deployment</guid>
            <pubDate>Fri, 26 Aug 2016 09:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>We've recently revised our development process on the iOS team and set up a continuous integration server to make and deploy our builds faster, easier and more consistently. This post should shed some light on what we chose and how we set it up.</p>
<h2>The Requirements</h2>
<p>Being an agency doing client work only, distributing test builds internally and to clients has always been a challenge.</p>
<p>For many years, we have exclusively used <a href="http://hockeyapp.com">HockeyApp</a>. As we're juggling an overwhelming amount of client Apple Developer Accounts, it's impractical to gather UDIDs from all of our clients and do new builds every time someone new wants to test an app. This is why we have been using our enterprise account to sign the Hockey builds we make with a <a href="https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/AppDistributionGuide/DistributingEnterpriseProgramApps/DistributingEnterpriseProgramApps.html">wildcard In-House provisioning profile</a>.</p>
<p>The drawback of this approach is that all Apple services like push notifications, iCloud or in-app purchases won't work. In order to get those working, you need to create an app identifier with a dedicated provisioning profile; and if you want to release the app on the App Store, this means that you will have to use a different bundle ID than the live app otherwise Apple won't allow you to create the app identifier.</p>
<p>We've chosen to use a combination of <a href="http://hockeyapp.com">HockeyApp</a> and <a href="https://developer.apple.com/testflight/">Testflight</a> for our beta distribution.</p>
<p>Testflight gives us the most accurate representation of how the app will behave when in production, but the delay between uploading and it being available to test can be pretty big. This is why we opted for a model that uploads to both platforms simultaneously. The client only ever tests on Testflight, but HockeyApp is very handy for quickly making a new build available for internal testing by Q/A and Project Managers.</p>
<p align="center"><img src="https://d32wt9uxwfssua.cloudfront.net/tech-blog/uploads/ci_circle.png"></p>
<p>The main requirements or features of a CI server are usually the same for any platform:</p>
<ul>
<li>Verifying if the project builds</li>
<li>Running unit tests <em>(future requirement as only our frameworks are tested at the moment)</em></li>
<li>Deployment</li>
</ul>
<p>Other reasons are very dependent on what kind of work you do either alone or as a company. Making one app for 5 years and deploying tens or hundreds of different apps every day will result in completely different needs.</p>
<p>Apart from those common requirements, we wanted to setup the CI system so that it keeps our deployment consistent and also save time.
(Who likes to stare at the screen for 15 minutes while Xcode is archiving?)</p>
<p>Most likely, you will run into a discussion about how customizable the system should be. Good thing to keep in mind is, that the more customizable the system will be, the more time-consuming it will be to set up each project. We wanted to be able to use a CI system that would give us quick per project setup with minimum manual work needed and some fair amount of customizability. This, of course results in a longer initial setup time of the actual CI system.</p>
<h2>Options</h2>
<p>An <a href="https://en.wikipedia.org/wiki/Comparison_of_continuous_integration_software">increasing number of CI solutions</a> is becoming available and it feels like there is a new one showing up every day. It makes choosing the right one more difficult and you will usually have to experiment with multiple before you find one that has features you need and actually saves you time.</p>
<p>The current trend seems to be a one-click solution that <em>magically</em> does everything for you. This usually means very low customizability and proprietary source code. We didn't want to go this way because we like to know what is going on under the hood and how it can be improved or augmented. Also, when <strong>magic</strong> breaks, you're in for hours and hours of a total nightmare!</p>
<h2>How did we choose?</h2>
<p><img src="https://d32wt9uxwfssua.cloudfront.net/tech-blog/uploads/jenkins_logo.png" alt="Jenkins"></p>
<p>Since a few of us had experience with <strong>Jenkins</strong> it was an obvious first choice and we got our prototype version running in a few days. Jenkins is the king of customizability and features and the industry standard for large projects with complex configurations.</p>
<p>We were quite happy with our setup, only major issues were the initial setup time for each project and overall <em>scariness</em> of the user interface. <strong>It was important that everybody could customize their project without feeling like they are launching a nuclear missile.</strong></p>
<p><img src="https://d32wt9uxwfssua.cloudfront.net/tech-blog/uploads/gitlab.png" alt="GitLab"></p>
<p>We wanted to choose something that would be easy to understand even for our interns or junior developers and since we recently updated our installation of GitLab to the latest version, we've been intrigued to try <strong>GitLab's Pipelines</strong> which are specifically meant for continuous integration.</p>
<p>Rewriting of some of the scripts from Jenkins was needed, but worth the result. We've achieved an extremely easy project setup (just adding one file to the repo) and it's easy to understand for everyone. For more complex projects we allow overriding the build scripts per project, again by just putting them in the repo.</p>
<p><strong>TL;DR:</strong> GitLab's Pipelines won for us. Jenkins is scary.</p>
<h2>Our Setup</h2>
<h3>Commit Message Trigger</h3>
<p>We figured out that the simplest way of triggering a CI build for us will be some magic words in a commit message. We still need to manually increment build and version numbers of our apps, and that's the perfect commit where to make the CI build the project.</p>
<p>First, we tell the CI where it should deploy the app after building and then the environment the should be set and used when building the app.</p>
<pre><code class="hljs language-scss"><span class="hljs-number">738</span>af0 Fixed stuff <span class="hljs-selector-attr">[ci testflight staging]</span>
</code></pre>
<p>To maintain consistency we always make a HockeApp build if you select a Testflight build, but in case uploading to Testflight fails for some reason we do support <code>testflight_only</code> option, which is especially handy when iTunes Connect API fails as it often does.</p>
<h3>.gitlab.yml (GitLab Pipelines Config)</h3>
<p>Used by Gitlab Pipelines to detect if a build/deploy should be done and what exactly should be done. Very similar to how you would configure Travis.</p>
<p>When run, the YAML file sets project specific environment variables, for example a bundle id or Slack channel name and copies template files, custom ruby scripts and fastlane configuration files into the repository.</p>
<pre><code class="hljs language-sh">stages:
  - build

build_project:
  stage: build
  script:
    - <span class="hljs-built_in">export</span>
      DEVELOPER_DIR=<span class="hljs-string">"/Applications/Xcode.app/Contents/Developer"</span>
      FL_SLACK_CHANNEL=<span class="hljs-string">"ios-ci"</span>
      SIGH_APP_IDENTIFIER=<span class="hljs-string">"com.nodes.app"</span>
      FASTLANE_ITC_TEAM_NAME=<span class="hljs-string">"Nodes ApS"</span>
      FASTLANE_ITC_TEAM_ID=<span class="hljs-string">"123456"</span>
      HOCKEY_APP_ID=<span class="hljs-string">"123456eabfc1234e456a34565e41e35e41"</span>
      MAIN_PLIST_PATH=<span class="hljs-string">"./YourApp/Info.plist"</span>

    - git <span class="hljs-built_in">clone</span> --depth 1 git@yourgitserver.com:ios/ci-template.git
    - ruby ./ci-buildtools/nodes-build/nodes-build.rb

  tags:
    - ios
</code></pre>
<p>Worth noting is that the copying step only happens if those files don't already exist in the repository, this way we can override actual build steps for complex projects by including modified version of those files in the repository.</p>
<p>As the last step, it runs the main Ruby file to start the build.</p>
<h3>Ruby Scripts &#x26; Gems</h3>
<p>We are using one custom script and two gems we made to be able to set up and start a build.</p>
<p><strong><a href="https://github.com/nodes-ios/hockeyver">hockeyver</a></strong> is a small gem that fetches last uploaded version and build from HockeyApp given that you provide an API token and app ID. We use this to be able to fail fast and early as all our Testflight builds have a corresponding build on Hockeyapp so we can check if that version already exists before even starting to build.</p>
<pre><code class="hljs language-bash">$ hockeyver --app_id YOUR_APP_ID -t YOUR_TOKEN
26

<span class="hljs-comment"># Later when used in fastlane</span>
ERROR [2016-08-30 17:12:09.04]: Build failed! Version already exists on hockey.
</code></pre>
<p><strong>carthage_cashier</strong> is another gem which provides an extra layer of caching on top of a dependency manager we use in most projects - Carthage. It caches all the built products, based on their version and also compiler version, so that we can load from cache when we need it, instead of building all dependencies again. This saves us a lot of time and is useful especially for CI builds, where dependencies rarely change.</p>
<pre><code class="hljs language-bash">$ carthage_cashier .
Copied dependencies from cache: [<span class="hljs-string">"Alamofire"</span>, <span class="hljs-string">"Reachability"</span>]
Following dependencies not cached, bootstrapping them now: [<span class="hljs-string">"Serializable"</span>, <span class="hljs-string">"Blobfish"</span>]
...
All dependencies loaded.
</code></pre>
<h3>Fastlane</h3>
<p><a href="https://fastlane.tools/">Fastlane</a> is a newer tool meant for automating various tasks during the development of mobile applications. Think Makefiles on steroids with some nice extra features for iOS. It consists of many different small tools like <code>gym</code>, <code>sigh</code>, <code>spaceship</code> and more that handle all your regular tasks, from (re)signing, deployment to publishing metadata and taking simulator screenshots.</p>
<p>All is configured in one main file, called the <code>fastfile</code> in <strong>lanes</strong>, which are just separate steps which you can execute. What we decided to do was to create a template fastfile where all the lanes and commands are preconfigured, so that it wouldn't require any additional setup from the developer on a project. Of course, we made sure it still is possible to provide your own fastfile in projects which would require additional setup.</p>
<pre><code class="hljs language-bash">$ bundle <span class="hljs-built_in">exec</span> fastlane test_flight api_env:staging changelog:<span class="hljs-string">"Something fixed."</span>
DEBUG [2016-08-21 16:32:29.23]: Using api_env: staging
DEBUG [2016-08-21 16:32:29.23]: Using changelog: Something fixed.
....
INFO [2016-08-21 16:33:55.01]: Starting with ipa upload to HockeyApp... this could take some time.
....
INFO [2016-08-21 16:44:17.10]: fastlane.tools just saved you 12 minutes! 🎉
</code></pre>
<h2>Summary</h2>
<p>Continuous integration is awesome and you should be using it. After all, it not only makes it easy, fast and consistent to make builds, but also gives you a point on the <a href="http://www.joelonsoftware.com/articles/fog0000000043.html">Joel Test</a>!</p>
<p align="center"><img src="https://d32wt9uxwfssua.cloudfront.net/tech-blog/uploads/caveman.jpg"></p>
<p>If you can't afford an in-house computer to set up as a build server or you prefer an all-in-one solution in the cloud, then your best bet would be <a href="https://buddybuild.com/">BuddyBuild</a>, which offers a tonne of features and has great support where you can communicate directly with the developers!</p>
<p>On the other hand, if you are producing many apps each with specific requirements you can't always use a simple solution like that. There might also be legal reasons and non-disclosure agreements preventing you from having an external server clone your repository. Or you just prefer to have your own dedicated servers in your own office, which start builds immediately and give you full control.</p>
<p>We are still iterating and improving our current setup, but hopefully, this article has at least made you think about having your own CI up and running.</p>
<p><em>Article Photo by <a href="https://unsplash.com/photos/Ra8x8H7GToE">Alexandru Acea</a></em></p>]]></content:encoded>
        </item>
    </channel>
</rss>