<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[LissetteIbnz]]></title><description><![CDATA[I'm a Frontend developer based in Tenerife, Canary Islands.
I'm a enthusiasts of TypeScript, React and Vue. I love sharing knowledge and collaborating with the ]]></description><link>https://lissetteibnz.es</link><generator>RSS for Node</generator><lastBuildDate>Sat, 11 Apr 2026 21:14:54 GMT</lastBuildDate><atom:link href="https://lissetteibnz.es/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Cancelando promesas en React]]></title><description><![CDATA[Si estás trabajando con React en algún momento puede haberte llegado a la consola del navegador un aviso sobre una pérdida de memoria al haber actualizado el estado de un componente estando desmontado:

Muchos de nosotros evitamos este problema recur...]]></description><link>https://lissetteibnz.es/cancelando-promesas-en-react</link><guid isPermaLink="true">https://lissetteibnz.es/cancelando-promesas-en-react</guid><category><![CDATA[React]]></category><category><![CDATA[hooks]]></category><category><![CDATA[ReactHooks]]></category><dc:creator><![CDATA[Sara Lissette]]></dc:creator><pubDate>Wed, 10 Aug 2022 13:28:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/jf1EomjlQi0/upload/v1660137982818/LeAjxxxn3.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Si estás trabajando con React en algún momento puede haberte llegado a la consola del navegador un aviso sobre una pérdida de memoria al haber actualizado el estado de un componente estando desmontado:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660031517339/7oXJQ3Jrh.png" alt="image.png" /></p>
<p>Muchos de nosotros evitamos este problema recurriendo a la típica comprobación de <code>isMounted</code>, donde básicamente controlábamos el desmonte de un componente de React mediante una referencia que consultábamos justo antes de hacer la actualización del estado. </p>
<blockquote>
<p>Recordemos que en las primeras versiones de React teníamos esta propiedad de forma interna en la librería, pero <a target="_blank" href="https://reactjs.org/blog/2015/12/16/ismounted-antipattern.html">fue deprecada allá por el 2015 porque se trataba de un anti-patrón</a>... una mala práctica que el equipo de React avisaba y desaconsejaba su uso. Eso sí, al menos nos mostraban el camino correcto 😅</p>
</blockquote>
<p><strong>Ahora bien, ¿qué podemos hacer para solucionar este problema?</strong></p>
<p>Podemos continuar con el uso del <code>isMounted</code>, pero como bien nos recomienda el artículo, lo suyo sería cancelar la promesa para que no se llegue a producir esa actualización del estado.</p>
<p>Pero si bien la forma anterior está correcta, no es lo más óptimo. Si nos fijamos en lo que sucede en nuestro navegador, vemos cómo efectivamente no tenemos esa pérdida de memoria porque no se intenta actualizar el estado cuando el componente está desmontado, pero lo que sí podemos ver es que nuestra llamada a la API continua su curso, descargando la información de la llamada aunque no haga nada con su respuesta.</p>
<blockquote>
<p>Hemos solucionado un problema pero seguimos arrastrando una pérdida de recursos.</p>
</blockquote>
<h2 id="heading-abortcontroller-con-fetch">AbortController con Fetch</h2>
<p>Hoy te propongo hacer uso de la interface <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/AbortController">AbortController</a> que te permitirá abortar o cancelar una solicitud web cuando quieras.</p>
<p>Su uso es muy sencillo como verás a continuación:</p>
<p><strong>Creamos el controlador</strong></p>
<pre><code><span class="hljs-keyword">const</span> controller = <span class="hljs-keyword">new</span> AbortController();
</code></pre><p>Este objeto nos proporciona un método <code>abort</code> que será el que nos permitirá abortar nuestra solicitud web cuando deseemos. También nos retorna una propiedad <code>signal</code> que será la que le pasaremos a nuestra configuración <code>fetch</code> para que enlace dicha funcionalidad.</p>
<p>Cuando <code>abort()</code> sea llamado:</p>
<ul>
<li><code>controller.signal</code> emitirá un evento <code>abort</code>.</li>
<li><code>controller.signal.aborted</code> será <code>true</code>.</li>
</ul>
<p><strong>Cómo lo usamos</strong></p>
<pre><code>const controller <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> AbortController();
fetch(url, {
  signal: controller.signal
});
</code></pre><p>Para nuestra suerte, <code>fetch</code> permite el uso de la interface <code>AbortController</code> así que con el código anterior estará preparado para actuar en caso de que nuestra señal sea abortada.</p>
<p><strong>Cancelando una llamada</strong></p>
<p>Ahora para cancelar nuestra llamada a la API, sólo debemos ejecutar el método <code>abort</code> de nuestro objeto <code>controller</code>:</p>
<pre><code>controller.abort();
</code></pre><p>Ahora veremos cómo podemos crearnos un hook para encapsular esta funcionalidad 👇</p>
<h2 id="heading-useabortablesignal-hook">useAbortableSignal Hook</h2>
<pre><code>export const useAbortableSignal <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  const [controller] <span class="hljs-operator">=</span> useState(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> <span class="hljs-keyword">new</span> AbortController());

  useEffect(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    <span class="hljs-keyword">return</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
      controller.abort();
    };
  }, []);

  <span class="hljs-keyword">return</span> {
    signal: controller.signal
  };
};
</code></pre><h3 id="heading-su-uso-en-un-componente">Su uso en un componente</h3>
<pre><code>export const useData <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  const { signal } <span class="hljs-operator">=</span> useAbortableSignal();

  const [isLoading, setIsLoading] <span class="hljs-operator">=</span> React.useState(<span class="hljs-literal">true</span>);
  const [<span class="hljs-function"><span class="hljs-keyword">error</span>, <span class="hljs-title">setError</span>] = <span class="hljs-title">React</span>.<span class="hljs-title">useState</span>(<span class="hljs-params"><span class="hljs-string">''</span></span>)</span>;
  const [data, setData] <span class="hljs-operator">=</span> React.useState([]);

  React.useEffect(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    const fetchData <span class="hljs-operator">=</span> async () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
      <span class="hljs-keyword">try</span> {
        const result <span class="hljs-operator">=</span> await getData(signal);
        setData(result);
      } <span class="hljs-keyword">catch</span> (err) {
        <span class="hljs-keyword">if</span> (err) {
          const isAborted <span class="hljs-operator">=</span> err.<span class="hljs-built_in">name</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">'AbortError'</span>;
          <span class="hljs-keyword">if</span> (isAborted) {
            <span class="hljs-keyword">return</span>;
          }

          setError(<span class="hljs-string">'Something went wrong while fetching data'</span>);
        }
      } finally {
        setIsLoading(<span class="hljs-literal">false</span>);
      }
    };

    fetchData();
  }, []);

  <span class="hljs-keyword">return</span> { isLoading, data, <span class="hljs-function"><span class="hljs-keyword">error</span> }</span>;
};
</code></pre><blockquote>
<p>Con el código anterior además estás controlando que el error manejado en el <code>catch</code> sea porque la solicitud ha sido cancelada y lo podríamos omitir sin problema.</p>
</blockquote>
<p>Y ahora si ves el resultado en la pestaña de red de las herramientas de desarrollador... 😊</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660138231811/0DDzLrK2X.png" alt="image.png" /></p>
<h3 id="heading-puedo-usar-el-mismo-controlador-para-varias-promesas">¿Puedo usar el mismo controlador para varias promesas?</h3>
<p>Sí, y de hecho si lo que quieres es cancelar la ejecución de las llamadas a la API en un componente, tiene sentido que todas las solicitudes compartan el mismo controlador porque cuando se desmonte el componente, abortaremos el controlador cancelando todas las solicitudes vivas en el componente.</p>
<h3 id="heading-compatibilidad">Compatibilidad</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660033836413/fg0pQIBBl.png" alt="image.png" /></p>
<p>En el momento de haber escrito este artículo, la tabla de compatibilidad era la de la imagen, pero te aconsejo que la revises nuevamente en este <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/AbortController#browser_compatibility">enlace</a> si tienes alguna duda.</p>
<h3 id="heading-codigo-de-ejemplo-y-comparativa">Código de ejemplo y comparativa</h3>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/s/abortable-fetch-ffkyzh">https://codesandbox.io/s/abortable-fetch-ffkyzh</a></div>
<p>Ver completo: https://codesandbox.io/s/abortable-fetch-ffkyzh</p>
<h3 id="heading-recursos">Recursos</h3>
<ul>
<li>https://developer.mozilla.org/en-US/docs/Web/API/AbortController</li>
<li>https://javascript.info/fetch-abort</li>
<li>https://developer.chrome.com/blog/abortable-fetch/</li>
<li>https://github.com/whatwg/fetch/issues/27</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Aumenta tu productividad con Vuex gracias a TypeScript]]></title><description><![CDATA[Puedes consultar mi artículo publicado en Software Crafters

Solución enfocada al uso de Vuex 3 la cual trabaja con Vue.js 2.]]></description><link>https://lissetteibnz.es/aumenta-tu-productividad-con-vuex-gracias-a-typescript</link><guid isPermaLink="true">https://lissetteibnz.es/aumenta-tu-productividad-con-vuex-gracias-a-typescript</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[Vue.js]]></category><category><![CDATA[Vuex]]></category><dc:creator><![CDATA[Sara Lissette]]></dc:creator><pubDate>Tue, 21 Sep 2021 09:36:46 GMT</pubDate><content:encoded><![CDATA[<p>Puedes consultar mi artículo publicado en <a target="_blank" href="https://softwarecrafters.io/vuejs/aumenta-productividad-vuex-typescript">Software Crafters</a></p>
<blockquote>
<p>Solución enfocada al uso de Vuex 3 la cual trabaja con Vue.js 2.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Template literals en tus test para mejorar la legibilidad y rapidez al escribirlos]]></title><description><![CDATA[Seguramente en muchos de tus test se repite la tónica de probar ciertos valores como null, undefined, arrays vacíos [], valores falsy y cosas así.
Al menos yo sí lo hago porque trabajo mucho con mapeadores de contenido de la API al modelo de la vista...]]></description><link>https://lissetteibnz.es/template-literals-en-tus-test-para-mejorar-la-legibilidad-y-rapidez-al-escribirlos</link><guid isPermaLink="true">https://lissetteibnz.es/template-literals-en-tus-test-para-mejorar-la-legibilidad-y-rapidez-al-escribirlos</guid><category><![CDATA[Testing]]></category><category><![CDATA[Jest]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Sara Lissette]]></dc:creator><pubDate>Tue, 20 Jul 2021 18:58:48 GMT</pubDate><content:encoded><![CDATA[<p>Seguramente en muchos de tus test se repite la tónica de probar ciertos valores como <code>null</code>, <code>undefined</code>, arrays vacíos <code>[]</code>, valores <code>falsy</code> y cosas así.</p>
<p>Al menos yo sí lo hago porque trabajo mucho con mapeadores de contenido de la API al modelo de la vista.</p>
<p>En ellos solía hacer esto:</p>
<pre><code>    test(<span class="hljs-string">"should return an empty array when passes a null value"</span>, <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {
      expect(mapFormatsApiToVm(<span class="hljs-literal">null</span>)).toEqual([])
    })

    test(<span class="hljs-string">"should return an empty array when passes an undefined value"</span>, <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {
      expect(mapFormatsApiToVm(<span class="hljs-literal">undefined</span>)).toEqual([])
    })

    test(<span class="hljs-string">"should return an empty array when passes an empty array"</span>, <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {
      expect(mapFormatsApiToVm([])).toEqual([])
    })
</code></pre><p>A lo mejor probar estos casos uno a uno no te parecen complicados ni te molesta repetirlos una y otra vez, pero... ¿qué tal si probamos con un validador de URLs? 
Entonces ya sí empieza a ser un tostón porque son muchos casos a comprobar.</p>
<p>Jest tiene una característica que puede ayudarte a realizar tests mucho más rápido y se llama <a target="_blank" href="https://jestjs.io/es-ES/docs/api#testeachtablename-fn-timeout">each</a></p>
<p>En este tuit te mostré cómo usarla rápidamente 👇 https://mobile.twitter.com/lissetteibnz/status/1362053307590672386</p>
<p>Pero lo que te quiero mostrar hoy creo que te ayudará mucho más.</p>
<p>Continuando con nuestro test de URL válida, vamos a probar una serie de casos:</p>
<pre><code><span class="hljs-literal">null</span>
<span class="hljs-literal">undefined</span>
<span class="hljs-string">""</span>
<span class="hljs-string">"https://www.example.com"</span>
<span class="hljs-string">"http://www.example.com"</span>
<span class="hljs-string">"www.example.com"</span>
<span class="hljs-string">"example.com"</span>
<span class="hljs-string">"http://blog.example.com"</span>
<span class="hljs-string">"http://www.example.com/product"</span>
<span class="hljs-string">"http://www.example.com/products?id=1&amp;page=2"</span>
<span class="hljs-string">"http://www.example.com#up"</span>
<span class="hljs-string">"http://255.255.255.255"</span>
<span class="hljs-string">"255.255.255.255"</span>
<span class="hljs-string">"http://www.site.com:8008"</span>
<span class="hljs-string">"http://invalid.com/perl.cgi?key= | http://web-site.com/cgi-bin/perl.cgi?key1=value1&amp;key2"</span>
</code></pre><p>Pues bien, en vez de escribir los test agrupando por su resultado y utilizar <code>each</code> con ellos, vamos a utilizar <code>each</code> pero pasando como argumento nuestra tabla de casos.</p>
<pre><code><span class="hljs-string">const</span> <span class="hljs-string">casesTable</span> <span class="hljs-string">=</span> [
      [<span class="hljs-literal">false</span>, <span class="hljs-literal">null</span>],
      [<span class="hljs-literal">false</span>, <span class="hljs-string">undefined</span>],
      [<span class="hljs-literal">false</span>, <span class="hljs-string">""</span>],
      [<span class="hljs-literal">false</span>, <span class="hljs-string">"http://invalid.com/perl.cgi?key= | http://web-site.com/cgi-bin/perl.cgi?key1=value1&amp;key2"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"https://www.example.com"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"http://www.example.com"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"www.example.com"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"example.com"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"http://blog.example.com"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"http://www.example.com/product"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"http://www.example.com/products?id=1&amp;page=2"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"http://www.example.com#up"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"http://255.255.255.255"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"255.255.255.255"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"http://www.site.com:8008"</span>],
    ]
</code></pre><p>Lo que hemos hecho es crear <strong>un array donde cada item es otro array en el que el primer valor es el resultado esperado y el segundo valor el caso a testar</strong>.</p>
<p>Esta tabla será la que alimente el método <code>each</code> de nuestro test.</p>
<p>Seguidamente, vamos a escribir el texto del test y aprovechando la funcionalidad de <code>template literals</code> vamos a escapar el primer y segundo argumento en formato <code>pretty</code> (con este formato veremos cómo valores como el string vacío nos lo muestra así "" en vez de aparecer un hueco en el texto).</p>
<pre><code>    const casesTable = [
      [<span class="hljs-literal">false</span>, <span class="hljs-literal">null</span>],
      [<span class="hljs-literal">false</span>, <span class="hljs-literal">undefined</span>],
      [<span class="hljs-literal">false</span>, <span class="hljs-string">""</span>],
      [
        <span class="hljs-literal">false</span>,
        <span class="hljs-string">"http://invalid.com/perl.cgi?key= | http://web-site.com/cgi-bin/perl.cgi?key1=value1&amp;key2"</span>,
      ],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"https://www.example.com"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"http://www.example.com"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"www.example.com"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"example.com"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"http://blog.example.com"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"http://www.example.com/product"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"http://www.example.com/products?id=1&amp;page=2"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"http://www.example.com#up"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"http://255.255.255.255"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"255.255.255.255"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"http://www.site.com:8008"</span>],
    ];
    test.each(casesTable)(<span class="hljs-string">"should return %p when passes %p value"</span>, <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {

    });
</code></pre><p>Y por último, recibiremos los parámetros para usarlos en nuestro test en el mismo orden definido en la tabla:</p>
<pre><code> const casesTable = [
      [<span class="hljs-literal">false</span>, <span class="hljs-literal">null</span>],
      [<span class="hljs-literal">false</span>, <span class="hljs-literal">undefined</span>],
      [<span class="hljs-literal">false</span>, <span class="hljs-string">""</span>],
      [
        <span class="hljs-literal">false</span>,
        <span class="hljs-string">"http://invalid.com/perl.cgi?key= | http://web-site.com/cgi-bin/perl.cgi?key1=value1&amp;key2"</span>,
      ],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"https://www.example.com"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"http://www.example.com"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"www.example.com"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"example.com"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"http://blog.example.com"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"http://www.example.com/product"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"http://www.example.com/products?id=1&amp;page=2"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"http://www.example.com#up"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"http://255.255.255.255"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"255.255.255.255"</span>],
      [<span class="hljs-literal">true</span>, <span class="hljs-string">"http://www.site.com:8008"</span>],
    ];
    test.each(casesTable)(<span class="hljs-string">"should return %p when passes %p value"</span>, <span class="hljs-function"><span class="hljs-params">(testResult, testValue)</span> =&gt;</span> {
      expect(isValidURL(testValue)).toBe(testResult);
    });
</code></pre><p>Y lo mejor de todo el resultado de nuestros test en la consola 😊</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1626807028436/cBgTFEGNI.png" alt="image.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1626807086205/SQfXeJAeT.png" alt="image.png" /></p>
<p>¿Qué te ha parecido? Espero que te resulte útil 😁</p>
<p>Te animo a revisar la documentación completa porque al igual que usar los template literals y las tablas en la descripción de los tests, lo puedes hacer en los describe, así que a volar tu imaginación.</p>
]]></content:encoded></item><item><title><![CDATA[Aserción, inferencia y anotación literal de tipos en TypeScript]]></title><description><![CDATA[Cuando trabajamos con TypeScript nos apoyamos en la herramienta para que nos ayude a crear nuestro código. Si todo va bien, TypeScript infiere los tipos del propio uso y nos arroja información sobre los errores que se produzcan. Pero en ocasiones, es...]]></description><link>https://lissetteibnz.es/asercion-inferencia-y-anotacion-literal-de-tipos-en-typescript</link><guid isPermaLink="true">https://lissetteibnz.es/asercion-inferencia-y-anotacion-literal-de-tipos-en-typescript</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[Vue.js]]></category><category><![CDATA[best practices]]></category><dc:creator><![CDATA[Sara Lissette]]></dc:creator><pubDate>Mon, 10 May 2021 15:24:08 GMT</pubDate><content:encoded><![CDATA[<p>Cuando trabajamos con TypeScript nos apoyamos en la herramienta para que nos ayude a crear nuestro código. Si todo va bien, TypeScript infiere los tipos del propio uso y nos arroja información sobre los errores que se produzcan. Pero en ocasiones, esto no es tan sencillo y debemos ayudar a TypeScript para que comprenda nuestro código.</p>
<p>Hoy te explicaré la diferencia principal entre la aserción de tipos, la inferencia de tipos y la anotación literal de tipos en TypeScript, a través de un ejemplo real con el framework Vue.</p>
<p><a class="post-section-overview" href="#ejemplo-en-la-vida-real">TL,DR</a></p>
<p>Vamos a trabajar con una función que nos devolverá un objeto que contendrá las propiedades de un producto.</p>
<p>Lo primero que haremos será definir una <code>interface</code> que será quien nos describa nuestro modelo de objeto:</p>
<pre><code><span class="hljs-keyword">type</span> Price = <span class="hljs-built_in">string</span>;
<span class="hljs-keyword">type</span> ProductId = <span class="hljs-built_in">number</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> Product {
  id: ProductId;
  author: <span class="hljs-built_in">string</span>;
  title: <span class="hljs-built_in">string</span>;
  price: Price;
}
</code></pre><p>Ahora vamos a definir nuestra función <code>getProduct</code> que como hemos dicho, sólo deberá crearnos un producto.</p>
<h2 id="inferencia-de-tipos">Inferencia de tipos</h2>
<p>Si asignamos la devolución de esta función a un objeto, TypeScript nos inferirá las propiedades que tiene. Si te fijas, no estamos haciendo uso de ningún tipado por lo que el modelo lo está infiriendo.</p>
<pre><code>const getProduct = <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> ({
  id: <span class="hljs-number">1</span>,
  author: <span class="hljs-string">'irrelevant author'</span>,
  title: <span class="hljs-string">'irrelevant title'</span>,
  price: <span class="hljs-string">'45.00'</span>
})
</code></pre><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620659363434/G3AyxwEw8.png" alt="image.png" /></p>
<p>¿Pero qué pasa con la inferencia de tipos? Pues que si no devolvemos alguna de las propiedades de la interface o las cambiamos, no se quejaría porque no está enlazada a ella y no es capaz de saber que realmente queremos retornar un objeto con un modelo concreto. <strong>En este momento, está trabajando la inferencia de tipos</strong> y no tenemos forma de cumplir una interface.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620659636887/CPmI0EUJu.png" alt="image.png" /></p>
<p>¿Cómo le decimos a TypeScript que queremos retornar, específicamente, un objeto con nuestro modelo? Por ejemplo, mediante la aserción de tipos.</p>
<h2 id="asercion-de-tipos">Aserción de tipos</h2>
<p>La aserción de tipos es cuando utilizamos la palabra clave <code>as</code> para decirle que ese valor es de X tipo.</p>
<p>Fíjate cómo añandiendo ahora en el retorno de la función <code>as Product</code>, cuando vamos a utilizar el objeto <code>product</code> nos dice que no está <code>anotherProp</code> disponible.</p>
<pre><code class="lang-diff">const getProduct = () =&gt; ({
  id: 1,
  author: 'irrelevant author',
  title: 'irrelevant title',
  price: '45.00',
  anotherProp: 'irrelevant prop',
<span class="hljs-deletion">- })</span>
<span class="hljs-addition">+ }) as Product</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620647737700/kxQyxLZXY.png" alt="image.png" /></p>
<p>Es más, ahora si quitamos una propiedad que cumpla la interface, se quejará nuestro código (comentamos la línea del precio del producto):</p>
<pre><code>const getProduct = <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> ({
  id: <span class="hljs-number">1</span>,
  author: <span class="hljs-string">'irrelevant author'</span>,
  title: <span class="hljs-string">'irrelevant title'</span>,
  <span class="hljs-regexp">//</span> price: <span class="hljs-string">'45.00'</span>,  &lt;- ERROR: Property <span class="hljs-string">'price'</span> <span class="hljs-keyword">is</span> missing <span class="hljs-keyword">in</span> type <span class="hljs-string">'{ id: number; author: string; title: string; anotherProp: string; }'</span> but required <span class="hljs-keyword">in</span> type <span class="hljs-string">'Product'</span>.
  anotherProp: <span class="hljs-string">'irrelevant prop'</span>
}) <span class="hljs-keyword">as</span> Product
</code></pre><p>Pero... ¿por qué no nos arroja un error sobre <code>anotherProp</code> si esa propiedad no es parte de nuestro modelo? Porque con la aserción de tipos las propiedades de más no influyen en nuestro modelo a nivel de tipos, porque si lo piensas, una vez vayas a usar el objeto, TypeScript no te mostrará esa propiedad disponible y sería como si no fuera a afectar a nuestro código.</p>
<p>Pero si realmente queremos que nos diga que esa propiedad no es parte de nuestro modelo y nos queremos poner estrictos, usaremos la anotación literal de tipos.</p>
<h2 id="anotacion-literal-de-tipos">Anotación literal de tipos</h2>
<pre><code class="lang-diff"><span class="hljs-deletion">- const getProduct = () =&gt; ({</span>
<span class="hljs-addition">+ const getProduct = (): Product =&gt; ({</span>
  id: 1,
  author: 'irrelevant author',
  title: 'irrelevant title',
  price: '45.00',
  anotherProp: 'irrelevant prop',
})
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620648303968/tObkf7V7o.png" alt="image.png" /></p>
<blockquote>
<p>ERROR: Type '{ id: number; author: string; title: string; price: string; anotherProp: string; }' is not assignable to type 'Product'.</p>
</blockquote>
<p>Además, recuerda que esta es la forma aconsejada de tipar tus funciones por razones obvias.</p>
<h2 id="ejemplo-en-la-vida-real">Ejemplo en la vida real</h2>
<p>¿Y saber estas diferencias para qué nos podrían servir? Pues voy a poner un ejemplo.</p>
<p>Últimamente estoy trabajando en un proyecto personal con Vue 3 y TypeScript, y buscando documentación y ejemplos sobre cómo tipar estructuras como el <code>data</code> de Vue, me encuentro con que siempre utilizan la aserción de tipos de TypeScript.</p>
<p>Veamos en un ejemplo qué problemas tiene usar este modelo de tipos.</p>
<p>Para ello, continuaremos con la definición de las interfaces anteriores para <code>Product</code>. Para quitar ruido, las tendremos en un fichero de tipos dentro de nuestra aplicación.</p>
<pre><code><span class="hljs-comment">// src/types/index.ts</span>

<span class="hljs-keyword">type</span> Price = <span class="hljs-built_in">string</span>;
<span class="hljs-keyword">type</span> ProductId = <span class="hljs-built_in">number</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> Product {
  id: ProductId;
  author: <span class="hljs-built_in">string</span>;
  title: <span class="hljs-built_in">string</span>;
  price: Price;
}
</code></pre><p>Y ahora definiremos un componente que usará nuestro modelo. </p>
<p>Este componente tendrá una propiedad <code>data</code> que será donde tendremos nuestro listado de productos y el id del producto seleccionado.</p>
<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"ts"</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">import</span> { defineComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">"vue"</span>;
<span class="hljs-keyword">import</span> { Product } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/types"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineComponent({
  data() {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">list</span>: [],
      <span class="hljs-attr">selectedProductId</span>: <span class="hljs-number">0</span>,
    }
  }
});
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre><p>Lo ideal sería definir una interface para declarar este Data. Mi intención sería la siguiente:</p>
<pre><code><span class="hljs-selector-tag">interface</span> <span class="hljs-selector-tag">Data</span> {
  <span class="hljs-attribute">list</span>: Product[];
  <span class="hljs-attribute">selectedProductId</span>: Product[<span class="hljs-string">"id"</span>];
}
</code></pre><p>Primero, veremos cómo suelen declarar este <code>data</code> en los ejemplos que he visto:</p>
<pre><code><span class="hljs-comment">// Forma común</span>
export default {
  <span class="hljs-keyword">data</span>() {
    <span class="hljs-keyword">return</span> {
      list: [] <span class="hljs-keyword">as</span> Product[],
      selectedProductId: <span class="hljs-number">0</span>
    }
  }
}

<span class="hljs-comment">// Otra forma</span>
export default {
  <span class="hljs-keyword">data</span>() {
    <span class="hljs-keyword">return</span> {
      list: [],
      selectedProductId: <span class="hljs-number">0</span>,
    } <span class="hljs-keyword">as</span> unknown <span class="hljs-keyword">as</span> Data;
  }
}
</code></pre><p>El problema que tiene la aserción de tipos es que tú puedes decirle que un valor será del tipo <code>Product</code>, pero en la inicialización pasarle un aguacate, que no se quejará de nada, porque le estás forzando el tipado. Le estás diciendo a TypeScript que confíe en ti 😅</p>
<p>Otro problema que tiene es que no tienes forma de unir la definición de tu interface <code>Data</code> de forma real. Si cambias algo, debes acordarte de cambiarlo en el <code>data</code> porque no te avisará de los errores.</p>
<p>Entonces, ¿qué podemos hacer? En este caso, el <code>data</code> de nuestro componente no es más que una <em>función</em> que retornará un objeto reactivo. Olvidemos lo de reactivo y centrémonos en <strong>una función que retorna un objeto</strong>.</p>
<p>Si queremos que nuestro <code>data</code> trabaje como anotación de tipos literales y no como aserción o inferencia, ¿por qué simplemente no le decimos el retorno que debe llevar esa función?</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
 data(): Data {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-built_in">list</span>: [],
      selectedProductId: <span class="hljs-number">0</span>,
    }
  }
}
</code></pre><p>Y de esta forma, si agregamos alguna propiedad o modificamos algo en nuestra interface Data, nos arrojará un error en la definición del <code>data</code> del componente</p>
<pre><code class="lang-diff">interface Data {
  list: Product[];
  selectedProductId: Product["id"];
<span class="hljs-addition">+  anotherProp: string;</span>
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620650989576/mgez1Czki.png" alt="image.png" /></p>
<p>En resumen:</p>
<p>¡Deja de usar <code>as</code> tal cosa! De hecho... creo que sólo en los <code>reduce</code> de creación es cuando único puede tener sentido una aserción de tipos. De resto, puede resultar un mal olor de tus tipos.</p>
]]></content:encoded></item><item><title><![CDATA[Utilizar i18n de una forma más eficiente (versión JS y JSDoc)]]></title><description><![CDATA[En mi anterior post te explicaba cómo poder utilizar i18n con un objeto en TypeScript. Ahora te mostraré cómo hacerlo en un proyecto sin TypeScript, mediante el uso de JSDoc.
Solución con JSDoc y JavaScript
Partimos de la misma función pero esta vez ...]]></description><link>https://lissetteibnz.es/utilizar-i18n-de-una-forma-mas-eficiente-version-js-y-jsdoc</link><guid isPermaLink="true">https://lissetteibnz.es/utilizar-i18n-de-una-forma-mas-eficiente-version-js-y-jsdoc</guid><category><![CDATA[i18n]]></category><category><![CDATA[React]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Sara Lissette]]></dc:creator><pubDate>Mon, 08 Feb 2021 13:44:40 GMT</pubDate><content:encoded><![CDATA[<p>En mi <a target="_blank" href="https://lissetteibnz.es/utilizar-i18n-de-una-forma-mas-eficiente-version-ts">anterior post</a> te explicaba cómo poder utilizar <code>i18n</code> con un objeto en TypeScript. Ahora te mostraré cómo hacerlo en un proyecto sin TypeScript, mediante el uso de JSDoc.</p>
<h2 id="heading-solucion-con-jsdoc-y-javascript">Solución con JSDoc y JavaScript</h2>
<p>Partimos de la misma función pero esta vez en JavaScript:</p>
<pre><code>export <span class="hljs-keyword">const</span> extractObjectPath = (obj) =&gt; {
  <span class="hljs-keyword">const</span> result = {};

  <span class="hljs-keyword">const</span> recursivePathCalculation = (source, rootPath = [], target = result) =&gt; {
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> key <span class="hljs-keyword">in</span> source) {
      <span class="hljs-keyword">if</span> (source.hasOwnProperty(key)) {
        <span class="hljs-keyword">const</span> path = rootPath.slice();
        path.push(key);

        <span class="hljs-keyword">const</span> <span class="hljs-keyword">value</span> = source[key];
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">value</span> !== <span class="hljs-literal">null</span> &amp;&amp; <span class="hljs-keyword">typeof</span> <span class="hljs-keyword">value</span> === <span class="hljs-string">"object"</span>) {
          recursivePathCalculation(<span class="hljs-keyword">value</span>, path, (target[key] = {}));
        } <span class="hljs-keyword">else</span> {
          target[key] = path.<span class="hljs-keyword">join</span>(<span class="hljs-string">"."</span>);
        }
      }
    }
  };
  recursivePathCalculation(obj);

  <span class="hljs-keyword">return</span> result;
};
</code></pre><p>Recuerda que esta función nos retorna una estructura de objeto donde cada propiedad es un nivel de nuestro objeto traductor.</p>
<p>Para poder usarla, vamos a exportar un objeto donde llamaremos a la función anterior pasando como parámetro un objeto de traducciones. </p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> tkeys = extractObjectPath({ ...es });
</code></pre><p>Y ahora llega la magia...✨ para poder inferir el tipado vamos a añadir mediante JSDoc el siguiente tipo:</p>
<pre><code><span class="hljs-comment">/** <span class="hljs-doctag">@type</span> typeof es */</span>
export <span class="hljs-keyword">const</span> tkeys = extractObjectPath({ ...es });
</code></pre><blockquote>
<p>Fíjate que cuando decimos <code>typeof es</code> nos referimos a que el tipo será "el valor" que tenga el objeto que le digamos, en este caso al objeto <code>es</code> que se corresponde con la importación de mi fichero de traducciones al castellano.</p>
</blockquote>
<p>De este modo, conseguiremos que al usar este objeto <code>tkeys</code> que previamente hemos importado, apoyarnos en el autocompletado que nos facilita el IDE:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1612787682575/22cD8pWMV.gif" alt="DemoTKeys.gif" /></p>
<p>🔥 Aquí puedes probarla y ver el código completo:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/s/i18n-con-javascript-y-jsdoc-1okc2?file=/src/translations/index.js">https://codesandbox.io/s/i18n-con-javascript-y-jsdoc-1okc2?file=/src/translations/index.js</a></div>
<p>¿Qué te ha parecido? Espero que te sirva de ayuda.</p>
<p>👉 En un siguiente artículo le daremos un giro de tuerca más 🔩 y te mostraré otra forma también muy interesante de conseguir tus traducciones 👩‍💻</p>
]]></content:encoded></item><item><title><![CDATA[Utilizar i18n de una forma más eficiente (versión TS)]]></title><description><![CDATA[Cuando hablamos de i18n en una aplicación nos solemos referir a la funcionalidad o soporte de internacionalización de la misma.
En JavaScript contamos con una serie de librerías que nos ayudan a dar soporte a esta funcionalidad y sin ir más lejos, ex...]]></description><link>https://lissetteibnz.es/utilizar-i18n-de-una-forma-mas-eficiente-version-ts</link><guid isPermaLink="true">https://lissetteibnz.es/utilizar-i18n-de-una-forma-mas-eficiente-version-ts</guid><category><![CDATA[i18n]]></category><category><![CDATA[React]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Sara Lissette]]></dc:creator><pubDate>Mon, 08 Feb 2021 12:35:37 GMT</pubDate><content:encoded><![CDATA[<p>Cuando hablamos de i18n en una aplicación nos solemos referir a la funcionalidad o soporte de internacionalización de la misma.</p>
<p>En JavaScript contamos con una serie de librerías que nos ayudan a dar soporte a esta funcionalidad y sin ir más lejos, existe una muy conocida llamada <a target="_blank" href="https://www.i18next.com/">i18n-next</a>, que además tiene soporte en los frameworks más conocidos y está altamente extendida.</p>
<p>Cuando la instalamos en nuestros proyectos la solemos usar con literales de traducción como cadenas de texto, y si bien, es una forma rápida de obtener la traducción, esta alternativa es deficiente de cara a su manipulación y refactorización, además de convertirla en un coladero de errores:</p>
<pre><code><span class="hljs-comment">// has a mistake 👻</span>
<span class="hljs-selector-tag">t</span>(<span class="hljs-string">"comon.cancel"</span>)
</code></pre><blockquote>
<p>Probablemente he llamado a la traducción como "common.cancel" y sin embargo al usarla he puesto "comon.cancel"</p>
</blockquote>
<h2 id="heading-solucion-con-typescript">Solución con TypeScript</h2>
<p>Hoy te propongo crear una función que te permitirá usar la librería de una forma distinta, la cual te ayudará a refactorizar esas traducciones sin problemas y que no te equivoques al escribirla. Por fin di adiós al típico error de <code>contineu</code> cuando querías poner <code>continue</code> .</p>
<p>Vamos a escribir una función recursiva que será la encargada de devolvernos la cadena que espera <code>i18n</code> como un string, pero que nos permitirá usarla como un objeto:</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> extractObjectPath = &lt;ObjectPath <span class="hljs-keyword">extends</span> <span class="hljs-built_in">object</span>&gt;(
  obj: ObjectPath
): <span class="hljs-function"><span class="hljs-params">ObjectPath</span> =&gt;</span> {
  <span class="hljs-keyword">const</span> result = {} <span class="hljs-keyword">as</span> ObjectPath;

  <span class="hljs-keyword">const</span> recursivePathCalculation = <span class="hljs-function">(<span class="hljs-params">
    source: <span class="hljs-built_in">object</span>,
    rootPath: <span class="hljs-built_in">string</span>[] = [],
    target = result
  </span>) =&gt;</span> {
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> key <span class="hljs-keyword">in</span> source) {
      <span class="hljs-keyword">if</span> (source.hasOwnProperty(key)) {
        <span class="hljs-keyword">const</span> path = rootPath.slice();
        path.push(key);

        <span class="hljs-keyword">const</span> value = source[key];
        <span class="hljs-keyword">if</span> (value !== <span class="hljs-literal">null</span> &amp;&amp; <span class="hljs-keyword">typeof</span> value === <span class="hljs-string">"object"</span>) {
          recursivePathCalculation(value, path, (target[key] = {}));
        } <span class="hljs-keyword">else</span> {
          target[key] = path.join(<span class="hljs-string">"."</span>);
        }
      }
    }
  };
  recursivePathCalculation(obj);

  <span class="hljs-keyword">return</span> result;
};
</code></pre><p>Ahora debemos exportar el objeto que usaremos en nuestra app usando esta función:</p>
<pre><code><span class="hljs-comment">// 👉 Nuestro objeto mágico 🪄</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> tkeys = extractObjectPath({ ...es });
</code></pre><p>De este modo, conseguiremos apoyarnos en el autocompletado que nos facilita el IDE:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1612787682575/22cD8pWMV.gif" alt="DemoTKeys.gif" /></p>
<p>🔥 Aquí puedes probarla y ver el código completo:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/s/i18n-con-typescript-jdxr0?file=/src/App.tsx">https://codesandbox.io/s/i18n-con-typescript-jdxr0?file=/src/App.tsx</a></div>
<p>👉 ¿No tienes TypeScript en tu proyecto? No pasa nada, aquí tienes la <a target="_blank" href="https://lissetteibnz.es/utilizar-i18n-de-una-forma-mas-eficiente-version-js-y-jsdoc">versión de JavaScript con JSDoc</a> 💁‍♀️</p>
<h4 id="heading-recursos">Recursos:</h4>
<ul>
<li><a target="_blank" href="https://developer.mozilla.org/es/docs/Mozilla/Add-ons/WebExtensions/API/i18n">i18n API</a></li>
<li><a target="_blank" href="https://es.wikipedia.org/wiki/Internacionalizaci%C3%B3n_y_localizaci%C3%B3n">Wiki i18n</a></li>
<li><a target="_blank" href="https://www.i18next.com/">i18n-next</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[React Native: Evitar que el usuario pueda tomar capturas de pantalla]]></title><description><![CDATA[La seguridad de nuestras aplicaciones es uno de los temas que a veces se dejan de lado y es un factor importante a tener en cuenta cuando desarrollamos si queremos crear un buen producto.
En este artículo voy a explicar cómo podemos aplicar una medid...]]></description><link>https://lissetteibnz.es/react-native-evitar-que-el-usuario-pueda-tomar-capturas-de-pantalla</link><guid isPermaLink="true">https://lissetteibnz.es/react-native-evitar-que-el-usuario-pueda-tomar-capturas-de-pantalla</guid><category><![CDATA[React Native]]></category><category><![CDATA[Android]]></category><category><![CDATA[iOS]]></category><category><![CDATA[best practices]]></category><dc:creator><![CDATA[Sara Lissette]]></dc:creator><pubDate>Fri, 29 Jan 2021 09:50:27 GMT</pubDate><content:encoded><![CDATA[<p>La seguridad de nuestras aplicaciones es uno de los temas que a veces se dejan de lado y es un factor importante a tener en cuenta cuando desarrollamos si queremos crear un buen producto.</p>
<p>En este artículo voy a explicar cómo podemos aplicar una medida de seguridad sencilla pero eficaz (al menos en el 99% de los casos porque evitarlo completamente es casi imposible) de cara a que el usuario no pueda tomar una captura de pantalla de nuestra aplicación cuando la abre en segundo plano.</p>
<p>Comenzamos desde un proyecto de React Native orientado a Android e iOS, así que primero explicaré cómo lo he hecho en Android y luego en iOS.</p>
<h2 id="configuracion-para-android">Configuración para Android</h2>
<blockquote>
<p>Por comodidad aconsejo abrir los ficheros Java desde Android Studio u otro IDE con soporte a Java para manejar sin mayor problema el código.</p>
</blockquote>
<p>Vamos a abrir el fichero <code>MainActivity.java</code> de nuestro proyecto Android. En mi caso está dentro de la carpeta <code>android&gt;app&gt;src&gt;main&gt;java&gt;com&gt;name_project</code></p>
<p>Una vez abierto, importamos el siguiente módulo:</p>
<pre><code><span class="hljs-keyword">import</span> android.<span class="hljs-keyword">view</span>.WindowManager;
</code></pre><p>Y seguidamente, haremos uso del mismo dentro del método <code>onCreate</code> (que crearemos si no está previamente creado), agregando las siguientes sentencias:</p>
<pre><code> <span class="hljs-selector-tag">getWindow</span>()<span class="hljs-selector-class">.setFlags</span>(
     <span class="hljs-selector-tag">WindowManager</span><span class="hljs-selector-class">.LayoutParams</span><span class="hljs-selector-class">.FLAG_SECURE</span>,
     <span class="hljs-selector-tag">WindowManager</span><span class="hljs-selector-class">.LayoutParams</span><span class="hljs-selector-class">.FLAG_SECURE</span>
 );
</code></pre><p>El resultado de nuestro fichero debe ser algo parecido a esto:</p>
<pre><code><span class="hljs-keyword">package</span> com.name_project;

<span class="hljs-keyword">import</span> android.app.Activity;
<span class="hljs-keyword">import</span> android.os.Bundle;

<span class="hljs-keyword">import</span> com.facebook.react.ReactActivity;

<span class="hljs-keyword">import</span> android.view.WindowManager;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MainActivity</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ReactActivity</span> </span>{

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">protected</span> String <span class="hljs-title">getMainComponentName</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"name_project"</span>;
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Activity mCurrentActivity;

    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span><span class="hljs-params">(Bundle savedInstanceState)</span> </span>{
        <span class="hljs-keyword">super</span>.onCreate(savedInstanceState);

        getWindow().setFlags(
                WindowManager.LayoutParams.FLAG_SECURE,
                WindowManager.LayoutParams.FLAG_SECURE
        );

        mCurrentActivity = <span class="hljs-keyword">this</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Activity <span class="hljs-title">getActivity</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> mCurrentActivity;
    }
}
</code></pre><h2 id="configuracion-para-ios">Configuración para iOS</h2>
<blockquote>
<p>Por comodidad aconsejo abrir los ficheros swift desde XCode para manejar sin mayor problema el código.</p>
</blockquote>
<p>Vamos a abrir el fichero <code>AppDelegate.swift</code> de nuestro proyecto iOS. En mi caso está en el raíz de la carpeta <code>ios</code></p>
<p>Dentro de este fichero y dentro de la clase <code>AppDelegate</code> vamos a crear dos funciones que directamente, no evitarán que el usuario pueda hacer capturas de pantalla pero lo que haremos será que cuando la aplicación detecte que está en segundo plano, cambiará la pantalla por una vista no visible con fondo negro.</p>
<pre><code><span class="hljs-meta">@UIApplicationMain</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppDelegate</span>: <span class="hljs-title">UIResponder</span>, <span class="hljs-title">UIApplicationDelegate</span>, <span class="hljs-title">RCTBridgeDelegate</span> </span>{
···

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">applicationWillResignActive</span><span class="hljs-params">(<span class="hljs-number">_</span> application: UIApplication)</span></span> {
    <span class="hljs-keyword">self</span>.window?.isHidden = <span class="hljs-literal">true</span>;
  }

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">applicationDidBecomeActive</span><span class="hljs-params">(<span class="hljs-number">_</span> application: UIApplication)</span></span> {
    <span class="hljs-keyword">self</span>.window?.isHidden = <span class="hljs-literal">false</span>;
  }
}
</code></pre><h2 id="conclusiones">Conclusiones</h2>
<p>En Android con este método nos aseguramos que directamente, usando las aplicaciones del sistema, el usuario no puede tomar una captura de pantalla ni grabar la misma por lo que tenemos una solución de seguridad rápida y eficaz. Sin embargo, en iOS no tenemos los mismos mecanismos y tenemos que ingeniárnoslas para poder hacer algo parecido. Detectamos el cambio de estado de la aplicación y ocultamos la pantalla.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1611913769995/UFw80t56G.gif" alt="1611913397418.gif" /></p>
<p>Esto nos da pie a que, con un poco más de código, podamos tener pantallas customizadas para advertir al usuario de que lo que intenta realizar no está permitido. Es todo cuestión de imaginación y ponerlo en práctica.</p>
<p>Espero que te haya gustado. No vemos por las redes 🤪</p>
]]></content:encoded></item></channel></rss>