<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Anadea</title>
    <description>The latest articles on DEV Community by Anadea (@anadea).</description>
    <link>https://dev.to/anadea</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F140133%2F012ef1d5-bcc0-49a6-90e9-013ae6d5ff7b.jpg</url>
      <title>DEV Community: Anadea</title>
      <link>https://dev.to/anadea</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/anadea"/>
    <language>en</language>
    <item>
      <title>pre-commit-config-shellcheck for syntax and semantic checks automation</title>
      <dc:creator>Anadea</dc:creator>
      <pubDate>Wed, 21 Sep 2022 10:10:52 +0000</pubDate>
      <link>https://dev.to/anadea/pre-commit-config-shellcheck-for-syntax-and-semantic-checks-automation-460e</link>
      <guid>https://dev.to/anadea/pre-commit-config-shellcheck-for-syntax-and-semantic-checks-automation-460e</guid>
      <description>&lt;h2&gt;
  
  
  The reasoning
&lt;/h2&gt;

&lt;p&gt;Second-checking of an already written code may be an exhausting and unreliable task for most of the programmers involved in &lt;a href="https://anadea.info/services/custom-software-development"&gt;custom software development&lt;/a&gt;. While refactoring the code a few days ago, we found some mistakes in shell code in the pre-commit config file. These errors have been there for a long time without any warnings. That's why we thought if these code checks either were more reliable or proceeded automatically. Thereafter, &lt;a href="https://github.com/Anadea/pre-commit-config-shellcheck"&gt;pre-commit-config-shellcheck&lt;/a&gt; was created for this very purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s the difference
&lt;/h2&gt;

&lt;p&gt;This tool will automatically detect and help you to resolve mistakes in your project’s &lt;em&gt;.pre-commit-config.yaml&lt;/em&gt; file. All the syntax and most semantic checks are done for you. Also, you will receive bug fixing recommendations. This is done by automatic check of all the shell code entries in the config file with the other tool—&lt;em&gt;Shellcheck&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Shellcheck
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/koalaman/shellcheck"&gt;Shellcheck&lt;/a&gt; is a shell script static analysis tool. The main purpose of this tool is to find popular syntax mistakes as well as to point out corner cases that may cause scripts to fail under future circumstances while giving suggestions for fixing those.&lt;/p&gt;

&lt;h3&gt;
  
  
  File checking
&lt;/h3&gt;

&lt;p&gt;All the checks of this tool are done on the &lt;em&gt;.pre-commit-config.yaml&lt;/em&gt; file, but this file can’t be directly analyzed by Shellcheck due to the structure of the file, which includes not only shell code but other configuration points. We found the workaround for this in creating a list of temporary files with found shell entry points and checking them separately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Subprocesses
&lt;/h3&gt;

&lt;p&gt;The main way of using Shellcheck is through a terminal, and this tool doesn’t have an adaptor for different programming languages, so we use the Python subprocess module in order to perform checks on each temporary file. Then the output from subprocesses is collected and the tool forms a single pre-commit-config-shellcheck output with the list of warnings and their locations.&lt;/p&gt;

&lt;p&gt;Example of usage and output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;; pre_commit_config_shellcheck.py .pre-commit-config.yaml                                                                                                                                                                               

In entry "seed-isort-config" on line 9:                                                                                                                                                                                                 
var = 42                                                                                                                                                                                                                                
    ^-- SC2283 (error): Remove spaces around = to assign (or use [ ] to compare, or quote '=' if literal).                                                                                                                              


In entry "seed-isort-config" on line 10:                                                                                                                                                                                                
echo '$var'                                                                                                                                                                                                                             
     ^----^ SC2016 (info): Expressions don't expand in single quotes, use double quotes for that.                                                                                                                                       

For more information:                                                                                                                                                                                                                   
  https://www.shellcheck.net/wiki/SC2283 -- Remove spaces around = to assign ...                                                                                                                                                        
  https://www.shellcheck.net/wiki/SC2016 -- Expressions don't expand in singl...                                                                                                                                                        

In entry "removestar" on line 19:                                                                                                                                                                                                       
removestar -i ${NAME}                                                                                                                                                                                                                   
              ^-----^ SC2086 (info): Double quote to prevent globbing and word splitting.                                                                                                                                               

Did you mean:                                                                                                                                                                                                                           
removestar -i "${NAME}"                                                                                                                                                                                                                 

For more information:                                                                                                                                                                                                                   
  https://www.shellcheck.net/wiki/SC2086 -- Double quote to prevent globbing ...    
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Easy to use
&lt;/h3&gt;

&lt;p&gt;For the sake of keeping this tool simple for users, there are a few ways to use pre-commit-config-shellcheck:&lt;/p&gt;

&lt;h4&gt;
  
  
  As a command-line tool
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pip install pre-commit-config-shellcheck
$ pre_commit_config_shellcheck.py .pre-commit-config.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  As the &lt;a href="https://pre-commit.com/"&gt;pre-commit&lt;/a&gt; hook
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .pre-commit-config.yaml
- repo: "https://github.com/Anadea/pre-commit-config-shellcheck"
  rev: "0.3.4"
  hooks:
    - id: "pre-commit-config-shellcheck"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  And also as a &lt;a href="https://github.com/features/actions/"&gt;GitHub action&lt;/a&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- name: "pre-commit-config-shellcheck"
  uses: "action/pre-commit-config-shellcheck@0.3.4"
  id: "pre-commit-config-shellcheck"
  with:
    config: ".pre-commit-config.yaml"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Pre-commit-config-shellcheck was successfully created, tested and deployed for third-party usage according to the licensing. This tool, additional information and licensing can be found on &lt;a href="https://github.com/Anadea/pre-commit-config-shellcheck"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>python</category>
      <category>shell</category>
      <category>precommit</category>
      <category>automation</category>
    </item>
    <item>
      <title>Our experience with Three.js library</title>
      <dc:creator>Anadea</dc:creator>
      <pubDate>Fri, 16 Sep 2022 13:17:50 +0000</pubDate>
      <link>https://dev.to/anadea/our-experience-with-threejs-library-36dd</link>
      <guid>https://dev.to/anadea/our-experience-with-threejs-library-36dd</guid>
      <description>&lt;p&gt;In the flow of everyday tasks from customers, we constantly had to face mechanisms for working with complex animations or with complex graphic designs. As one of the possible ways to solve such problems, we chose to use the &lt;a href="https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene"&gt;Three.js library&lt;/a&gt;, which has quite a wide range of configurational and practical usage.&lt;/p&gt;

&lt;p&gt;Below, you will find the description of our experience and some experiments in using this library, along with the main features and the paths to reach the goals set with code examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part I—Introduction
&lt;/h2&gt;

&lt;p&gt;Since most of our &lt;a href="https://anadea.info/services/web-development/front-end"&gt;Front end development&lt;/a&gt; projects are written in React, it was appropriate to experiment in the React space. But when we tried to integrate the code into the React component, we encountered many inconveniences caused by the specifics of Three.js and React working in tandem.&lt;/p&gt;

&lt;p&gt;Yes, all of these incompatibilities could be bypassed if the code was played around with well, given the specifics of React. But why should we waste time on something that has been investigated before? So instead of the native Three.js, we advise to use a library which was developed specifically for React applications, &lt;strong&gt;&lt;a href="https://docs.pmnd.rs/react-three-fiber/getting-started/introduction"&gt;React-three-fiber&lt;/a&gt;&lt;/strong&gt;. It is installed via npm and has a clear documentation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm install three @react-three/fiber
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;There is only one drawback, or rather a nuance in it: the library works only with React version 18 or higher.&lt;/p&gt;

&lt;p&gt;Despite the convenience of React-three-fiber, we still recommend you start exploring the 3D world with native Three.js. That’s because to navigate well in 3D and the possibilities of the library, first, you must learn the main concepts of the library: scene, camera, mesh, which consists of its geometry and material and lighting (ambient or point lights). You should understand how the XYZ axes are located and how to navigate the Three.js.&lt;/p&gt;
&lt;h2&gt;
  
  
  Part II—Loading 3D models
&lt;/h2&gt;

&lt;p&gt;The library is most frequently used for uploading 3D models from designers of the page. Just uploading, showing and lighting isn’t so complicated and doesn’t require a lot of effort from the developer. You are able to add animation for the model, such as rotation on its axis. Or you can add a possibility to interact with the model via OrbitControls.&lt;/p&gt;

&lt;p&gt;Our designer has drawn the company logo in Blender and had it in 3 formats: glb, fbx, obj. All of these formats are supported by &lt;strong&gt;Three.js&lt;/strong&gt; and &lt;strong&gt;React-three-fiber&lt;/strong&gt;.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.youtube.com/watch?list=TLGGQ6B9XrRZoSwxNjA5MjAyMg&amp;amp;v=q1xsB3w1EZI&amp;amp;feature=youtu.be" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--3X2DRhQG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.ytimg.com/vi/q1xsB3w1EZI/maxresdefault.jpg" height="495" class="m-0" width="880"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.youtube.com/watch?list=TLGGQ6B9XrRZoSwxNjA5MjAyMg&amp;amp;v=q1xsB3w1EZI&amp;amp;feature=youtu.be" rel="noopener noreferrer" class="c-link"&gt;
          3D logo - YouTube
        &lt;/a&gt;
      &lt;/h2&gt;
        
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--g5T16SA0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.youtube.com/s/desktop/43848079/img/favicon.ico" width="16" height="16"&gt;
        youtube.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;The latter format gives us the possibility to upload the model without material (just a white) and color it using JavaScript. This way, you can make your model more interactive. It is possible to change the texture, make it more rough or metallic.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.youtube.com/watch?list=TLGGMAcAZkMikNIxNjA5MjAyMg&amp;amp;v=Pr5iNqQwHns&amp;amp;feature=youtu.be" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--V4C6Iyxd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.ytimg.com/vi/Pr5iNqQwHns/maxresdefault.jpg" height="495" class="m-0" width="880"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.youtube.com/watch?list=TLGGMAcAZkMikNIxNjA5MjAyMg&amp;amp;v=Pr5iNqQwHns&amp;amp;feature=youtu.be" rel="noopener noreferrer" class="c-link"&gt;
          Color options - YouTube
        &lt;/a&gt;
      &lt;/h2&gt;
        
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--g5T16SA0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.youtube.com/s/desktop/43848079/img/favicon.ico" width="16" height="16"&gt;
        youtube.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Part III—3D models in JavaScript
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;This part is about creating a 3D model on the example of animated Earth.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Creating a 3D model using only code will require more effort and time than just uploading one. You need to have a basic knowledge of the library’s opportunities, geometry and material species, and a good 3D orientation. Certainly, you can find a lot of videos or articles about how it’s best to create one or another training model and how to interact with textures and animation. Having brought all parts together (knowledge, ideas and comments from the project team), we’ve created something interesting:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.youtube.com/watch?list=TLGGxAypMDVPDGcxNjA5MjAyMg&amp;amp;v=5h_iS4cSwow&amp;amp;feature=youtu.be" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--I7a_J875--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.ytimg.com/vi/5h_iS4cSwow/maxresdefault.jpg" height="495" class="m-0" width="880"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.youtube.com/watch?list=TLGGxAypMDVPDGcxNjA5MjAyMg&amp;amp;v=5h_iS4cSwow&amp;amp;feature=youtu.be" rel="noopener noreferrer" class="c-link"&gt;
          Earth and moon - YouTube
        &lt;/a&gt;
      &lt;/h2&gt;
        
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--g5T16SA0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.youtube.com/s/desktop/43848079/img/favicon.ico" width="16" height="16"&gt;
        youtube.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;You can end up in outer space if you add to the code.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.youtube.com/watch?list=TLGGMtxOW-y28O8xNjA5MjAyMg&amp;amp;v=s0JYLwVNIVs&amp;amp;feature=youtu.be" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--3_7GU9fT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.ytimg.com/vi/s0JYLwVNIVs/maxresdefault.jpg" height="495" class="m-0" width="880"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.youtube.com/watch?list=TLGGMtxOW-y28O8xNjA5MjAyMg&amp;amp;v=s0JYLwVNIVs&amp;amp;feature=youtu.be" rel="noopener noreferrer" class="c-link"&gt;
          Earth and moon - perspective - YouTube
        &lt;/a&gt;
      &lt;/h2&gt;
        
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--g5T16SA0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.youtube.com/s/desktop/43848079/img/favicon.ico" width="16" height="16"&gt;
        youtube.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;As we can see, the geometry is rather simple - it’s a sphere. However, the materials are more complex. Textures (special images) of Earth and Moon were used here. These are easily found on the Internet. You can use with Moon texture (moonTexture) or use &lt;a href="https://threejs.org/docs/#api/en/materials/ShaderMaterial"&gt;Shaders&lt;/a&gt; to get complex texture, for example, atmosphere imitation. This aspect is tricky enough and should be approached individually, but you can find a lot of examples of code that can be adapted to your needs. That’s what we did.&lt;/p&gt;

&lt;p&gt;As for animation, the Earth’s axis-turning was the easiest part. You should only set the axis and rotation speed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;useFrame(() =&amp;gt; {
  ref.current.rotation.y += 0.002;
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;By the way, custom hooks from the React-three-fiber library like &lt;strong&gt;useFrame&lt;/strong&gt; and &lt;strong&gt;useLoader&lt;/strong&gt; are irreplaceable hooks for comfortable work with 3D in React apps. &lt;strong&gt;useFrame&lt;/strong&gt; is used to work with animation; &lt;strong&gt;useLoader&lt;/strong&gt; is used for uploading 3D models, maps, textures and vertices.&lt;/p&gt;

&lt;p&gt;Rotating the Moon around the Earth by the circle trajectory was a more complex task. Here, you could use some math.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;useFrame(() =&amp;gt; {
  // circle trajectory
  let date = Date.now() * 0.0005 + 1;
  ref.current.position.set(
    Math.cos(date) * 2 + 0, 
    0, 
    Math.sin(date) * 2 + 0);
  // rotation
  ref.current.rotation.y += 0.004;
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;em&gt;date&lt;/em&gt; sets sequential coefficients for changing coordinate values, and adjustment of the 0.0005 number can increase or slow down the rotation speed.&lt;/p&gt;

&lt;p&gt;I set the Moon’s position x-y-z below, where y stays equal to zero because I want the trajectory to be horizontal, in the X plane. However, you can set &lt;em&gt;y = Math.sin(date) * 2 + 0&lt;/em&gt; and get the elliptical trajectory inclined at 45 degrees.&lt;/p&gt;

&lt;p&gt;By the way, 0 in the end is the pivot coordinate, around which the mesh is rotating. We have a pivot at the Earth sphere (position 0-0-0). So, we can get different results by changing one or another parameter.&lt;/p&gt;

&lt;p&gt;Stars are also moving according to the set direction. We’ll provide more details on it down the road.&lt;/p&gt;

&lt;p&gt;The most challenging task was setting real coordinates by using latitudes and longitudes of specific places on Earth. Out of curiosity, we chose coordinates of a real flight around the world:&lt;/p&gt;

&lt;p&gt;Kyiv-Dubai-Manila-Osaka-Honolulu-Seattle-London-Lviv&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const pinsCoordinates = [
  { lat: 50.450001, lng: 30.523333 }, //Kyiv
  { lat: 25.276987, lng: 55.296249 }, //Dubai
  { lat: 14.599512, lng: 120.984222 }, //Manila
  { lat: 34.672314, lng: 135.484802 }, //Osaka
  { lat: 21.315603, lng: -157.858093 }, //Honolulu
  { lat: 47.608013, lng: -122.335167 }, //Seattle
  { lat: 51.509865, lng: -0.118092 }, //London
  { lat: 49.842957, lng: 24.031111 }, //Lviv
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Meanwhile, xyz-coordinates should alter depending on the sphere sizes.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* Getting coordinates x,y,z on the sphere by latitude and longitude.
   Sphere scale should be '1' */
function getCoordinates(lat, lng) {
  // convert latitude and longitude to Phi and Theta
  const Phi = (90 - lat) * (Math.PI / 180);
  const Theta = (lng + 180) * (Math.PI / 180);
  // r = radius of SphereGeometry (should be 1 to be better)
  // x = -r * (sin(Phi) * cos(Theta))
  // y = cos(Phi)
  // z = sin(Phi) * sin(Theta)
  const x = -(Math.sin(Phi) * Math.cos(Theta));
  const y = Math.cos(Phi);
  const z = Math.sin(Phi) * Math.sin(Theta);

  return { x, y, z };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;As you can see, the function takes lat-lng pare and returns xyz coordinates for the subsequent pin on the sphere. By experimenting with the size and scale of the sphere, we discovered that to get the most precise coordinates, we better use radius and scale of 1 and adjust the initial sphere’s size by changing the distance from the Camera.&lt;/p&gt;

&lt;p&gt;To get the imitation that all pins are a whole and rotate along with the Earth, you should group them using the tag. We can see the result once we remove the Earth. Adding the same rotation trajectory like that of the Earth complemented the imitation.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.youtube.com/watch?list=TLGGAYXK_cMntT0xNjA5MjAyMg&amp;amp;v=iORKqvBIhYA&amp;amp;feature=youtu.be" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--E-u4CS9a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.ytimg.com/vi/iORKqvBIhYA/maxresdefault.jpg" height="495" class="m-0" width="880"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.youtube.com/watch?list=TLGGAYXK_cMntT0xNjA5MjAyMg&amp;amp;v=iORKqvBIhYA&amp;amp;feature=youtu.be" rel="noopener noreferrer" class="c-link"&gt;
          Pins; no Earth - YouTube
        &lt;/a&gt;
      &lt;/h2&gt;
        
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--g5T16SA0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.youtube.com/s/desktop/43848079/img/favicon.ico" width="16" height="16"&gt;
        youtube.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Below, see the example of the Pin’s component code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const pinsXYZCoordinates = [];
pinsCoordinates.forEach((pin) =&amp;gt; {
  const pinXYZ = getCoordinates(pin.lat, pin.lng);
  pinsXYZCoordinates.push(pinXYZ);
});

function Pins() {
  const ref = useRef();

  useFrame(() =&amp;gt; {
    ref.current.rotation.y += 0.002;
  });

  return (
    &amp;lt;&amp;gt;
      &amp;lt;group ref={ref}&amp;gt;
        {pinsXYZCoordinates.map((pin) =&amp;gt; {
          return (
            &amp;lt;mesh key={pin.x} position={[pin.x, pin.y, pin.z]}&amp;gt;
              &amp;lt;sphereGeometry args={[0.02, 30, 30]}&amp;gt;&amp;lt;/sphereGeometry&amp;gt;
              &amp;lt;meshStandardMaterial color={0xdc296c}&amp;gt;&amp;lt;/meshStandardMaterial&amp;gt;
            &amp;lt;/mesh&amp;gt;
          );
        })}
      &amp;lt;/group&amp;gt;
    &amp;lt;/&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here, pins are small spheres of standard material and color. But you are free to use more complex geometries or upload your own 3D models at all.&lt;/p&gt;
&lt;h2&gt;
  
  
  Part IV—Complex animation along a given trajectory
&lt;/h2&gt;

&lt;p&gt;Continuing the topic of animation, we managed to get a certain shape from random positioning points &lt;strong&gt;on the example of an outline of a country&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We haven’t found any service that draws the outlines of shapes, so we drew the shape of the country ourselves. It consists of 120 2D vectors Vector2(x,y) and is then divided into a given number of points. For clarity, we’ve left the static shape on the left.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.youtube.com/watch?list=TLGGmkbCzARVKesxNjA5MjAyMg&amp;amp;v=l_ZK-y0JlxQ&amp;amp;feature=youtu.be" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--fF0Ff7od--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.ytimg.com/vi/l_ZK-y0JlxQ/maxresdefault.jpg" height="495" class="m-0" width="880"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.youtube.com/watch?list=TLGGmkbCzARVKesxNjA5MjAyMg&amp;amp;v=l_ZK-y0JlxQ&amp;amp;feature=youtu.be" rel="noopener noreferrer" class="c-link"&gt;
          Country outlines - YouTube
        &lt;/a&gt;
      &lt;/h2&gt;
        
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--g5T16SA0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.youtube.com/s/desktop/43848079/img/favicon.ico" width="16" height="16"&gt;
        youtube.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;To make such an animation, we need to get the initial and the final 3D coordinates of each point. As the shape consists of 2D vectors, we transformed the point’s scope to an array of positions of each point - [[1.34, 2.34, 1.5], [1.62, 2.28, 1.5], . . . ].&lt;/p&gt;

&lt;p&gt;The random coordinates were obtained quite easily, and the stars from Part III were distributed in the same way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// creates 500 random coordinates for star-points 
const starVertices = [];
for (let i = 0; i &amp;lt; 500; i++) {
  const x = (Math.random() - 0.5) * 30;
  const y = (Math.random() - 0.5) * 20;
  const z = -Math.random() * 10;
  starVertices.push(x, y, z)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, all points are separate Meshes (Components) which were added to the scene using Array.map(). This enables us to animate each point separately by a single line of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vector = new THREE.Vector3(x, y, z) // set the direction of mesh movement

useFrame(() =&amp;gt; {
  // 0.008 - part of vector's path wich mesh goes by one frame
  ref.current.position.lerp(vector, 0.008)
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, this method can be used to move Mesh from one point to another.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part V—Conclusion
&lt;/h2&gt;

&lt;p&gt;The examples of using the &lt;strong&gt;Three.js&lt;/strong&gt; library above are just a drop in the ocean of the library's capabilities. In this case, the official documentation won’t give a full understanding of 3D. We watched a lot of video tutorials, read a lot of articles on the Internet and made a lot of mistakes on the way to achieving the desired result. Another complication is that many examples are only applied to Three.js. Therefore, it was necessary to adjust the solution to React-three-fiber.&lt;/p&gt;

&lt;p&gt;Overall, the library is very cool and 100% worth your attention.&lt;/p&gt;

</description>
      <category>threejs</category>
      <category>3d</category>
    </item>
    <item>
      <title>MetricsCollector for Ruby on Rails</title>
      <dc:creator>Anadea</dc:creator>
      <pubDate>Mon, 12 Sep 2022 14:23:40 +0000</pubDate>
      <link>https://dev.to/anadea/metricscollector-for-ruby-on-rails-2mjl</link>
      <guid>https://dev.to/anadea/metricscollector-for-ruby-on-rails-2mjl</guid>
      <description>&lt;p&gt;From one point, we've started collecting some metrics from our projects in Ruby on Rails so that we could see the dynamic and statistics, because some problems catch your attention only when you can see them right in front of your eyes.&lt;/p&gt;

&lt;p&gt;Above mentioned metrics included &lt;strong&gt;percent of test coverage&lt;/strong&gt; provided by &lt;em&gt;simplecov&lt;/em&gt;. With it, we could react in case of sharp decline because we were always trying to have at least 80% code to be covered with tests. Second metric we decided to look into were &lt;strong&gt;vulnerabilities&lt;/strong&gt;, warnings and deprecations from the brakeman. That’s how we wouldn't miss the gaps in our projects. Furthermore, to follow best practices, score from rubycritic was also included.&lt;/p&gt;

&lt;p&gt;And the last one is just a simple amount of code lines and files provided by the &lt;em&gt;CLOC&lt;/em&gt; library. So that any implementation of big features would be visible in statistics.&lt;/p&gt;

&lt;p&gt;As a result, we've understood that we are doing repetitive actions in different projects and with different frequency. And to avoid that, we have decided to automate the process of collecting all desired metrics so that a developer wouldn't waste time on this. Before, in order to collect metrics, they had to launch project, pull all updates, run all tests to get percent of test coverage, run the script that collects all metrics and then put all results into Google Documents that we kept for each project.&lt;/p&gt;

&lt;p&gt;With a list of steps wrapped in MetricsCollector, we were able to create a job in the pipeline (in our case it runs on GitLab). The job creates artifacts for each gem presented in our tool, collects all results in a certain structure and moreover, it sends them into our messaging apps. On top of that, it sends the results into appropriate documents to google spreadsheets.&lt;/p&gt;

&lt;p&gt;To use MetricsCollector, next should be done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bundle exec metrics_collector
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, it collects metrics from output of all included gems, generates results in json, csv file formats and besides that, shows results in the console to make it visible in pipeline:&lt;/p&gt;

&lt;p&gt;Cloc total lines: 25000&lt;br&gt;
Cloc total files: 600&lt;br&gt;
Brakeman errors: 1&lt;br&gt;
Brakeman warnings: 2&lt;br&gt;
Brakeman ignored warnings: 1&lt;br&gt;
Coverage: 82.44&lt;br&gt;
Rubycritic score: 66.08&lt;/p&gt;

&lt;p&gt;By including MetricsCollector in pipeline as a separate job, we were able to easily check metrics after every minor/major update of the project:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--U9mshj8n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/2983/MetricsCollector_1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U9mshj8n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/2983/MetricsCollector_1.png" alt="metrics collector 1" width="383" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We've found it's also a nice addition to make it possible to download desired documents from artifacts:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kPq-PbHQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/2984/MetricsCollector_2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kPq-PbHQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/2984/MetricsCollector_2.png" alt="metrics collector ror" width="250" height="173"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But it's also not really convenient to get outputs from artifacts all the time, so we have integrated Slack into our tool. Since then, the pipelines in our projects send outputs right to our Slack channels of the corresponding projects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gyQwJP8K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/2991/MetricsCollector_3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gyQwJP8K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/2991/MetricsCollector_3.jpg" alt="metricscollector" width="872" height="623"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both files (csv, json) and text variants are sent at the same time just for convenience.&lt;/p&gt;

&lt;p&gt;However, we thought that it would take ages to track any statistics right from Slack, so we have implemented integration with Google Spreadsheets. This made it really convenient to check for the whole history of collected metrics from one place.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1kV9iw07--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/2989/MetricsCollector_4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1kV9iw07--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/2989/MetricsCollector_4.png" alt="ruby on rails metrics collection" width="880" height="274"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s worth mentioning that it can upload metrics only to the first worksheet for now.&lt;/p&gt;

&lt;p&gt;We’ve used spreadsheet’s official Ruby client so there is not that much of logic for populating worksheet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Initialize SpreadSheet service
@service = Google::Apis::SheetsV4::SheetsService.new
# Send metrics to the next not populated line in worksheet
@service.append_spreadsheet_value(@spreadsheet,'1:1', @request_body, value_input_option: 'USER_ENTERED' )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we plan to expand the gem in future, we have encapsulated gem handlers and file generators from business logic, so we won’t have to update old logic and could focus on implementing new sorts of metrics/output options.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7DPKCnl2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/2988/MetricsCollector_5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7DPKCnl2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/2988/MetricsCollector_5.png" alt="ror metrics collector" width="464" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a result, we have automated repetitive actions, saved a lot of time from collecting all metrics manually and made the tool easily expandable.&lt;/p&gt;

&lt;p&gt;That solution suits our needs perfectly, we will keep maintaining the tool in the future. It's open source so you can check the project in the &lt;a href="https://github.com/Anadea/metrics_collector"&gt;GitHub repository&lt;/a&gt; of a &lt;a href="https://anadea.info/services/custom-software-development"&gt;custom software development company&lt;/a&gt; Anadea.&lt;/p&gt;

</description>
      <category>ror</category>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>How to Verify Users' Passwords Using the Android KeyStore</title>
      <dc:creator>Anadea</dc:creator>
      <pubDate>Tue, 08 Jan 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/anadea/how-to-verify-users-passwords-using-the-android-keystore-p3k</link>
      <guid>https://dev.to/anadea/how-to-verify-users-passwords-using-the-android-keystore-p3k</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YJT0QmHq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/1991/Security_android.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YJT0QmHq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/1991/Security_android.jpg" alt="Android security" width="750" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everybody knows that security is a very important issue for users and therefore developers should take a proper care of it. There are many posts about Android security in the Internet - some them explain difference between cryptography algorithms, others are dedicated to basics of Android security. In this post I will spotlight a single use case Android developers may face in their work.&lt;/p&gt;

&lt;p&gt;Let's assume that we develop an application and we need to identify users - I am talking about a regular login screen. We are developing a standalone application and so we cannot rely on backend user validation capability as we usually do. Of course, we need a secure authorization flow. &lt;/p&gt;


&lt;center&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zGE_dvd8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/1993/Knock_Knock.jpg" alt="Knock Knock" width="200" height="258"&gt;&lt;/center&gt;


&lt;center&gt;Image source: &lt;a href="https://en.wikipedia.org/wiki/Knock_Knock_(play)" rel="nofollow"&gt;Knock Knock&lt;/a&gt;
&lt;/center&gt;

&lt;p&gt;Saving users' passwords is a bad practice and none of developers want to be responsible for compromising users' credentials. The point is that many users are likely to use the same password for many services. As a result, a compromised password in our application may be a key to many other services.&lt;/p&gt;
&lt;h2&gt;
  
  
  Resolving a security task in Android SDK
&lt;/h2&gt;

&lt;p&gt;Let's try to find out what we can use to resolve a security task in Android SDK. The first tool that springs in our mind is the KeyStore API introduced in Android 4.3. We can use the KeyStore as a container to store cryptographic keys. The most significant benefit of the KeyStore is that it has access to Android System Keystore and it is maintained by a system process. In this way, we delegate keeping cryptographic keys to a system service. &lt;/p&gt;

&lt;p&gt;What we are going to do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate a key pair for asynchronous encryption and put it in the KeyStore.&lt;/li&gt;
&lt;li&gt;Sign user password with Digital Signature. The Signature procedure will be initiated with the key from step 1.&lt;/li&gt;
&lt;li&gt;Verify another user password with the Signature from step 2.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Stop talking and let's start coding!
&lt;/h3&gt;

&lt;p&gt;First, we need to create a key pair with RSA specific parameters. To do this, we should start from creating parameters for RSA key, however we should do it a bit differently for Android M and above. &lt;/p&gt;

&lt;p&gt;This is how we obtain specifications for Android SDK below M version:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And this chunk of code for Android version M and above:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Besides RSA specific parameters, we should know the following: &lt;strong&gt;"AndroidKeyStore"&lt;/strong&gt; will be used to obtain the key pair from KeyStore and Certificate related to it. The key pair is going to be valid from 'start' date to 'end' date.&lt;/p&gt;

&lt;p&gt;Having RSA specifications we can generate a KeyPair:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;KeyPairGenerator is used to generate the KeyPair and add it to the Android KeyStore. We get an instance of KeyPairGenerator for RSA algorithm and pass &lt;strong&gt;"AndroidKeyStore"&lt;/strong&gt; parameter to ensure to use Android System Keystore. Then, we initialize KeyPairGenerator with specifications we set before and eventually generateKeyPair method generates KeyPair and puts it into Android System Keystore.&lt;/p&gt;

&lt;p&gt;New KeyPair will be generated every time we call 'generateKeyPair()' method. That's why, before generating another KeyPair, we should make sure that AndroidKeyStore doesn't already have a KeyPair with the same alias to prevent overriding last KeyPair and losing existing certificates.&lt;/p&gt;

&lt;p&gt;We have the RSA KeyPair generated and ready to use it. Note, that we will use only private key for our purpose. Let's start using our AndroidKeyStore!&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;First, we obtain an instance of the KeyStore and pass &lt;strong&gt;"AndroidKeyStore"&lt;/strong&gt; parameter to ensure to use Android System Keystore. Then we should finish initialization of the KeyStore with "load()" method. As soon as we have Android System Keystore, we can get the keys we generated before with &lt;code&gt;ALIAS_VERIFY_SIGNATURE&lt;/code&gt; and cast the keys to the required type.&lt;/p&gt;

&lt;p&gt;And now, let's suppose that a new user is created within our application and we have password in plain text. Do you remember that we are not going to persist user plain password anywhere for our authorization process? But we can use digital Signature facility provided with Signature class. Let's get down to signing user passwords.  &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;In this method we use Signature class and it doesn't encrypt anything, but it signs our password with a private key from the Android KeyStore. As a result, we have a signature of the password and it is not sensitive information so that we can save the signature in SharedPreferences as it is. Please consider that we initiate Signature class for signing operation.&lt;/p&gt;

&lt;p&gt;We are ready to perform the last step and to verify if another user password matches a registered one. &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Again, we use Signature class facility to verify if provided input string and original password are identical. However, this time Signature class is initiated for verification purpose.&lt;/p&gt;

&lt;p&gt;To wrap up, we have a secure way of data verification without persisting any sensitive information at all. Also, generated certificate is not kept by our application but by the Android KeyStore so it is senseless to hack the application to find a signing certificate. &lt;/p&gt;

&lt;h4&gt;
  
  
  References
&lt;/h4&gt;

&lt;p&gt;In the post I used just a few chunks of code to express the whole idea, but you can dive into details of the whole class on GitHab: &lt;a href="https://github.com/sanya5791/KeystoreSamples/blob/master/app/src/main/java/com/akhutornoy/tastekeystore/security/DataSignatureVerifier.kt"&gt;KeystoreSamples&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The following resources were used in the article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.android.com/training/articles/keystore"&gt;Android keystore system&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/googlesamples/android-BasicAndroidKeyStore"&gt;googlesamples/android-BasicAndroidKeyStore&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Originally published at: &lt;a href="https://anadea.info/blog/how-to-verify-users-passwords-using-android-keystore"&gt;anadea.info&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s connect
&lt;/h2&gt;

&lt;p&gt;If you enjoyed this article, visit our blog: &lt;a href="https://anadea.info/blog"&gt;https://anadea.info/blog&lt;/a&gt; or follow us on &lt;a href="https://twitter.com/AnadeaInc"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>android</category>
      <category>keystore</category>
      <category>mobileappdevelopment</category>
      <category>androidapp</category>
    </item>
    <item>
      <title>How to Leverage the Project Management Triangle</title>
      <dc:creator>Anadea</dc:creator>
      <pubDate>Mon, 18 Jun 2018 00:00:00 +0000</pubDate>
      <link>https://dev.to/anadea/how-to-leverage-the-project-management-triangle-137i</link>
      <guid>https://dev.to/anadea/how-to-leverage-the-project-management-triangle-137i</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---dZJr7zk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/1562/Project-management.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---dZJr7zk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/1562/Project-management.png" alt="Project management" width="880" height="526"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Being the project manager at a &lt;a href="https://anadea.info/"&gt;software development company&lt;/a&gt;, I often have to deal with the requests like "I want to do this app for a fixed price, can you do it?" or "Could you tell me a precise cost of my project?". Moreover, clients asking these questions may have just a brief idea of what they want to implement and they may not be sure about all the details of their project, however, they need the answer. &lt;/p&gt;

&lt;p&gt;The only way to handle such situations is to introduce a clear process which lets us deliver a software product that meets technical requirements and at the same time achieve desired time / cost outcomes. Basically, this brings us to the necessity to find the right balance between the constraints faced in every IT project: scope, schedule and cost. This is also known as the Triple Constraint or the Project Management Triangle.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wyGqalPN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/1563/Project-triangle.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wyGqalPN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/1563/Project-triangle.png" alt="Project management triangle" width="880" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;"In order to manage you have to measure it"&lt;/p&gt;

&lt;p&gt;In this article, we will show you how to properly manage all three project constraints and &lt;a href="https://anadea.info/blog/transparency-as-a-clue-to-successful-partnership"&gt;maintain transparency&lt;/a&gt; throughout the software development lifecycle.&lt;/p&gt;

&lt;p&gt;Let's review each constraint in detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scope
&lt;/h2&gt;

&lt;p&gt;Scope is the set of features and functionality required to complete the project.  There are certain steps and activities needed to clearly define the scope and deliver it on time and within schedule and budget.&lt;/p&gt;

&lt;p&gt;The first step is to create a specification document where we:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Break functionality into Epics and User Stories.&lt;/li&gt;
&lt;li&gt;Write down scenarios for user stories.&lt;/li&gt;
&lt;li&gt;Specify acceptance criteria for user stories.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The purpose of creating this document is to outline the requirements for all the features and make it clear where each feature starts and where it ends.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ARpspjsf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/1559/Mind-maps.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ARpspjsf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/1559/Mind-maps.png" alt="Creating IT project specification" width="880" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second step is to create a design for the system (features). At this stage, all pages, screens, features and modules are accurately drawn so that all project participants can clearly understand how it will look like in the future.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KHoRUq-_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/1564/ux-ui.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KHoRUq-_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/1564/ux-ui.png" alt="Creating UI/UX design" width="880" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Based on outcomes of these two steps, project manager creates a Work breakdown structure which records all features that need to be implemented. Now, the project has the list of items that can be estimated by a development team and this is the next step of the process. &lt;/p&gt;

&lt;p&gt;Estimation of every feature includes the following activities:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Development of the feature&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
At this stage, a developer writes code based on specification document and design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Feature testing&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://anadea.info/services/quality-assurance"&gt;QA specialist&lt;/a&gt; ensures that the feature meets specified requirements and do not cause any negative impact on other parts of the system. If any issues are detected, quality engineer documents the issue (called 'bug') and addresses it to the development camp.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Bugfixing&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Based on &lt;a href="https://anadea.info/blog/who-are-quality-assurance-engineers-and-why-do-you-need-one-for-your-project"&gt;QA specialist's recommendations&lt;/a&gt;, developer fixes bugs or implements improvements initiated by QA.&lt;/p&gt;

&lt;p&gt;In order to track progress on the development phase, project manager compares planned estimate with actual one so that there is a clear understanding if the project is on schedule or not.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ttlkWXJn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/1561/Schedule.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ttlkWXJn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/1561/Schedule.png" alt="Track development progress" width="880" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Schedule
&lt;/h2&gt;

&lt;p&gt;Once we have the Work breakdown structure with estimations, we can understand how long it will take us to implement the project given the allocated development team. &lt;/p&gt;

&lt;p&gt;A tool which is used for this purpose is a Gantt chart. This chart makes it easy to visualize project management timelines by transforming task names, start dates, durations, and end dates into cascading horizontal bar charts.&lt;/p&gt;

&lt;p&gt;We usually organize our work based on Agile (Scrum) framework and break project into iterations (Sprints). Duration of each Sprint is 2 weeks and it includes such activities as:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sprint planning to plan the scope of work for a sprint.&lt;/li&gt;
&lt;li&gt;Implementation of the scope (development and testing). &lt;/li&gt;
&lt;li&gt;Daily standups to get the team synced on the sprint progress.&lt;/li&gt;
&lt;li&gt;Demo meeting to show results of the sprint work.&lt;/li&gt;
&lt;li&gt;Retrospective meeting to summarize the sprint results.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w-X__434--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/1558/Gantt-chart.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w-X__434--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/1558/Gantt-chart.png" alt="Gantt chart for tracking software project constraints" width="880" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, using Gantt chart allows all participants to know when feature development starts and when it ends. &lt;/p&gt;

&lt;h2&gt;
  
  
  Cost
&lt;/h2&gt;

&lt;p&gt;The 3rd side of the project triangle is cost - the funds which should be paid for the Scope implementation within the planned Schedule. Cost is usually  calculated by the following formula:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Service Hours (h) * Rate per hour ($) = Cost ($)&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Project costs is a very sensitive aspect and should be tracked very carefully. For the budget tracking, we use a Burndown chart, a graphical representation of how project budget is spent versus planned funds baseline. Funds are shown on the vertical axis, with sprints on the horizontal one. This chart shows if we are staying within the budget scope or there is a risk to exceed it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JfugkxtT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/1560/Burndown-chart.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JfugkxtT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anadea.info/uploads/image_attachment/image/1560/Burndown-chart.png" alt="Burndown chart for leveraging the triple constraints of project management" width="880" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Change requests, e.g. adding new features or modifying UX flow, may significantly affect the budget. The Burndown chart will help to keep finger on the budget pulse and control budget throughout the project lifecycle. &lt;/p&gt;

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

&lt;p&gt;Using Work breakdown structure, Gantt chart and Burndown chart allows us to maintain transparency and keep the balance between the key project management constraints - Scope, Schedule and Cost - during the development process. When you understand the actual state of things in your project, you can efficiently manage it and address issues if something goes wrong. &lt;/p&gt;

&lt;p&gt;It doesn't matter how big or small your project is, it will have its own project management triangle which, when managed properly, can become a triangle of success for your business venture.&lt;/p&gt;




&lt;h2&gt;
  
  
  Let’s connect
&lt;/h2&gt;

&lt;p&gt;If you enjoyed this article, visit our blog: &lt;a href="https://anadea.info/blog"&gt;https://anadea.info/blog&lt;/a&gt; or follow us on &lt;a href="https://twitter.com/AnadeaInc"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>software</category>
      <category>projectmanagement</category>
      <category>projectplanning</category>
    </item>
    <item>
      <title>In-App Advertising Guide For Developers</title>
      <dc:creator>Anadea</dc:creator>
      <pubDate>Thu, 17 May 2018 00:00:00 +0000</pubDate>
      <link>https://dev.to/anadea/in-app-advertising-guide-for-developers-4a5i</link>
      <guid>https://dev.to/anadea/in-app-advertising-guide-for-developers-4a5i</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fanadea.info%2Fuploads%2Fimage_attachment%2Fimage%2F1538%2FIn-app-ads.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fanadea.info%2Fuploads%2Fimage_attachment%2Fimage%2F1538%2FIn-app-ads.jpg" alt="In-app advertising"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Advertising has always been a source of revenue for companies. Sometimes advertising supplemented subscription revenue, and other times the entire business was funded almost solely from advertising. The latter model is how open-airwave broadcasters made money in the past.&lt;/p&gt;

&lt;p&gt;An advertisement-only business model is especially useful on the Internet, where people demand content for free. With a global landscape of competition, someone is always willing to offer a product for free, as long as they can generate &lt;a href="https://anadea.info/blog/the-life-cycle-of-online-ads-from-user-preferences-to-ad-revenue" rel="noopener noreferrer"&gt;advertising revenue&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Not only that, but mobile computing has grown rapidly since the inception of the iPhone in 2008. &lt;a href="http://www.pewresearch.org/fact-tank/2017/01/12/evolution-of-technology/" rel="nofollow noopener noreferrer"&gt;According to Pew Research&lt;/a&gt;, more than 50% of Americans have a tablet, and nearly 80% have a smartphone. They're also spending considerable amounts of &lt;a href="https://www.statista.com/statistics/323522/us-user-mobile-app-engagement-age/" rel="nofollow noopener noreferrer"&gt;time engaging with those apps&lt;/a&gt; every month.&lt;/p&gt;

&lt;p&gt;Assuming you've decided to implement advertising for at least some of your app's revenue, what should you consider in your build?&lt;/p&gt;

&lt;h2&gt;
  
  
  Ad placement and type
&lt;/h2&gt;

&lt;p&gt;The first thing to consider is where you will place your advertisements. This applies to both spatial and temporal placement. Will you use interstitial advertisements, such as between levels of a game? If you don't have transitions, such as an English-Korean dictionary, there is no suitable place for interstitial advertisements. In this latter case, it is better to have thin banner advertisements that display continually along an edge of the app.&lt;/p&gt;

&lt;p&gt;Do you want banner adverts to expand to full screen after every 5 word lookups? Or every 10? Do you want to expand to full screen at all? It might be attractive for higher-paying advertisers, since they can reach a captive audience, but it might also alienate your users.&lt;/p&gt;

&lt;p&gt;The type of advert you choose will impact your placement decisions, too. Video advertisements are certainly better interstitially, while push notifications might be best periodically throughout the day. You might also use different styles for different parts. If your app is a productivity app with a news feed, you might have banner adverts in the personal section and native advertisements in the news feed section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Balance your needs and your customers' comfort
&lt;/h3&gt;

&lt;p&gt;There are plenty of apps that become nearly unusable because there are too many ads. You will need to balance your revenue needs with the number of ads you display, especially if you use adverts that interrupt usage. Non-invasive banner ads may display continually, but you obviously cannot show a video every few seconds. For the latter type, it is especially important to keep data caps in mind too: not every customer has unlimited data, and many low-cap users will immediately delete your app if a video advertisement sucks up 5 megabytes of data.&lt;/p&gt;

&lt;p&gt;Also avoid being too annoying. No one wants 20 notifications from an app when they wake up or finish work. If half of those notifications are simply advertisements, you risk distancing your customers and losing their trust.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use the right tools
&lt;/h3&gt;

&lt;p&gt;This is important for your sanity. &lt;a href="https://blog.enhance.co/2018/01/04/5-stellar-tool-choices-mobile-app-development/" rel="nofollow noopener noreferrer"&gt;Plenty of tools&lt;/a&gt; exist to keep you sane. Just on development, there are multiple SDKs for platforms, and if you want to develop for the widest audience, you will need to manage all of them. Then there is the advertising revenue management. And the analytics management. And the marketing management. Having the wrong tools will frustrate you, eventually leading to product breakdowns and frustrated customers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test everything
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://anadea.info/" rel="noopener noreferrer"&gt;Software developers&lt;/a&gt; are intimate with testing. Non-developers may believe most developers spend their time pounding away at keyboards to write thousands of lines of code. Developers know they spend most of their time staring at code they wrote two days ago trying to figure out why it isn't working.&lt;/p&gt;

&lt;p&gt;Testing doesn't vanish once the base product is finished. You need to test your advertising scheme. One trap developers often overlook is advert timing. If users move through your app faster or more frequently than you anticipated, they will be served ads much more than you intended. This simple oversight leads to dissatisfied customers and negative reviews.&lt;/p&gt;

&lt;p&gt;You also want to make sure your ads are excitable from various devices (nothing raises a user's blood pressure faster than an interstitial advert that places the exit button off the screen). And they should look good on every display. With so many devices available, you &lt;a href="https://anadea.info/blog/qa-engineers-working-cycle" rel="noopener noreferrer"&gt;need to test a lot&lt;/a&gt; to avoid leaving out some customers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consider alternative sources of income
&lt;/h2&gt;

&lt;p&gt;In-app advertising is highly prevalent. Many apps derive 100% of their revenue from advertising. However, this is akin to the proverbial egg basket: you shouldn't completely rely on a single source of income. Consider other avenues of revenue, such as in-app purchases or an upgraded version that completely removes adverts for those willing to pay. &lt;/p&gt;

&lt;p&gt;In fact, the &lt;a href="https://amp.businessinsider.com/images/5984879176084a1d008b5721-640-487.png" rel="nofollow noopener noreferrer"&gt;app revenue landscape&lt;/a&gt; suggests in-app purchases make up the vast majority of non-advert-based revenue, while paid installs and subscriptions are a very small portion. Keeping this in mind, carefully consider your route to diversifying your revenue streams.&lt;/p&gt;

&lt;p&gt;If you're more of a visual person, you can see all of this in an illustrative infographic &lt;a href="https://s3-us-west-2.amazonaws.com/enh-ig/DevelopersGuideInAppAds.png" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is a guest post by Nick Andrew Rojas.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s connect
&lt;/h2&gt;

&lt;p&gt;If you enjoyed this article, visit our blog: &lt;a href="https://anadea.info/blog" rel="noopener noreferrer"&gt;https://anadea.info/blog&lt;/a&gt; or follow us on &lt;a href="https://twitter.com/AnadeaInc" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>development</category>
      <category>inappadvertising</category>
      <category>appdevelopment</category>
      <category>monetization</category>
    </item>
  </channel>
</rss>
