<?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: Armando Picón</title>
    <description>The latest articles on DEV Community by Armando Picón (@devpicon).</description>
    <link>https://dev.to/devpicon</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F46512%2F896b8426-d0aa-4c3a-8a68-46c9bbc6a2c4.jpg</url>
      <title>DEV Community: Armando Picón</title>
      <link>https://dev.to/devpicon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/devpicon"/>
    <language>en</language>
    <item>
      <title>The conversation around software development is regressing with AI</title>
      <dc:creator>Armando Picón</dc:creator>
      <pubDate>Fri, 28 Aug 2026 06:07:08 +0000</pubDate>
      <link>https://dev.to/devpicon/the-conversation-around-software-development-is-regressing-with-ai-4ook</link>
      <guid>https://dev.to/devpicon/the-conversation-around-software-development-is-regressing-with-ai-4ook</guid>
      <description>&lt;p&gt;One of the advantages — or possible disadvantages — of keeping up with the latest advances in technology is that, at some point, a revolution can become so constantly present on social media that you inevitably end up saturated with every post, video, or reel talking about it.&lt;/p&gt;

&lt;p&gt;I remember that this was one of the reasons why I initially refused to adopt AI in my workflows.&lt;/p&gt;

&lt;p&gt;However, time passed and, inevitably, I started using some tools for things as simple as iterating over ideas and thoughts that tend to come back to me while I study and learn new things.&lt;/p&gt;

&lt;p&gt;That helped me lower some of the barriers and mental blocks I had imposed on myself as a result of the information overload I experienced at the beginning.&lt;/p&gt;

&lt;p&gt;On top of that, I have also had the chance to filter the kind of content I consume much more carefully.&lt;/p&gt;

&lt;p&gt;For now, I follow a fairly limited number of creators because I know they actually put into practice the things they talk about. So I get the feeling that the amount of "bullshit" is considerably lower compared to other, more sensationalist creators.&lt;/p&gt;

&lt;p&gt;And it was precisely through that selection of content that I started noticing something I find quite interesting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Are we reinventing the wheel?
&lt;/h2&gt;

&lt;p&gt;Those who follow me across my different social networks know that I mainly work on mobile application development for Android and that, more recently, I have also become involved in multiplatform development thanks to Kotlin Multiplatform.&lt;/p&gt;

&lt;p&gt;However, while exploring the use of AI tools in software development more broadly, I have started to notice a kind of regression in the conversation around our profession.&lt;/p&gt;

&lt;p&gt;Not necessarily in the tools.&lt;/p&gt;

&lt;p&gt;Not necessarily in the capabilities we now have available.&lt;/p&gt;

&lt;p&gt;But in the way we talk about them.&lt;/p&gt;

&lt;p&gt;I keep coming across more and more content — including content from engineers working directly at companies building these kinds of tools — where it feels like we are rediscovering practices that have been part of software engineering for decades.&lt;/p&gt;

&lt;p&gt;Now we constantly talk about guardrails to prevent an agent from introducing incorrect code.&lt;/p&gt;

&lt;p&gt;But we have been using compilers, type systems, linters, static analysis, tests, and pipelines for years precisely to establish boundaries around which changes we consider acceptable.&lt;/p&gt;

&lt;p&gt;We can also design increasingly sophisticated workflows to improve the context a model receives, organize instructions, split work across agents, or help the model understand our codebase better.&lt;/p&gt;

&lt;p&gt;All of that has value.&lt;/p&gt;

&lt;p&gt;But if, after building that workflow, we still do not have a solid CI system capable of automatically verifying what the model has just produced, then we are still placing a large part of our trust in the assumption that the model understood our instructions correctly.&lt;/p&gt;

&lt;p&gt;And when we start talking about agent verification, the mechanism underneath often looks surprisingly familiar:&lt;/p&gt;

&lt;p&gt;the agent generates code, we run a series of checks, we observe what failed, and then we iterate again.&lt;/p&gt;

&lt;p&gt;The automation may be new.&lt;/p&gt;

&lt;p&gt;The speed may be new.&lt;/p&gt;

&lt;p&gt;The agent is definitely new.&lt;/p&gt;

&lt;p&gt;But the principle behind the verification is not necessarily new.&lt;/p&gt;

&lt;p&gt;And I think this is where it is worth stopping for a moment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Probabilism vs. determinism
&lt;/h2&gt;

&lt;p&gt;For the average user — and probably also for developers who have not spent much time studying what is happening behind these tools — AI can look like a black box.&lt;/p&gt;

&lt;p&gt;You give it an instruction and, almost magically, it starts generating a series of results that will probably satisfy what you are looking for.&lt;/p&gt;

&lt;p&gt;But if we look a little deeper, we find something much more interesting.&lt;/p&gt;

&lt;p&gt;Behind many of these tools there is an entire set of components that, working together, give us capabilities such as reasoning, memory, access to information, interaction with local or remote tools, execution of actions, and more.&lt;/p&gt;

&lt;p&gt;At the center of much of this machinery are language models.&lt;/p&gt;

&lt;p&gt;And behind these models are mathematical systems that, simplifying things quite a bit, estimate a probability distribution over what the next token in a sequence should be.&lt;/p&gt;

&lt;p&gt;That detail matters.&lt;/p&gt;

&lt;p&gt;We are working with probabilistic systems.&lt;/p&gt;

&lt;p&gt;That means we should not assume that the exact same input will necessarily produce the exact same output.&lt;/p&gt;

&lt;p&gt;On the other end, we have deterministic systems.&lt;/p&gt;

&lt;p&gt;In other words, systems where, given the same conditions and the same input data, we expect to obtain the same result.&lt;/p&gt;

&lt;p&gt;A simple arithmetic operation is probably the most obvious example.&lt;/p&gt;

&lt;p&gt;2 + 2 will still be 4 no matter how many times we run the operation.&lt;/p&gt;

&lt;p&gt;And this is exactly where a paradox appears that I find particularly interesting:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The more probabilistic the way we generate code becomes, the more we need deterministic mechanisms to verify whether that code is acceptable.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Back to software development
&lt;/h2&gt;

&lt;p&gt;A few lines above, I mentioned that I still see and hear content creators related to software development talking about how much AI is changing our profession.&lt;/p&gt;

&lt;p&gt;And yes, many things are changing.&lt;/p&gt;

&lt;p&gt;The speed at which we can produce code is changing.&lt;/p&gt;

&lt;p&gt;The number of alternatives we can explore is changing.&lt;/p&gt;

&lt;p&gt;The way we interact with our tools is changing.&lt;/p&gt;

&lt;p&gt;Even the role developers play in some parts of the process is changing.&lt;/p&gt;

&lt;p&gt;But if we deliberately abstract away many of those differences, there is one fundamental change that is much easier to observe:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We can delegate code generation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before, that code was written primarily by a human.&lt;/p&gt;

&lt;p&gt;Today, an increasingly large portion of it can be generated by an AI model.&lt;/p&gt;

&lt;p&gt;Of course, someone will say that I am oversimplifying things.&lt;/p&gt;

&lt;p&gt;And yes.&lt;/p&gt;

&lt;p&gt;I am.&lt;/p&gt;

&lt;p&gt;Deliberately.&lt;/p&gt;

&lt;p&gt;Because that simplification allows us to observe something important: changing who — or what — generates the code does not eliminate the mechanisms we used to verify it.&lt;/p&gt;

&lt;p&gt;Quite the opposite.&lt;/p&gt;

&lt;p&gt;For decades, we have been building fairly deterministic mechanisms that help us reduce uncertainty and verify certain properties of the software we produce.&lt;/p&gt;

&lt;p&gt;To name a few:&lt;/p&gt;

&lt;h3&gt;
  
  
  Compilation and type systems
&lt;/h3&gt;

&lt;p&gt;Compiler checks, type systems, and null-safety help us detect type errors, invalid contracts, and nullability issues before running the program.&lt;/p&gt;

&lt;h3&gt;
  
  
  Static Analysis
&lt;/h3&gt;

&lt;p&gt;Android Lint, Detekt, SwiftLint, or Ktlint help us detect certain known bugs, bad practices, and code smells through predefined rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Formatting
&lt;/h3&gt;

&lt;p&gt;Ktlint, Spotless, SwiftFormat, or Dart Format help us establish consistent and verifiable formatting rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unit Testing
&lt;/h3&gt;

&lt;p&gt;JUnit, XCTest, Kotlin Test, or Dart Test help us verify units of behavior in isolation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integration Testing
&lt;/h3&gt;

&lt;p&gt;It allows us to verify how different components work together: repositories, databases, network layers, services, persistence, and so on.&lt;/p&gt;

&lt;h3&gt;
  
  
  UI Testing
&lt;/h3&gt;

&lt;p&gt;Compose UI Test, Espresso, XCUITest, or Flutter Integration Test help us verify certain behaviors through the user interface.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code Coverage
&lt;/h3&gt;

&lt;p&gt;JaCoCo, Kover, or Xcode Coverage help us measure what portion of the code was executed during our tests.&lt;/p&gt;

&lt;p&gt;Coverage does not prove quality by itself. Having 100% coverage does not necessarily mean having good tests, much less having a bug-free product. But it is still a measurable signal that can be part of a broader verification system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Git Hooks
&lt;/h3&gt;

&lt;p&gt;Pre-commit and pre-push hooks allow us to run certain checks before our changes leave our machine.&lt;/p&gt;

&lt;p&gt;And we could keep going.&lt;/p&gt;

&lt;p&gt;CI pipelines.&lt;/p&gt;

&lt;p&gt;Contract testing.&lt;/p&gt;

&lt;p&gt;Mutation testing.&lt;/p&gt;

&lt;p&gt;Property-based testing.&lt;/p&gt;

&lt;p&gt;Dependency checks.&lt;/p&gt;

&lt;p&gt;Security checks.&lt;/p&gt;

&lt;p&gt;Reproducible builds.&lt;/p&gt;

&lt;p&gt;There are many more.&lt;/p&gt;

&lt;p&gt;And some of them will probably make much more sense than others depending on the product, platform, or team we are working with.&lt;/p&gt;

&lt;p&gt;The important thing is not to use all of them.&lt;/p&gt;

&lt;p&gt;The important thing is to understand the principle behind them.&lt;/p&gt;

&lt;p&gt;None of these practices guarantees, on its own, that our software is correct.&lt;/p&gt;

&lt;p&gt;That absolute guarantee has practically never existed.&lt;/p&gt;

&lt;p&gt;What they do is something far more useful:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;They systematically reduce our uncertainty.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  AI does not eliminate these practices
&lt;/h2&gt;

&lt;p&gt;It makes them more important.&lt;/p&gt;

&lt;p&gt;And this is precisely where I feel part of the current conversation is looking too much in one direction.&lt;/p&gt;

&lt;p&gt;We can spend hours improving our prompts.&lt;/p&gt;

&lt;p&gt;We can discuss how to build better context files.&lt;/p&gt;

&lt;p&gt;We can experiment with different ways to organize instructions.&lt;/p&gt;

&lt;p&gt;We can create agents, sub-agents, tools, MCPs, and increasingly complex workflows.&lt;/p&gt;

&lt;p&gt;In fact, we should learn how to work with all of that properly.&lt;/p&gt;

&lt;p&gt;But if our strategy ultimately depends exclusively on the model "understanding" what we want and producing correct code, then we are still playing with prompts.&lt;/p&gt;

&lt;p&gt;We are still trying to increase the probability of getting a good answer.&lt;/p&gt;

&lt;p&gt;And that is not necessarily bad.&lt;/p&gt;

&lt;p&gt;The problem starts when we confuse improving that probability with verifying that the result is correct.&lt;/p&gt;

&lt;p&gt;They are different problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Probabilistic generation, deterministic verification
&lt;/h2&gt;

&lt;p&gt;I think we can use a fairly simple framework here to think about how to incorporate AI into our development processes.&lt;/p&gt;

&lt;p&gt;It is not trying to invent anything new.&lt;/p&gt;

&lt;p&gt;In fact, that is exactly the point.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Generate
&lt;/h3&gt;

&lt;p&gt;We allow the model to do the things these tools can be extremely useful for:&lt;/p&gt;

&lt;p&gt;exploring alternatives, writing code, performing refactors, proposing implementations, navigating our codebase, or even operating directly on our repository.&lt;/p&gt;

&lt;p&gt;At this stage, we accept the probabilistic nature of the process.&lt;/p&gt;

&lt;p&gt;We are not trying to eliminate it.&lt;/p&gt;

&lt;p&gt;The model proposes.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Constrain
&lt;/h3&gt;

&lt;p&gt;Then we make the generated code face contracts defined by us.&lt;/p&gt;

&lt;p&gt;The compiler.&lt;/p&gt;

&lt;p&gt;The type system.&lt;/p&gt;

&lt;p&gt;Null-safety.&lt;/p&gt;

&lt;p&gt;Formatting.&lt;/p&gt;

&lt;p&gt;Linting.&lt;/p&gt;

&lt;p&gt;Static analysis.&lt;/p&gt;

&lt;p&gt;Architectural rules.&lt;/p&gt;

&lt;p&gt;API contracts.&lt;/p&gt;

&lt;p&gt;Whenever a constraint can be turned into an executable rule, we gain the ability to stop depending on the model "remembering" it.&lt;/p&gt;

&lt;p&gt;The system simply enforces it.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Verify
&lt;/h3&gt;

&lt;p&gt;Then we verify behavior.&lt;/p&gt;

&lt;p&gt;Unit tests.&lt;/p&gt;

&lt;p&gt;Integration tests.&lt;/p&gt;

&lt;p&gt;UI tests.&lt;/p&gt;

&lt;p&gt;Contract tests.&lt;/p&gt;

&lt;p&gt;Property-based tests.&lt;/p&gt;

&lt;p&gt;Whatever mechanisms make sense for the problem we are solving.&lt;/p&gt;

&lt;p&gt;At this point, we stop asking whether the code "looks correct."&lt;/p&gt;

&lt;p&gt;We start asking whether it satisfies concrete properties we can verify.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Decide
&lt;/h3&gt;

&lt;p&gt;Finally, we establish the conditions under which we accept that change.&lt;/p&gt;

&lt;p&gt;Build.&lt;/p&gt;

&lt;p&gt;Tests.&lt;/p&gt;

&lt;p&gt;Quality gates.&lt;/p&gt;

&lt;p&gt;Security checks.&lt;/p&gt;

&lt;p&gt;Code review.&lt;/p&gt;

&lt;p&gt;Human approval when appropriate.&lt;/p&gt;

&lt;p&gt;Only after going through those verifications do we allow the code to move forward.&lt;/p&gt;

&lt;p&gt;Generate.&lt;/p&gt;

&lt;p&gt;Constrain.&lt;/p&gt;

&lt;p&gt;Verify.&lt;/p&gt;

&lt;p&gt;Decide.&lt;/p&gt;

&lt;p&gt;In reality, we are not doing something radically different from what we were already doing.&lt;/p&gt;

&lt;p&gt;We are changing the entity that generates part of the code and dramatically increasing the speed at which it can do so.&lt;/p&gt;

&lt;p&gt;And precisely because of that, we need to strengthen the rest of the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what is a guardrail, really?
&lt;/h2&gt;

&lt;p&gt;This is where the conversation becomes particularly interesting.&lt;/p&gt;

&lt;p&gt;A guardrail does not have to be a particularly clever instruction inside a prompt.&lt;/p&gt;

&lt;p&gt;It can simply be a rule the model cannot ignore.&lt;/p&gt;

&lt;p&gt;The model can forget that we have a particular formatting convention.&lt;/p&gt;

&lt;p&gt;Ktlint does not.&lt;/p&gt;

&lt;p&gt;The model can incorrectly infer a type contract.&lt;/p&gt;

&lt;p&gt;The compiler does not negotiate with it.&lt;/p&gt;

&lt;p&gt;The model can convince us, with a perfectly reasonable explanation, that its implementation works.&lt;/p&gt;

&lt;p&gt;A failing test has a very different opinion.&lt;/p&gt;

&lt;p&gt;Of course, we can now use agents to execute all of these mechanisms, interpret the results, and even automatically fix whatever failed.&lt;/p&gt;

&lt;p&gt;That does represent a new and extremely interesting capability.&lt;/p&gt;

&lt;p&gt;But we are building that capability on top of principles we already knew.&lt;/p&gt;

&lt;p&gt;We should not confuse a new layer of automation with the emergence of a new discipline.&lt;/p&gt;

&lt;p&gt;It is still software engineering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stop just playing with prompts
&lt;/h2&gt;

&lt;p&gt;I have nothing against prompts.&lt;/p&gt;

&lt;p&gt;Nor against context engineering, agents, or increasingly sophisticated workflows.&lt;/p&gt;

&lt;p&gt;Quite the opposite.&lt;/p&gt;

&lt;p&gt;They are tools I am progressively incorporating into my own work, and I find them extremely interesting.&lt;/p&gt;

&lt;p&gt;But I think there is an important difference between improving the probability that a model will produce correct code and building a system capable of rejecting it when it does not.&lt;/p&gt;

&lt;p&gt;The first strategy tries to get a better answer.&lt;/p&gt;

&lt;p&gt;The second reduces our dependence on the answer being correct in the first place.&lt;/p&gt;

&lt;p&gt;And if I can choose where to place my trust, I would rather place it in what I can verify.&lt;/p&gt;

&lt;p&gt;Maybe that is one of the most interesting opportunities AI is giving us.&lt;/p&gt;

&lt;p&gt;If we increasingly need to spend less time manually writing every single line of code, then we can invest part of that time in building better contracts, better tests, better pipelines, and better verification systems.&lt;/p&gt;

&lt;p&gt;Not to stop AI from working.&lt;/p&gt;

&lt;p&gt;But precisely so that we can trust more of what it produces.&lt;/p&gt;

&lt;p&gt;AI is dramatically changing who can generate code, how much code we can generate, and how quickly we can do it.&lt;/p&gt;

&lt;p&gt;But it has not eliminated the engineering we need to decide which code deserves to reach production.&lt;/p&gt;

&lt;p&gt;And maybe part of the conversation around software development is regressing precisely because we are too fascinated by this new way of generating code and are forgetting everything we had already learned about how to verify it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The more probabilistic software generation becomes, the more deterministic the process that decides whether we accept the result should be.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>mobile</category>
      <category>software</category>
    </item>
    <item>
      <title>La conversación sobre desarrollo de software está involucionando con la IA</title>
      <dc:creator>Armando Picón</dc:creator>
      <pubDate>Fri, 28 Aug 2026 05:53:33 +0000</pubDate>
      <link>https://dev.to/devpicon/la-conversacion-sobre-desarrollo-de-software-esta-involucionando-con-la-ia-1im9</link>
      <guid>https://dev.to/devpicon/la-conversacion-sobre-desarrollo-de-software-esta-involucionando-con-la-ia-1im9</guid>
      <description>&lt;p&gt;Una de las ventajas - o posibles desventajas - de estar al tanto de los últimos avances de la tecnología es que, llegado cierto punto, alguna revolución puede estar presente de forma tan continua en las redes sociales que inevitablemente uno termina saturándose con cada post, video o reel que habla del tema.&lt;/p&gt;

&lt;p&gt;Recuerdo que esa fue una de las razones por las que inicialmente me negué a adoptar la IA en mis flujos de trabajo.&lt;/p&gt;

&lt;p&gt;Sin embargo, el tiempo pasó e inevitablemente comencé a emplear algunas herramientas para cosas tan sencillas como iterar sobre ideas y pensamientos que suelen ser recurrentes en mí mientras voy estudiando y aprendiendo cosas nuevas.&lt;/p&gt;

&lt;p&gt;De esta forma, empecé a bajar las barreras y bloqueos mentales que yo mismo me había impuesto producto de la saturación de información que había experimentado en un inicio.&lt;/p&gt;

&lt;p&gt;Adicionalmente, he tenido la oportunidad de filtrar bastante la clase de contenido que consumo.&lt;/p&gt;

&lt;p&gt;Por el momento sigo a un número acotado de creadores porque sé que realmente ponen en práctica las cosas que divulgan. Entonces, tengo la sensación de que el nivel de "chamullo" es bastante menor en comparación con otros creadores más sensacionalistas.&lt;/p&gt;

&lt;p&gt;Y justamente a partir de esa selección de contenido comencé a notar algo que me resulta bastante curioso.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Estamos reinventando la rueda?
&lt;/h2&gt;

&lt;p&gt;Para quienes me siguen en mis diferentes redes sociales, saben que me dedico principalmente al desarrollo de aplicaciones móviles para Android y que, recientemente, también me he involucrado en el desarrollo multiplataforma gracias a Kotlin Multiplatform.&lt;/p&gt;

&lt;p&gt;Sin embargo, explorando el uso de herramientas de IA en el desarrollo de software en general, he comenzado a observar una especie de involución en la conversación alrededor de nuestra profesión.&lt;/p&gt;

&lt;p&gt;No necesariamente en las herramientas.&lt;/p&gt;

&lt;p&gt;No necesariamente en las capacidades que tenemos disponibles.&lt;/p&gt;

&lt;p&gt;Sino en la manera en que hablamos sobre ellas.&lt;/p&gt;

&lt;p&gt;Cada vez encuentro más contenido - incluso proveniente de ingenieros que trabajan directamente en empresas que están construyendo este tipo de herramientas - donde pareciera que estamos redescubriendo prácticas que llevan décadas formando parte de la ingeniería de software.&lt;/p&gt;

&lt;p&gt;Ahora hablamos constantemente de guardrails para evitar que un agente introduzca código incorrecto.&lt;/p&gt;

&lt;p&gt;Pero llevamos años utilizando compiladores, sistemas de tipos, linters, static analysis, tests y pipelines precisamente para establecer límites sobre qué cambios consideramos aceptables.&lt;/p&gt;

&lt;p&gt;También podemos diseñar workflows cada vez más sofisticados para mejorar el contexto que recibe un modelo, organizar instrucciones, dividir trabajo entre agentes o conseguir que el modelo entienda mejor nuestro codebase.&lt;/p&gt;

&lt;p&gt;Todo eso tiene valor.&lt;/p&gt;

&lt;p&gt;Pero si después de construir ese workflow no tenemos un CI sólido capaz de verificar automáticamente lo que acaba de producir, seguimos depositando gran parte de nuestra confianza en que el modelo haya entendido correctamente nuestras instrucciones.&lt;/p&gt;

&lt;p&gt;Y cuando comenzamos a hablar de agent verification, muchas veces el mecanismo que encontramos debajo resulta sorprendentemente familiar:&lt;/p&gt;

&lt;p&gt;el agente genera código, ejecutamos una serie de checks, observamos qué falló y volvemos a iterar.&lt;/p&gt;

&lt;p&gt;La automatización puede ser nueva.&lt;/p&gt;

&lt;p&gt;La velocidad puede ser nueva.&lt;/p&gt;

&lt;p&gt;El agente definitivamente es nuevo.&lt;/p&gt;

&lt;p&gt;Pero el principio detrás de la verificación no necesariamente lo es.&lt;/p&gt;

&lt;p&gt;Y creo que aquí vale la pena detenernos un momento.&lt;/p&gt;

&lt;h2&gt;
  
  
  Probabilismo vs. determinismo
&lt;/h2&gt;

&lt;p&gt;Para el usuario común - y probablemente también para desarrolladores que no se han detenido demasiado a estudiar qué ocurre detrás de estas herramientas - la IA puede parecer una caja negra.&lt;/p&gt;

&lt;p&gt;Le entregas una instrucción y mágicamente comienza a generar una serie de resultados que probablemente satisfagan lo que estás buscando.&lt;/p&gt;

&lt;p&gt;Pero si observamos un poco más abajo encontramos algo bastante más interesante.&lt;/p&gt;

&lt;p&gt;Detrás de muchas de estas herramientas existe todo un conjunto de componentes que, funcionando en conjunto, nos permiten contar con capacidades de razonamiento, memoria, acceso a información, interacción con herramientas locales o remotas, ejecución de acciones, entre otras cosas.&lt;/p&gt;

&lt;p&gt;En el centro de buena parte de este andamiaje se encuentran los modelos de lenguaje.&lt;/p&gt;

&lt;p&gt;Y detrás de estos modelos existen sistemas matemáticos que, simplificando bastante, van estimando una distribución de probabilidades sobre cuál debería ser el siguiente token dentro de una secuencia.&lt;/p&gt;

&lt;p&gt;Ese detalle importa.&lt;/p&gt;

&lt;p&gt;Estamos trabajando con sistemas probabilísticos.&lt;/p&gt;

&lt;p&gt;Eso significa que no deberíamos asumir que, ante una misma entrada, obtendremos necesariamente exactamente la misma salida.&lt;/p&gt;

&lt;p&gt;En el otro extremo tenemos los sistemas determinísticos.&lt;/p&gt;

&lt;p&gt;Es decir, aquellos en los que, dadas las mismas condiciones y los mismos datos de entrada, esperamos obtener el mismo resultado.&lt;/p&gt;

&lt;p&gt;Una operación aritmética simple probablemente sea el ejemplo más evidente.&lt;/p&gt;

&lt;p&gt;2 + 2 seguirá siendo 4 independientemente de cuántas veces ejecutemos la operación.&lt;/p&gt;

&lt;p&gt;Y es justamente aquí donde aparece una paradoja que me parece particularmente interesante:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cuanto más probabilística se vuelve la forma en que generamos código, más necesitamos mecanismos determinísticos para verificar si ese código es aceptable.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Volviendo al desarrollo de software
&lt;/h2&gt;

&lt;p&gt;Mencionaba líneas arriba que sigo viendo y escuchando a creadores de contenido relacionados con el desarrollo de software hablar de cuánto está cambiando la incorporación de la IA en nuestra profesión.&lt;/p&gt;

&lt;p&gt;Y sí, están cambiando muchas cosas.&lt;/p&gt;

&lt;p&gt;Está cambiando la velocidad con la que podemos producir código.&lt;/p&gt;

&lt;p&gt;Está cambiando la cantidad de alternativas que podemos explorar.&lt;/p&gt;

&lt;p&gt;Está cambiando la forma en la que interactuamos con nuestras herramientas.&lt;/p&gt;

&lt;p&gt;Está cambiando incluso el rol que desempeña el desarrollador dentro de algunas partes del proceso.&lt;/p&gt;

&lt;p&gt;Pero si abstraemos deliberadamente muchas de esas diferencias, hay un cambio fundamental mucho más sencillo de observar:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Podemos delegar la generación del código.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Antes ese código era escrito principalmente por un ser humano.&lt;/p&gt;

&lt;p&gt;Hoy una proporción cada vez mayor puede ser generada por un modelo de IA.&lt;/p&gt;

&lt;p&gt;Claro, no faltará quien diga que estoy sobresimplificando las cosas.&lt;/p&gt;

&lt;p&gt;Y efectivamente lo estoy haciendo.&lt;/p&gt;

&lt;p&gt;Deliberadamente.&lt;/p&gt;

&lt;p&gt;Porque esa simplificación permite observar algo importante: cambiar quién - o qué - genera el código no elimina los mecanismos que utilizábamos para verificarlo.&lt;/p&gt;

&lt;p&gt;Todo lo contrario.&lt;/p&gt;

&lt;p&gt;Durante décadas hemos construido mecanismos bastante determinísticos que nos permiten reducir incertidumbre y verificar determinadas propiedades del software que producimos.&lt;/p&gt;

&lt;p&gt;Por mencionar algunos:&lt;/p&gt;

&lt;h3&gt;
  
  
  Compilación y sistemas de tipos
&lt;/h3&gt;

&lt;p&gt;Compiler checks, type systems y null-safety nos permiten detectar errores de tipos, contratos inválidos y problemas de nullability antes de ejecutar el programa.&lt;/p&gt;

&lt;h3&gt;
  
  
  Static Analysis
&lt;/h3&gt;

&lt;p&gt;Android Lint, Detekt, SwiftLint o Ktlint nos permiten detectar determinados bugs conocidos, malas prácticas y code smells mediante reglas previamente definidas.&lt;/p&gt;

&lt;h3&gt;
  
  
  Formatting
&lt;/h3&gt;

&lt;p&gt;Ktlint, Spotless, SwiftFormat o Dart Format nos permiten establecer reglas de formato consistentes y verificables.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unit Testing
&lt;/h3&gt;

&lt;p&gt;JUnit, XCTest, Kotlin Test o Dart Test nos permiten comprobar unidades de comportamiento de manera aislada.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integration Testing
&lt;/h3&gt;

&lt;p&gt;Nos permite verificar cómo distintos componentes colaboran entre sí: repositories, bases de datos, network layers, servicios, persistencia, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  UI Testing
&lt;/h3&gt;

&lt;p&gt;Compose UI Test, Espresso, XCUITest o Flutter Integration Test nos permiten verificar determinados comportamientos desde la interfaz.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code Coverage
&lt;/h3&gt;

&lt;p&gt;JaCoCo, Kover o Xcode Coverage nos permiten medir qué porción del código fue ejecutada durante nuestras pruebas.&lt;/p&gt;

&lt;p&gt;Coverage no demuestra calidad por sí solo. Tener 100 % de coverage no significa necesariamente contar con buenos tests ni mucho menos con un producto libre de errores. Pero sí constituye una señal medible que puede formar parte de un sistema de verificación mayor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Git Hooks
&lt;/h3&gt;

&lt;p&gt;Pre-commit y pre-push hooks nos permiten ejecutar determinadas verificaciones antes de que nuestros cambios abandonen nuestra máquina.&lt;/p&gt;

&lt;p&gt;Y podríamos seguir.&lt;/p&gt;

&lt;p&gt;CI pipelines.&lt;/p&gt;

&lt;p&gt;Contract testing.&lt;/p&gt;

&lt;p&gt;Mutation testing.&lt;/p&gt;

&lt;p&gt;Property-based testing.&lt;/p&gt;

&lt;p&gt;Dependency checks.&lt;/p&gt;

&lt;p&gt;Security checks.&lt;/p&gt;

&lt;p&gt;Reproducible builds.&lt;/p&gt;

&lt;p&gt;Son muchas más.&lt;/p&gt;

&lt;p&gt;Y probablemente algunas tengan mucho más sentido que otras dependiendo del producto, plataforma o equipo en el que estemos trabajando.&lt;/p&gt;

&lt;p&gt;Lo importante no es utilizar todas.&lt;/p&gt;

&lt;p&gt;Lo importante es entender el principio que existe detrás.&lt;/p&gt;

&lt;p&gt;Ninguna de estas prácticas garantiza por sí sola que nuestro software sea correcto.&lt;/p&gt;

&lt;p&gt;Esa garantía absoluta prácticamente nunca ha existido.&lt;/p&gt;

&lt;p&gt;Lo que hacen es algo mucho más útil:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reducen sistemáticamente nuestra incertidumbre.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  La IA no elimina estas prácticas
&lt;/h2&gt;

&lt;p&gt;Las vuelve más importantes.&lt;/p&gt;

&lt;p&gt;Y es precisamente aquí donde tengo la sensación de que parte de la conversación actual está mirando demasiado hacia un solo lado.&lt;/p&gt;

&lt;p&gt;Podemos dedicar horas a mejorar nuestros prompts.&lt;/p&gt;

&lt;p&gt;Podemos discutir cómo construir mejores archivos de contexto.&lt;/p&gt;

&lt;p&gt;Podemos experimentar con diferentes maneras de organizar instrucciones.&lt;/p&gt;

&lt;p&gt;Podemos crear agentes, subagentes, herramientas, MCPs y workflows cada vez más complejos.&lt;/p&gt;

&lt;p&gt;De hecho, deberíamos aprender a trabajar correctamente con todo eso.&lt;/p&gt;

&lt;p&gt;Pero si nuestra estrategia termina dependiendo exclusivamente de que el modelo "entienda bien" lo que queremos y produzca código correcto, seguimos jugando a prompts.&lt;/p&gt;

&lt;p&gt;Seguimos intentando aumentar la probabilidad de obtener una buena respuesta.&lt;/p&gt;

&lt;p&gt;Y eso no es necesariamente malo.&lt;/p&gt;

&lt;p&gt;El problema aparece cuando confundimos mejorar esa probabilidad con verificar que el resultado sea correcto.&lt;/p&gt;

&lt;p&gt;Son problemas distintos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generación probabilística, verificación determinística
&lt;/h2&gt;

&lt;p&gt;Creo que aquí podemos utilizar un marco bastante sencillo para pensar cómo incorporar IA dentro de nuestros procesos de desarrollo.&lt;/p&gt;

&lt;p&gt;No pretende inventar nada nuevo.&lt;/p&gt;

&lt;p&gt;De hecho, justamente ese es el punto.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Generar
&lt;/h3&gt;

&lt;p&gt;Permitimos que el modelo haga aquello en lo que estas herramientas pueden resultar extremadamente útiles:&lt;/p&gt;

&lt;p&gt;explorar alternativas, escribir código, realizar refactors, proponer implementaciones, navegar nuestro codebase o incluso operar directamente sobre nuestro repositorio.&lt;/p&gt;

&lt;p&gt;En esta parte aceptamos el probabilismo.&lt;/p&gt;

&lt;p&gt;No intentamos eliminarlo.&lt;/p&gt;

&lt;p&gt;El modelo propone.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Restringir
&lt;/h3&gt;

&lt;p&gt;Luego hacemos que el código generado se enfrente a contratos definidos por nosotros.&lt;/p&gt;

&lt;p&gt;El compilador.&lt;/p&gt;

&lt;p&gt;El sistema de tipos.&lt;/p&gt;

&lt;p&gt;Null-safety.&lt;/p&gt;

&lt;p&gt;Formatting.&lt;/p&gt;

&lt;p&gt;Linting.&lt;/p&gt;

&lt;p&gt;Static analysis.&lt;/p&gt;

&lt;p&gt;Reglas arquitectónicas.&lt;/p&gt;

&lt;p&gt;Contratos de APIs.&lt;/p&gt;

&lt;p&gt;Siempre que una restricción pueda convertirse en una regla ejecutable, tenemos la posibilidad de dejar de depender de que el modelo la "recuerde".&lt;/p&gt;

&lt;p&gt;El sistema simplemente la hace cumplir.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Verificar
&lt;/h3&gt;

&lt;p&gt;Después verificamos el comportamiento.&lt;/p&gt;

&lt;p&gt;Unit tests.&lt;/p&gt;

&lt;p&gt;Integration tests.&lt;/p&gt;

&lt;p&gt;UI tests.&lt;/p&gt;

&lt;p&gt;Contract tests.&lt;/p&gt;

&lt;p&gt;Property-based tests.&lt;/p&gt;

&lt;p&gt;Los mecanismos que correspondan al problema que estamos resolviendo.&lt;/p&gt;

&lt;p&gt;En este punto dejamos de preguntarnos si el código "parece correcto".&lt;/p&gt;

&lt;p&gt;Comenzamos a preguntar si cumple propiedades concretas que podemos comprobar.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Decidir
&lt;/h3&gt;

&lt;p&gt;Finalmente establecemos las condiciones bajo las cuales aceptamos ese cambio.&lt;/p&gt;

&lt;p&gt;Build.&lt;/p&gt;

&lt;p&gt;Tests.&lt;/p&gt;

&lt;p&gt;Quality gates.&lt;/p&gt;

&lt;p&gt;Security checks.&lt;/p&gt;

&lt;p&gt;Revisión de código.&lt;/p&gt;

&lt;p&gt;Aprobación humana cuando corresponda.&lt;/p&gt;

&lt;p&gt;Solo después de atravesar esas verificaciones permitimos que el código avance.&lt;/p&gt;

&lt;p&gt;Generar.&lt;/p&gt;

&lt;p&gt;Restringir.&lt;/p&gt;

&lt;p&gt;Verificar.&lt;/p&gt;

&lt;p&gt;Decidir.&lt;/p&gt;

&lt;p&gt;En realidad, no estamos haciendo algo radicalmente distinto a lo que ya hacíamos.&lt;/p&gt;

&lt;p&gt;Estamos cambiando la entidad que genera parte del código y aumentando dramáticamente la velocidad con la que puede hacerlo.&lt;/p&gt;

&lt;p&gt;Y precisamente por eso necesitamos reforzar el resto del sistema.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entonces, ¿qué es realmente un guardrail?
&lt;/h2&gt;

&lt;p&gt;Aquí es donde la conversación se vuelve particularmente interesante.&lt;/p&gt;

&lt;p&gt;Un guardrail no tiene por qué ser una instrucción particularmente ingeniosa dentro de un prompt.&lt;/p&gt;

&lt;p&gt;Puede ser simplemente una regla que el modelo no puede ignorar.&lt;/p&gt;

&lt;p&gt;El modelo puede olvidar que tenemos una determinada convención de formato.&lt;/p&gt;

&lt;p&gt;Ktlint no.&lt;/p&gt;

&lt;p&gt;El modelo puede inferir incorrectamente un contrato de tipos.&lt;/p&gt;

&lt;p&gt;El compilador no negocia con él.&lt;/p&gt;

&lt;p&gt;El modelo puede convencernos con una explicación perfectamente razonable de que su implementación funciona.&lt;/p&gt;

&lt;p&gt;Un test que falla tiene una opinión bastante diferente.&lt;/p&gt;

&lt;p&gt;Por supuesto que ahora podemos utilizar agentes para ejecutar todos estos mecanismos, interpretar los resultados e incluso corregir automáticamente aquello que falló.&lt;/p&gt;

&lt;p&gt;Eso sí representa una capacidad nueva y extremadamente interesante.&lt;/p&gt;

&lt;p&gt;Pero estamos construyendo esa capacidad sobre principios que ya conocíamos.&lt;/p&gt;

&lt;p&gt;No deberíamos confundir una nueva capa de automatización con la aparición de una nueva disciplina.&lt;/p&gt;

&lt;p&gt;Sigue siendo ingeniería de software.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dejar de jugar solamente a prompts
&lt;/h2&gt;

&lt;p&gt;No tengo nada contra los prompts.&lt;/p&gt;

&lt;p&gt;Tampoco contra el context engineering, los agentes o los workflows cada vez más sofisticados.&lt;/p&gt;

&lt;p&gt;Todo lo contrario.&lt;/p&gt;

&lt;p&gt;Son herramientas que estoy incorporando progresivamente en mi propio trabajo y que me parecen tremendamente interesantes.&lt;/p&gt;

&lt;p&gt;Pero creo que existe una diferencia importante entre mejorar las probabilidades de que un modelo produzca código correcto y construir un sistema capaz de rechazarlo cuando no lo es.&lt;/p&gt;

&lt;p&gt;La primera estrategia intenta conseguir una mejor respuesta.&lt;/p&gt;

&lt;p&gt;La segunda reduce nuestra dependencia de que la respuesta sea correcta desde el principio.&lt;/p&gt;

&lt;p&gt;Y si puedo elegir dónde depositar mi confianza, prefiero hacerlo en aquello que puedo verificar.&lt;/p&gt;

&lt;p&gt;Quizá ahí está una de las oportunidades más interesantes que nos está dando la IA.&lt;/p&gt;

&lt;p&gt;Si cada vez necesitamos invertir menos tiempo en escribir manualmente cada línea de código, podemos invertir una parte de ese tiempo en construir mejores contratos, mejores tests, mejores pipelines y mejores sistemas de verificación.&lt;/p&gt;

&lt;p&gt;No para impedir que la IA trabaje.&lt;/p&gt;

&lt;p&gt;Sino precisamente para poder confiar más en aquello que produce.&lt;/p&gt;

&lt;p&gt;La IA está cambiando dramáticamente quién puede generar código, cuánto código podemos generar y a qué velocidad podemos hacerlo.&lt;/p&gt;

&lt;p&gt;Pero no eliminó la ingeniería que necesitamos para decidir qué código merece llegar a producción.&lt;/p&gt;

&lt;p&gt;Y quizá parte de la conversación alrededor del desarrollo de software está involucionando precisamente porque estamos demasiado fascinados con la nueva forma de generar código y estamos olvidando todo lo que ya habíamos aprendido sobre cómo verificarlo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cuanto más probabilística se vuelve la generación de software, más determinístico debería ser el proceso que decide si aceptamos el resultado.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>softwareengineering</category>
      <category>mobile</category>
    </item>
    <item>
      <title>🔐 Beyond SSL Pinning: mTLS, Backend Security &amp; Real-World Mobile Architecture (Part 3)</title>
      <dc:creator>Armando Picón</dc:creator>
      <pubDate>Mon, 04 May 2026 21:44:39 +0000</pubDate>
      <link>https://dev.to/devpicon/beyond-ssl-pinning-mtls-backend-security-real-world-mobile-architecture-part-3-oeb</link>
      <guid>https://dev.to/devpicon/beyond-ssl-pinning-mtls-backend-security-real-world-mobile-architecture-part-3-oeb</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwhpym0964quse1bniok1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwhpym0964quse1bniok1.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the previous parts, we explored SSL pinning across &lt;a href="https://dev.to/devpicon/ssl-pinning-in-mobile-apps-android-ios-practical-guide-trade-offs-2pk2"&gt;Android&lt;/a&gt; and &lt;a href="https://dev.to/devpicon/ssl-pinning-in-mobile-apps-android-ios-practical-guide-trade-offs-part-2-3p4m"&gt;iOS&lt;/a&gt;, including both certificate and public key approaches.&lt;/p&gt;

&lt;p&gt;But here’s the uncomfortable truth:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Even perfectly implemented pinning is not enough.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this final part, we move beyond the client and look at what truly defines a secure mobile architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mutual TLS (mTLS)&lt;/li&gt;
&lt;li&gt;Backend access control&lt;/li&gt;
&lt;li&gt;Defense in depth&lt;/li&gt;
&lt;li&gt;When mobile security actually fails in production&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧠 Why Pinning Is Not the Endgame
&lt;/h2&gt;

&lt;p&gt;Pinning protects the &lt;strong&gt;channel&lt;/strong&gt;, not the &lt;strong&gt;system&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✔ Prevents MITM attacks
&lt;/li&gt;
&lt;li&gt;❌ Does NOT prevent unauthorized API access
&lt;/li&gt;
&lt;li&gt;❌ Does NOT validate who is calling your backend
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your API is publicly exposed, anyone can still:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Postman
&lt;/li&gt;
&lt;li&gt;Reverse engineer your app
&lt;/li&gt;
&lt;li&gt;Replay requests
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the real question becomes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How do we ensure that only trusted clients can talk to our backend?&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🔴 Enter mTLS (Mutual TLS)
&lt;/h2&gt;

&lt;p&gt;Unlike standard TLS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server presents a certificate ✅
&lt;/li&gt;
&lt;li&gt;Client verifies server
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With &lt;strong&gt;mTLS&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server presents certificate ✅
&lt;/li&gt;
&lt;li&gt;Client presents certificate ✅
&lt;/li&gt;
&lt;li&gt;Both sides verify each other
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔐 mTLS Flow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client → Server: Hello
Server → Client: Certificate
Client → Server: Certificate (client identity)
Server → Client: Accept / Reject
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;✔ What mTLS Solves&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Strong client authentication&lt;/li&gt;
&lt;li&gt;Prevents unauthorized clients (e.g. Postman, curl)&lt;/li&gt;
&lt;li&gt;Removes reliance on API keys alone&lt;/li&gt;
&lt;li&gt;Can partially replace VPN in some architectures&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;⚠️ Why mTLS Is Rare in Mobile&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Because it’s operationally expensive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Securely storing client certificates on device is hard&lt;/li&gt;
&lt;li&gt;Certificates must be rotated&lt;/li&gt;
&lt;li&gt;Risk of extraction on rooted/jailbroken devices&lt;/li&gt;
&lt;li&gt;Complex provisioning process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 Most teams underestimate this complexity&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;🧩 Where mTLS Actually Makes Sense&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Use it when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enterprise apps (controlled devices)&lt;/li&gt;
&lt;li&gt;MDM-managed environments&lt;/li&gt;
&lt;li&gt;Internal tools&lt;/li&gt;
&lt;li&gt;High-security B2B systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avoid it for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public consumer apps&lt;/li&gt;
&lt;li&gt;Apps distributed via App Store / Play Store&lt;/li&gt;
&lt;li&gt;Large-scale unknown user bases&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;🧠 Real-World Alternative: Token-Based Security&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Instead of relying on transport-level identity:&lt;/p&gt;

&lt;p&gt;👉 Use &lt;strong&gt;application-level identity&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;App → HTTPS → API Gateway → Auth (JWT / OAuth) → Services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;✔ What This Solves&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;User authentication&lt;/li&gt;
&lt;li&gt;Fine-grained authorization&lt;/li&gt;
&lt;li&gt;Revocation&lt;/li&gt;
&lt;li&gt;Scalability&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;🧱 Defense in Depth (What Actually Works)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A realistic production setup looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Mobile App
   ↓
TLS (HTTPS)
   ↓
(Optional) Certificate Pinning
   ↓
API Gateway
   ↓
Auth Layer (JWT / OAuth)
   ↓
Rate Limiting / WAF
   ↓
Microservices
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;🔍 Common Mistakes in Mobile Security&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Let’s be blunt:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;❌ “We added SSL pinning, we’re secure”&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;No — you protected only the transport layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;❌ Hardcoding API keys in the app&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;These will be extracted. Always.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;❌ Trusting the client&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The client is always hostile. Assume compromise.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;❌ Ignoring backend validation&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Security belongs to the backend, not the app.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;🧠 When Mobile Security Fails&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Not because of TLS.&lt;/p&gt;

&lt;p&gt;But because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Poor authentication design&lt;/li&gt;
&lt;li&gt;Lack of rate limiting&lt;/li&gt;
&lt;li&gt;Missing monitoring&lt;/li&gt;
&lt;li&gt;Weak backend validation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;🧭 Practical Recommendations&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you’re building a modern mobile app:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start with HTTPS (mandatory)&lt;/li&gt;
&lt;li&gt;Add proper authentication (JWT / OAuth)&lt;/li&gt;
&lt;li&gt;Introduce API Gateway controls&lt;/li&gt;
&lt;li&gt;Add rate limiting &amp;amp; monitoring&lt;/li&gt;
&lt;li&gt;Consider pinning as an extra layer&lt;/li&gt;
&lt;li&gt;Evaluate mTLS only if you truly need it&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;🧠 Final Takeaway&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Security is not a feature — it’s a system.&lt;/p&gt;

&lt;p&gt;Pinning protects the connection.&lt;br&gt;
 mTLS protects the client identity.&lt;br&gt;
 Backend security protects your business.&lt;/p&gt;

&lt;p&gt;If you ignore the last one, the first two won’t save you.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;👋 Closing&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;At this point, you’ve seen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to implement pinning on Android&lt;/li&gt;
&lt;li&gt;How to implement it on iOS&lt;/li&gt;
&lt;li&gt;And where it actually fits in a real-world architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use it wisely.&lt;/p&gt;

&lt;p&gt;And more importantly — design beyond it.&lt;/p&gt;

</description>
      <category>mobile</category>
      <category>security</category>
      <category>backend</category>
      <category>api</category>
    </item>
    <item>
      <title>🔐 SSL Pinning in Mobile Apps: Android &amp; iOS (Practical Guide + Trade-offs) - Part 2</title>
      <dc:creator>Armando Picón</dc:creator>
      <pubDate>Mon, 04 May 2026 21:25:47 +0000</pubDate>
      <link>https://dev.to/devpicon/ssl-pinning-in-mobile-apps-android-ios-practical-guide-trade-offs-part-2-3p4m</link>
      <guid>https://dev.to/devpicon/ssl-pinning-in-mobile-apps-android-ios-practical-guide-trade-offs-part-2-3p4m</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9vf95rask2k78gf52d7f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9vf95rask2k78gf52d7f.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/devpicon/ssl-pinning-in-mobile-apps-android-ios-practical-guide-trade-offs-2pk2"&gt;Unlike Android&lt;/a&gt;, where libraries like OkHttp abstract much of the complexity, iOS takes a more low-level approach to networking and security.&lt;/p&gt;

&lt;p&gt;This means one thing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You have more control — but also more responsibility.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this second part, we’ll explore how SSL pinning is implemented in iOS using two different strategies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Certificate Pinning (&lt;code&gt;.cer&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Public Key Pinning (recommended for production)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both approaches achieve the same goal — trusting only your backend — but they differ significantly in terms of stability, maintainability, and real-world viability.&lt;/p&gt;

&lt;p&gt;We’ll also take a step back and look at the bigger picture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When pinning makes sense&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When it becomes a liability&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And how it fits into a broader mobile security strategy&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s dive in.&lt;/p&gt;

&lt;h2&gt;
  
  
  🍎 iOS Implementation
&lt;/h2&gt;

&lt;p&gt;iOS is more low-level. You’ll work with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;URLSession&lt;/li&gt;
&lt;li&gt;URLSessionDelegate&lt;/li&gt;
&lt;li&gt;Security.framework&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are two approaches:&lt;/p&gt;




&lt;h2&gt;
  
  
  🟢 Approach 1: Certificate Pinning with .cer
&lt;/h2&gt;

&lt;p&gt;This is typically the first approach developers encounter when implementing pinning on iOS.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔧 Steps
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Export your backend certificate as .cer&lt;/li&gt;
&lt;li&gt;Add it to your Xcode project&lt;/li&gt;
&lt;li&gt;Compare it at runtime&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  🧪 Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;PinningDelegate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;NSObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;URLSessionDelegate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;urlSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;URLSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;didReceive&lt;/span&gt; &lt;span class="nv"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;URLAuthenticationChallenge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nv"&gt;completionHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;@escaping&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;URLSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;AuthChallengeDisposition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;URLCredential&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="n"&gt;challenge&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;protectionSpace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;authenticationMethod&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;NSURLAuthenticationMethodServerTrust&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;serverTrust&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;challenge&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;protectionSpace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;serverTrust&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;serverCertificate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SecTrustGetCertificateAtIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverTrust&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;completionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancelAuthenticationChallenge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;serverCertData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SecCertificateCopyData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverCertificate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;

        &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;localCertPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Bundle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;forResource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"server"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;ofType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"cer"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;localCertData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;contentsOf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;fileURLWithPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;localCertPath&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;completionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancelAuthenticationChallenge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;serverCertData&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;localCertData&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;completionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;useCredential&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;URLCredential&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;trust&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;serverTrust&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;completionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancelAuthenticationChallenge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  ⚠️ Downside (critical)
&lt;/h3&gt;

&lt;p&gt;This approach is:&lt;/p&gt;

&lt;p&gt;💣 Fragile&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Certificates expire&lt;/li&gt;
&lt;li&gt;Renewal changes .cer&lt;/li&gt;
&lt;li&gt;Your app breaks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 You must release a new version of the app&lt;/p&gt;




&lt;h2&gt;
  
  
  🟡 Approach 2: Public Key Pinning (Recommended)
&lt;/h2&gt;

&lt;p&gt;Instead of comparing full certificates:&lt;/p&gt;

&lt;p&gt;👉 Compare public keys&lt;/p&gt;

&lt;p&gt;✔️ Advantages&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Survives certificate renewal&lt;/li&gt;
&lt;li&gt;More stable in production&lt;/li&gt;
&lt;li&gt;Equivalent to Android approach&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;🧪 Conceptual Example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;serverPublicKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SecCertificateCopyKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverCertificate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;localPublicKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SecCertificateCopyKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;localCertificate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Compare keys or their hashes&lt;/span&gt;
&lt;span class="c1"&gt;// ⚠️ Apple APIs here are verbose and require careful handling.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🧭 Better Option: Use a Library
&lt;/h3&gt;

&lt;p&gt;Instead of manual implementation, use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Alamofire (widely used networking library)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;evaluators&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ServerTrustEvaluating&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s"&gt;"api.yourservice.com"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PinnedCertificatesTrustEvaluator&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;manager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ServerTrustManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;evaluators&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;evaluators&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;serverTrustManager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🔍 What SSL Pinning DOES NOT Do
&lt;/h3&gt;

&lt;p&gt;Let’s be clear:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Covered by Pinning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Encrypt traffic&lt;/td&gt;
&lt;td&gt;✔ (via TLS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prevent MITM&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authenticate user&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Protect API access&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replace VPN&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;👉 You still need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JWT / OAuth&lt;/li&gt;
&lt;li&gt;API Gateway&lt;/li&gt;
&lt;li&gt;Rate limiting&lt;/li&gt;
&lt;li&gt;Backend security&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  ⚠️ Real-World Trade-offs
&lt;/h3&gt;

&lt;p&gt;Before adding pinning, ask yourself:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❗ Operational cost&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Certificate rotation becomes risky&lt;/li&gt;
&lt;li&gt;You need fallback pins&lt;/li&gt;
&lt;li&gt;You need monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;❗ Release dependency&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A backend change can break clients instantly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;❗ Debugging complexity&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Harder to inspect traffic (Charles Proxy, etc.)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🧠 When Should You Use It?
&lt;/h3&gt;

&lt;p&gt;Use pinning if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You build fintech / healthcare apps&lt;/li&gt;
&lt;li&gt;You operate in hostile network environments&lt;/li&gt;
&lt;li&gt;You have strong DevOps practices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avoid (or delay) if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You’re building a typical consumer app&lt;/li&gt;
&lt;li&gt;You don’t control backend infrastructure&lt;/li&gt;
&lt;li&gt;Your team lacks experience with cert rotation&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🧩 Recommended Architecture (No VPN)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Mobile App
   ↓
HTTPS (TLS)
   ↓
API Gateway
   ↓
Authentication (JWT / OAuth)
   ↓
Microservices
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Optional hardening:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Certificate Pinning 🔐&lt;/li&gt;
&lt;li&gt;WAF 🛡️&lt;/li&gt;
&lt;li&gt;Rate limiting 🚦&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🧠 Final Thoughts
&lt;/h3&gt;

&lt;p&gt;“SSL pinning” is often mentioned casually, but:&lt;/p&gt;

&lt;p&gt;👉 It’s not a silver bullet&lt;br&gt;
👉 It’s not a replacement for authentication&lt;br&gt;
👉 It’s not trivial to maintain&lt;/p&gt;

&lt;p&gt;Used correctly, it adds a strong extra layer of defense.&lt;/p&gt;

&lt;p&gt;Used blindly, it becomes a production risk.&lt;/p&gt;




&lt;h3&gt;
  
  
  👋 Closing
&lt;/h3&gt;

&lt;p&gt;If you’re working with Kotlin Multiplatform or shared logic, keep in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pinning is platform-specific&lt;/li&gt;
&lt;li&gt;You’ll need separate implementations for Android and iOS&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ios</category>
      <category>mobile</category>
      <category>networking</category>
      <category>security</category>
    </item>
    <item>
      <title>🔐 SSL Pinning in Mobile Apps: Android &amp; iOS (Practical Guide + Trade-offs) - Part 1</title>
      <dc:creator>Armando Picón</dc:creator>
      <pubDate>Mon, 04 May 2026 21:20:13 +0000</pubDate>
      <link>https://dev.to/devpicon/ssl-pinning-in-mobile-apps-android-ios-practical-guide-trade-offs-2pk2</link>
      <guid>https://dev.to/devpicon/ssl-pinning-in-mobile-apps-android-ios-practical-guide-trade-offs-2pk2</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw6cehy4m4u9cj6qp2lnj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw6cehy4m4u9cj6qp2lnj.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When building mobile apps that consume APIs over the internet, HTTPS is &lt;strong&gt;mandatory&lt;/strong&gt;—but sometimes it’s not enough.&lt;/p&gt;

&lt;p&gt;If your app handles sensitive data (finance, health, enterprise), you might want to go one step further:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Certificate Pinning (aka “SSL pinning”)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This article explains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;What SSL pinning actually is (and what it isn’t)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How to implement it in &lt;strong&gt;Android&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a second part, these topics will be covered: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;How to implement it in &lt;strong&gt;iOS&lt;/strong&gt; (both &lt;code&gt;.cer&lt;/code&gt; and Public Key approaches)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The real trade-offs nobody tells you&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧠 What is SSL Pinning (really)?
&lt;/h2&gt;

&lt;p&gt;Despite the name, modern apps use &lt;strong&gt;TLS&lt;/strong&gt;, not SSL.&lt;/p&gt;

&lt;p&gt;👉 The correct term is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Certificate Pinning&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;or &lt;strong&gt;TLS Pinning&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But “SSL pinning” is still widely used.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔐 Default HTTPS behavior
&lt;/h3&gt;

&lt;p&gt;By default, your app trusts &lt;strong&gt;any valid certificate&lt;/strong&gt; signed by trusted Certificate Authorities (CAs).&lt;/p&gt;

&lt;p&gt;That means:&lt;br&gt;
App → HTTPS → Server (valid cert) → OK&lt;/p&gt;
&lt;h3&gt;
  
  
  🚨 The problem
&lt;/h3&gt;

&lt;p&gt;If an attacker installs a malicious certificate (e.g. on public WiFi), they could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Intercept traffic&lt;/li&gt;
&lt;li&gt;Decrypt requests&lt;/li&gt;
&lt;li&gt;Act as a proxy (MITM attack)&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  🔐 What Pinning Changes
&lt;/h2&gt;

&lt;p&gt;Instead of trusting all valid certs:&lt;/p&gt;

&lt;p&gt;👉 Your app trusts &lt;strong&gt;only a specific certificate or public key&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If it doesn’t match → ❌ connection rejected&lt;/p&gt;



&lt;p&gt;In Android, SSL pinning can be implemented either at the HTTP client level (e.g., OkHttp) or at the platform level using Network Security Config.&lt;/p&gt;
&lt;h2&gt;
  
  
  📱 Android Implementation (OkHttp)
&lt;/h2&gt;

&lt;p&gt;Android makes this relatively straightforward thanks to OkHttp.&lt;/p&gt;
&lt;h3&gt;
  
  
  ✔️ Public Key Pinning (recommended)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;certificatePinner&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CertificatePinner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api.yourservice.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"sha256/AAAAAAAAAAAAAAAAAAAA..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OkHttpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;certificatePinner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;certificatePinner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  🧪 How to get the SHA-256 hash
&lt;/h3&gt;

&lt;p&gt;You can extract it using OpenSSL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl s_client &lt;span class="nt"&gt;-connect&lt;/span&gt; api.yourservice.com:443 &lt;span class="nt"&gt;-servername&lt;/span&gt; api.yourservice.com &lt;span class="se"&gt;\&lt;/span&gt;
  | openssl x509 &lt;span class="nt"&gt;-pubkey&lt;/span&gt; &lt;span class="nt"&gt;-noout&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | openssl pkey &lt;span class="nt"&gt;-pubin&lt;/span&gt; &lt;span class="nt"&gt;-outform&lt;/span&gt; der &lt;span class="se"&gt;\&lt;/span&gt;
  | openssl dgst &lt;span class="nt"&gt;-sha256&lt;/span&gt; &lt;span class="nt"&gt;-binary&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | openssl enc &lt;span class="nt"&gt;-base64&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🧭 Alternative on Android: Network Security Config (Platform-Level Pinning)
&lt;/h2&gt;

&lt;p&gt;So far, we’ve implemented SSL pinning using OkHttp, which gives us fine-grained control at the HTTP client level.&lt;/p&gt;

&lt;p&gt;However, Android also provides a &lt;strong&gt;platform-level approach&lt;/strong&gt;: &lt;code&gt;Network Security Config&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Instead of configuring pinning in code, you can declare it in XML and apply it globally to your app.&lt;/p&gt;




&lt;h3&gt;
  
  
  🔧 Example Configuration
&lt;/h3&gt;

&lt;p&gt;Create a file under:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;res/xml/network_security_config.xml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;network-security-config&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;domain-config&lt;/span&gt; &lt;span class="na"&gt;cleartextTrafficPermitted=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;domain&lt;/span&gt; &lt;span class="na"&gt;includeSubdomains=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;api.yourservice.com&lt;span class="nt"&gt;&amp;lt;/domain&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;pin-set&lt;/span&gt; &lt;span class="na"&gt;expiration=&lt;/span&gt;&lt;span class="s"&gt;"2026-05-09"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;pin&lt;/span&gt; &lt;span class="na"&gt;digest=&lt;/span&gt;&lt;span class="s"&gt;"SHA-256"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;YOUR_BASE64_SHA256_PIN&lt;span class="nt"&gt;&amp;lt;/pin&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/pin-set&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/domain-config&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/network-security-config&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then reference it in your AndroidManifest.xml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;application&lt;/span&gt;
    &lt;span class="na"&gt;android:networkSecurityConfig=&lt;/span&gt;&lt;span class="s"&gt;"@xml/network_security_config"&lt;/span&gt;
    &lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;🧠 How It Works&lt;/p&gt;

&lt;p&gt;This configuration tells Android:&lt;/p&gt;

&lt;p&gt;Only trust connections to this domain if the server’s certificate matches the pinned public key.&lt;/p&gt;

&lt;p&gt;Unlike OkHttp’s CertificatePinner, this operates at the platform level, not just within a specific HTTP client.&lt;/p&gt;




&lt;h3&gt;
  
  
  ⚖️ OkHttp vs Network Security Config
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;th&gt;Control&lt;/th&gt;
&lt;th&gt;Flexibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OkHttp CertificatePinner&lt;/td&gt;
&lt;td&gt;Per client&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network Security Config&lt;/td&gt;
&lt;td&gt;App-wide&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  ⚠️ Trade-offs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Still subject to certificate rotation issues&lt;/li&gt;
&lt;li&gt;Less dynamic than code-based approaches&lt;/li&gt;
&lt;li&gt;Android-only (no equivalent in iOS)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🧭 When Should You Use It?
&lt;/h3&gt;

&lt;p&gt;Use Network Security Config when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want a centralized security policy&lt;/li&gt;
&lt;li&gt;Your app uses multiple networking libraries&lt;/li&gt;
&lt;li&gt;You prefer a declarative approach&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use OkHttp pinning when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need fine-grained control&lt;/li&gt;
&lt;li&gt;You want to scope pinning to specific requests or clients&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;🧠 Key Insight&lt;/p&gt;

&lt;p&gt;Both approaches ultimately solve the same problem:&lt;/p&gt;

&lt;p&gt;Restricting trust to a known certificate or public key.&lt;/p&gt;

&lt;p&gt;The difference lies in where the responsibility lives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In your networking layer (OkHttp)&lt;/li&gt;
&lt;li&gt;Or in the Android platform configuration&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Wrapping Up (Part 1)
&lt;/h2&gt;

&lt;p&gt;At this point, we’ve covered what SSL pinning really is, what problems it solves (and what it doesn’t), and how to implement it on Android using a modern, production-ready approach.&lt;/p&gt;

&lt;p&gt;If there’s one takeaway from this first part, it’s this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pinning is not about replacing your security model — it’s about strengthening the transport layer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On Android, the ecosystem makes it relatively straightforward to adopt public key pinning with tools like OkHttp. However, the real challenge isn’t implementation — it’s &lt;strong&gt;operational discipline&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handling certificate rotation&lt;/li&gt;
&lt;li&gt;Avoiding hard failures in production&lt;/li&gt;
&lt;li&gt;Understanding when pinning actually adds value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before moving forward, ask yourself:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Do I really need pinning, or am I trying to compensate for missing backend security?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://dev.to/devpicon/ssl-pinning-in-mobile-apps-android-ios-practical-guide-trade-offs-part-2-3p4m"&gt;In the next part&lt;/a&gt;, we’ll move to iOS — where things are a bit more low-level, and where the trade-offs become even more evident.&lt;/p&gt;

</description>
      <category>mobile</category>
      <category>android</category>
      <category>security</category>
      <category>networking</category>
    </item>
    <item>
      <title>Gemma 4 E2B on Mobile: Android vs iOS Reality (1)</title>
      <dc:creator>Armando Picón</dc:creator>
      <pubDate>Mon, 04 May 2026 19:53:30 +0000</pubDate>
      <link>https://dev.to/devpicon/gemma-4-e2b-on-mobile-android-vs-ios-reality-1-38lm</link>
      <guid>https://dev.to/devpicon/gemma-4-e2b-on-mobile-android-vs-ios-reality-1-38lm</guid>
      <description>&lt;p&gt;I'm still looking for a new job. In the meantime, I'm running experiments with the new Gemma 4 E2B model... &lt;/p&gt;

&lt;p&gt;I just wanted to share a few notes on this...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The model uses the .litertlm extension, as it now relies on LiteRT-LM, the new framework to run these models in edge devices. It's an evolution of TensorFlow Lite.&lt;/li&gt;
&lt;li&gt;On Android,  there is a Kotlin-based API to run these models on Android devices optimized for coroutines.&lt;/li&gt;
&lt;li&gt;On iOS, there is currently no native Swift API to run it, so you have to rely on the C++ API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, one of my experiments consists of evaluating whether it's possible to run this model in a device without a dedicated IA chipset.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9atbmhnh8ykkg6d2uqxa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9atbmhnh8ykkg6d2uqxa.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mobile</category>
      <category>ios</category>
      <category>android</category>
    </item>
    <item>
      <title>[iOS] CocoaPods cheatsheet</title>
      <dc:creator>Armando Picón</dc:creator>
      <pubDate>Fri, 06 Feb 2026 02:37:51 +0000</pubDate>
      <link>https://dev.to/devpicon/ios-cocoapods-cheatsheet-3j5b</link>
      <guid>https://dev.to/devpicon/ios-cocoapods-cheatsheet-3j5b</guid>
      <description>&lt;p&gt;This post is just a reminder about how to use CocoaPods.&lt;/p&gt;

&lt;p&gt;First things first...&lt;/p&gt;

&lt;h2&gt;
  
  
  What is CocoaPods
&lt;/h2&gt;

&lt;p&gt;CocoaPods is a dependency manager for iOS projects.&lt;br&gt;
It integrates third-party libraries into Xcode projects using a &lt;strong&gt;Podfile&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Install ruby
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install ruby
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Install CocoaPods
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo gem install cocoapods
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Verify the installation using&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pod --version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Initialize CocoaPods in a project
&lt;/h2&gt;

&lt;p&gt;First, go to the project folder (where .xcodeproj is located) and run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pod init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A minimal example of configuration
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;platform :ios, '15.0'

target 'YourAppTarget' do
  use_frameworks!

  pod 'Alamofire'
  pod 'SnapKit'
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key things:&lt;br&gt;
    • platform → minimum iOS version&lt;br&gt;
    • target → must match your app target name exactly&lt;br&gt;
    • use_frameworks! → required for many Swift pods&lt;/p&gt;
&lt;h2&gt;
  
  
  Install the dependency
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pod install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This creates:&lt;br&gt;
    • Pods/ folder&lt;br&gt;
    • YourProject.xcworkspace&lt;/p&gt;

&lt;p&gt;🚨 From now on, open the .xcworkspace, not the .xcodeproj.&lt;/p&gt;
&lt;h2&gt;
  
  
  Using a pod in code
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Alamofire

AF.request("https://example.com").response { response in
  print(response)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If it compiles → done.&lt;/p&gt;
&lt;h2&gt;
  
  
  Common commands
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pod install     # install dependencies
pod update      # update dependencies
pod repo update # update specs repo
pod deintegrate # remove CocoaPods completely
pod search NAME # search pods
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Typical problems &amp;amp; fixes
&lt;/h2&gt;

&lt;p&gt;❌ “No such module”&lt;br&gt;
    • You opened .xcodeproj instead of .xcworkspace&lt;br&gt;
    • Target name mismatch in Podfile&lt;/p&gt;

&lt;p&gt;❌ Build settings conflicts&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pod deintegrate
pod install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;❌ Slow installs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pod install --repo-update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Some additional notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;CocoaPods is mostly for legacy projects&lt;/li&gt;
&lt;li&gt;For libraries that don't support SPM (Swift Package Manager)&lt;/li&gt;
&lt;li&gt;Consider using only Swift Package Manager&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ios</category>
      <category>cheatsheet</category>
      <category>dependencymanagement</category>
    </item>
    <item>
      <title>Android - Scalable dependency management with version catalogs</title>
      <dc:creator>Armando Picón</dc:creator>
      <pubDate>Wed, 13 Dec 2023 21:56:58 +0000</pubDate>
      <link>https://dev.to/devpicon/scalable-dependency-management-with-version-catalog-47lj</link>
      <guid>https://dev.to/devpicon/scalable-dependency-management-with-version-catalog-47lj</guid>
      <description>&lt;p&gt;I’m revisiting some helpful content for starting a new Android project. One of the pain points is how to handle a ton of dependency declarations for multi-module projects. Here is where version catalogs come to our rescue.&lt;/p&gt;

&lt;h1&gt;
  
  
  Declaring dependencies
&lt;/h1&gt;

&lt;p&gt;Before discussing the version catalogs, we will see how dependencies are being declared in an Android project. To declare a dependency in an Android project, you would add something like the following code in your &lt;code&gt;build.gradle.kts&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;android&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Here is where the dependencies are declared&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"androidx.core:core-ktx:1.12.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// ...other dependecies&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Coordinates
&lt;/h2&gt;

&lt;p&gt;One important concept to have in mind is the concept of &lt;strong&gt;&lt;em&gt;coordinates&lt;/em&gt;&lt;/strong&gt;. Each coordinate is composed by “&lt;strong&gt;[group]:[artifact name]:[version&lt;/strong&gt;]”. For example, in the example above the coordinate of the &lt;code&gt;core-ktx&lt;/code&gt; dependency is &lt;code&gt;[androidx.core]:[core-ktx]:[1.12.0]&lt;/code&gt;, so the same dependency can be decomposed in a declaration like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"androidx.core"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"core-ktx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.12.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep in mind this concept; we will return to it later.&lt;/p&gt;

&lt;h2&gt;
  
  
  A common issue with multi-module projects
&lt;/h2&gt;

&lt;p&gt;In case you have more than one module in your project, each dependency needs to be declared on every module that requires the specific dependency. The problem arises with this approach because of having repetitive declarations; every time you want to update a dependency version, you have to make the change in every gradle file where the dependency is declared. This makes dependency handling redundant and messy.&lt;/p&gt;

&lt;p&gt;Here is where &lt;strong&gt;Version catalogs&lt;/strong&gt; come to our rescue.&lt;/p&gt;

&lt;h1&gt;
  
  
  Version catalogs
&lt;/h1&gt;

&lt;p&gt;Version catalogs is a feature of Gradle, so you can use it if you use Gradle as your build system in your Android project. This allows you to declare and organize all your dependencies in a single file, which acts as a catalog where each dependency is associated to a single alias, making this catalogs acts as a source of truth and it can be referenced in every gradle file.&lt;/p&gt;

&lt;p&gt;To enable this option you need to follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new file into the &lt;code&gt;gradle&lt;/code&gt; folder at the project level with the name &lt;code&gt;libs.versions.toml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Declare the dependencies into that file.&lt;/li&gt;
&lt;li&gt;Reference these declarations in every gradle file of each module.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you don't know where the gradle folder is, you can typically find it at the root project level; you can use the Project view to look at it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fje05000htzcgzulteiwa.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fje05000htzcgzulteiwa.jpeg" alt=" " width="213" height="125"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point, you can ask… how these declarations must be made into that file?&lt;/p&gt;

&lt;h1&gt;
  
  
  Basic usage
&lt;/h1&gt;

&lt;p&gt;Now, let's see the structure of the &lt;code&gt;libs.versions.toml&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[versions]&lt;/span&gt;
&lt;span class="py"&gt;core-ktx&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.12.0"&lt;/span&gt;

&lt;span class="nn"&gt;[libraries]&lt;/span&gt;
&lt;span class="py"&gt;androidx-core-ktx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;group&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"androidx.core"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"core-ktx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version.ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"core-ktx"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the most basic format you have to declare two sections &lt;code&gt;[versions]&lt;/code&gt; and &lt;code&gt;[libraries]&lt;/code&gt;, the first one is used to declare versions for each dependency, the second is for the dependencies itself.&lt;/p&gt;

&lt;p&gt;For this example, we took the same dependency that it has been declared in the first example of this article, in the gradle file. In order to make the &lt;code&gt;core-ktx&lt;/code&gt; dependency available into the libs catalog, we associate the alias &lt;code&gt;androidx-core-ktx&lt;/code&gt; to the GAV (group, artifact, version) coordinates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The main benefit is these declarations are visible by all the modules in your project. So you can reference these declarations from your gradle file.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;android&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Here is where the dependencies are declared&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;androidx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ktx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// ...other dependecies&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: Every time you added a new declaration in the TOML file you have to sync the project to make it available for the modules.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Also, using Version catalogs let you to reference the same version for multiple different libraries, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[versions]&lt;/span&gt;
&lt;span class="py"&gt;android-paging&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"3.2.1"&lt;/span&gt;

&lt;span class="nn"&gt;[libraries]&lt;/span&gt;
&lt;span class="py"&gt;android-paging-common&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;module&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"androidx.paging:paging-common-ktx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version.ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"android-paging"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;android-paging-runtime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;module&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"androidx.paging:paging-runtime-ktx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version.ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"android-paging"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;android-paging-rxjava2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;module&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"androidx.paging:paging-rxjava2-ktx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version.ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"android-paging"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we simplify the coordinates using the module to group the &lt;code&gt;[group]:[artifact]&lt;/code&gt; pair and use the same version reference for all the android-paging dependencies.&lt;/p&gt;

&lt;h1&gt;
  
  
  Going a bit further
&lt;/h1&gt;

&lt;p&gt;Not only you can declare the regular dependencies in that TOML file, you can declare your plugins and even create bundles for grouping dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to declare plugins
&lt;/h2&gt;

&lt;p&gt;To declare a plugin you only need to add the &lt;code&gt;[plugins]&lt;/code&gt; section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[versions]&lt;/span&gt;
&lt;span class="err"&gt;//Here&lt;/span&gt; &lt;span class="err"&gt;the&lt;/span&gt; &lt;span class="err"&gt;versions&lt;/span&gt; &lt;span class="err"&gt;are&lt;/span&gt; &lt;span class="err"&gt;declared&lt;/span&gt;
&lt;span class="py"&gt;android-gradle-plugin&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"8.2.0"&lt;/span&gt;

&lt;span class="nn"&gt;[libraries]&lt;/span&gt;
&lt;span class="err"&gt;//Here&lt;/span&gt; &lt;span class="err"&gt;the&lt;/span&gt; &lt;span class="err"&gt;libraries&lt;/span&gt; &lt;span class="err"&gt;are&lt;/span&gt; &lt;span class="err"&gt;declared&lt;/span&gt;

&lt;span class="nn"&gt;[plugins]&lt;/span&gt;
&lt;span class="py"&gt;android-application&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="py"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"com.android.application"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version.ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"android-gradle-plugin"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, after syncing the project, we can change the references from this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Top-level build file where you can add configuration options common to all sub-projects/modules.&lt;/span&gt;
&lt;span class="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.android.application"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s"&gt;"8.2.0"&lt;/span&gt; &lt;span class="n"&gt;apply&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
    &lt;span class="c1"&gt;//... more plugins are placed here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Top-level build file where you can add configuration options common to all sub-projects/modules.&lt;/span&gt;
&lt;span class="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;android&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;apply&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
    &lt;span class="c1"&gt;//... more plugins are placed here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to create your own bundle
&lt;/h2&gt;

&lt;p&gt;To create a bundle, you have to add a &lt;code&gt;[bundles]&lt;/code&gt; section into your TOML file. For example, here I’m adding the Room bundle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[versions]&lt;/span&gt;
&lt;span class="py"&gt;room&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2.6.1"&lt;/span&gt;
&lt;span class="py"&gt;ksp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.9.0-1.0.13"&lt;/span&gt;

&lt;span class="nn"&gt;[libraries]&lt;/span&gt;
&lt;span class="py"&gt;androidx-room-compiler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;group&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"androidx.room"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"room-compiler"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version.ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"room"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;androidx-room-ktx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;group&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"androidx.room"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"room-ktx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version.ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"room"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;androidx-room-runtime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;group&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"androidx.room"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"room-runtime"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version.ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"room"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nn"&gt;[plugins]&lt;/span&gt;
&lt;span class="py"&gt;kotlin-symbol-processing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"com.google.devtools.ksp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version.ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ksp"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nn"&gt;[bundles]&lt;/span&gt;
&lt;span class="py"&gt;android-room-bundle&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"androidx-room-ktx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"androidx-room-runtime"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, you can use the bundle like this after syncing your project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// More plugins are added here...&lt;/span&gt;
        &lt;span class="nf"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kotlin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;processing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;android&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Some dependencies are declared here&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bundles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;android&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;ksp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;androidx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compiler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;It’s also worth mentioning that using a version catalog is likely the best way to establish a single source of truth for all your project dependencies. It's also worth mentioning that the official documentation has a section about this topic.&lt;/p&gt;

&lt;p&gt;You can experiment with different strategies using bundles to organize the necessary dependencies for each project, avoiding the tedious process of checking each Gradle file to see what you’ve declared.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;Migrate to version catalogs&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.android.com/build/migrate-to-catalogs" rel="noopener noreferrer"&gt;https://developer.android.com/build/migrate-to-catalogs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sharing dependency version between projects&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.gradle.org/current/userguide/platforms.html" rel="noopener noreferrer"&gt;https://docs.gradle.org/current/userguide/platforms.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>dependencymanagement</category>
      <category>gradle</category>
      <category>versioncatalogs</category>
    </item>
    <item>
      <title>Android - Desmitificando Clean Architecture</title>
      <dc:creator>Armando Picón</dc:creator>
      <pubDate>Thu, 30 Dec 2021 17:22:28 +0000</pubDate>
      <link>https://dev.to/devpicon/android-desmitificando-clean-architecture-33m6</link>
      <guid>https://dev.to/devpicon/android-desmitificando-clean-architecture-33m6</guid>
      <description>&lt;h2&gt;
  
  
  Antes de comenzar…
&lt;/h2&gt;

&lt;p&gt;Si es tu primera vez leyendo sobre Clean Architecture quizá te convenga saber algunas cosillas antes de seguir:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean Architecture es el título de un libro escrito por Robert C. Martin (aka. Uncle Bob)&lt;/li&gt;
&lt;li&gt;No es una arquitectura en sí, sino una guía que encierra un conjunto de principios a seguir.&lt;/li&gt;
&lt;li&gt;La base de esta guía se encuentra en aplicar los principios SOLID.&lt;/li&gt;
&lt;li&gt;En términos generales, te intenta llevar a una estructura basada en el Dominio (&lt;em&gt;Domain&lt;/em&gt;) como punto central de tu aplicación (la capa en la que se implementará la lógica de tu aplicación). ¿Alguien habló de Domain-driven development?&lt;/li&gt;
&lt;li&gt;Un buen número de desarrolladores la confunde la arquitectura con N-capas.&lt;/li&gt;
&lt;li&gt;No es el santo grial, es una expresión algo mainstream entre los desarrolladores y por ende sirve como una forma fácil de describirle a un desarrollador de qué manera se ha estructurado un proyecto.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ¿Qué es una arquitectura?
&lt;/h2&gt;

&lt;p&gt;Básicamente, es una estructura, una manera de distribuir los componentes que conforman una aplicación en distintas agrupaciones. Por esto mismo, no existe una única manera de organizar tu proyecto. Diversos aspectos como el estado de tu proyecto, el tamaño de tu equipo, el tiempo, etc influirán en las decisiones que tomes al momento de decidir la manera en la que organizarás los componentes de tu aplicación.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clean Architecture en el mundo&amp;nbsp;Android
&lt;/h2&gt;

&lt;p&gt;Mucho se ha dicho y escrito sobre cómo aplicar &lt;em&gt;Clean Architecture&lt;/em&gt; en las aplicaciones que desarrollamos, un buen número de desarrolladores te hablarán sobre &lt;em&gt;Separation of concerns and testing&lt;/em&gt; como las principales características de seguir estos principios, suelen a su vez olvidar un aspecto muy importante sobre &lt;em&gt;Clean Architecture&lt;/em&gt; y que consiste en que su base yace en la implementación de la capa de Dominio.&lt;/p&gt;

&lt;p&gt;Dicho esto ¿cómo se estructuraría un proyecto base siguiendo estos principios? Para trabajar en esto es importante establecer dos consideraciones importantes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Existe una regla en &lt;em&gt;Clean Architecture&lt;/em&gt; por la cual toda capa exterior dependerá de una capa interior (y no al revés), quedando para el final la capa de Dominio sin dependencia alguna.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdadpc22gfbugpf3pmd17.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdadpc22gfbugpf3pmd17.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Existe un flujo de datos que pasa por todas las capas externas e internas tanto de ida desde que se efectúe el ingreso de datos por parte del usuario (o de los sensores), pasando por la lógica de negocio/aplicación y terminando en la persistencia de los datos o el envío de los mismos a un servicio remoto, como también de vuelta con el resultado de la operación con el servicio hasta la presentación del resultado a nuestro usuario.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fccepm0me3tm77wsfltwp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fccepm0me3tm77wsfltwp.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Teniendo en cuenta estos dos aspectos, para una estructura básica contaríamos con tres módulos: &lt;em&gt;presentation&lt;/em&gt;, &lt;em&gt;domain&lt;/em&gt; y &lt;em&gt;data&lt;/em&gt;. La relación entre estos sería de la siguiente manera:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F96jyttwblu32zd88ru05.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F96jyttwblu32zd88ru05.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Qué componentes irían en cada&amp;nbsp;capa?
&lt;/h2&gt;

&lt;p&gt;Es importante reiterar que al hablar de arquitectura, hablamos de organización y estructura, por lo tanto, la distribución y qué componentes en específico irán en cada módulo dependerá del patrón que decidas emplear y el propósito de cada módulo:&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Domain&lt;/em&gt; deberá contener los elementos que contengan la lógica de tu aplicación y la lógica del negocio. Con ello en mente, tendremos clases de dominio, y además componentes que tendrán la lógica como son los Casos de Uso o Interactors. Este módulo NO debe depender del Android framework ni de dependencias de terceros. Este módulo puede ser un módulo Kotlin.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Presentation&lt;/em&gt; tendrá los elementos que permitan mostrar información a nuestro usuario, recibir datos del mismo o de sensores. Además de los componentes visuales propios de Android (Activity y Fragment) y su sistema de UI (archivos XML o funciones de Compose), si se siguen patrones como MVP o MVVM, tendremos también Presenters o ViewModels respectivamente. Este módulo es un módulo Android.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Data&lt;/em&gt; va a incluir todas las dependencias de networking (Retrofit, volley, etc) y de persistencia de datos (Room, SharedPreferences, DataStore, Firebase, etc). Si se emplea el patrón repositorio será en esta capa en la que tendremos la implementación de los repositorios y las fuentes de datos (data sources). Este módulo es un módulo Android también.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Seguramente aquí te podría surgir la siguiente duda: si &lt;em&gt;Domain&lt;/em&gt; no tiene visibilidad de &lt;em&gt;Data&lt;/em&gt; ¿cómo es que podríamos inyectar los repositorios en nuestros casos de uso? La respuesta se encuentra en el principio de Inversión de Dependencia (&lt;em&gt;Dependency inversion&lt;/em&gt;) de SOLID.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependency inversion principle
&lt;/h2&gt;

&lt;p&gt;Este principio de SOLID establece dos consignas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Los módulos de alto nivel no deberían importar nada de un módulo de bajo nivel. Pero ambos deberían depender de abstracciones (interfaces).&lt;/li&gt;
&lt;li&gt;Las abstracciones no deberían depender de los detalles. Los detalles (las implementaciones concretas) deberían depender de las abstracciones.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Teniendo en mente esto, nuestro módulo &lt;em&gt;Domain&lt;/em&gt; deberá ser propietario de la interfaz del repositorio que se implementará dentro del módulo &lt;em&gt;Data&lt;/em&gt;.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgboebcmfhsy7uuprzhjg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgboebcmfhsy7uuprzhjg.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En código esto luciría más o menos de la siguiente manera, en el módulo &lt;em&gt;Domain&lt;/em&gt; estaría la abstracción de un BookRepository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;interface BookRepository {
    suspend fun getBooks():List&amp;lt;Book&amp;gt;
}
class GetBooksUseCase(
    private val bookRepository: BookRepository
) { /*...*/ }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mientras que en el módulo Data iría su implementación.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class BookRepositoryImpl (...) : BookRepository {
    override suspend fun getBooks(): List&amp;lt;Book&amp;gt; { /* ... */}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;De este modo al momento de crear una instancia de nuestro caso de uso le podremos inyectar la implementación debido a la interfaz que hemos declarado.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusiones
&lt;/h2&gt;

&lt;p&gt;Podría mencionar algunas cosas como parte de la conclusión:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contar con una arquitectura o forma de organizar los componentes es mejor que no tener ninguna.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Clean Architecture&lt;/em&gt; no es una arquitectura en sí, pero nos provee de una guía a seguir para estructurar nuestros proyectos.&lt;/li&gt;
&lt;li&gt;Existen otras arquitecturas que no cuentan con mucha fama, pero no por ello son menos útiles.&lt;/li&gt;
&lt;li&gt;Teniendo esta estructura, puedes usar lo que se te antoje tanto en la presentación (sea Views-XMLs o Compose) como a nivel del acceso a datos (Retrofit, Room, SharedPreferences, etc).&lt;/li&gt;
&lt;li&gt;En mi experiencia, la buena aplicación de los principios SOLID te da una buena base para ir perfilando la arquitectura de tu proyecto.&lt;/li&gt;
&lt;li&gt;La relanzada &lt;a href="https://developer.android.com/jetpack/guide" rel="noopener noreferrer"&gt;guía oficial de arquitectura de Android&lt;/a&gt; no sigue Clean Architecture, pero sí provee de una arquitectura separada en capas.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164" rel="noopener noreferrer"&gt;Clean Architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Dependency_inversion_principle" rel="noopener noreferrer"&gt;Dependency Inversion principle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fernandocejas.com/blog/engineering/2019-05-08-architecting-android-reloaded/" rel="noopener noreferrer"&gt;Architecting Android…Reloaded by Fernando Cejas&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>android</category>
      <category>programming</category>
      <category>architecture</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Kotlin - Scope Functions (let, with, run, apply &amp; also)</title>
      <dc:creator>Armando Picón</dc:creator>
      <pubDate>Mon, 22 Feb 2021 00:36:49 +0000</pubDate>
      <link>https://dev.to/devpicon/kotlin-scoped-functions-let-with-run-apply-also-dhp</link>
      <guid>https://dev.to/devpicon/kotlin-scoped-functions-let-with-run-apply-also-dhp</guid>
      <description>&lt;p&gt;Empecemos viendo este código:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  name?.let{
    //...
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Estoy seguro que en algún momento lo has visto y sabes que es una forma de validar que &lt;code&gt;name&lt;/code&gt; no sea nulo antes de emplear el valor dentro del bloque de código dentro de la función &lt;code&gt;let&lt;/code&gt;. Pero ¿realmente sabes las implicancias del uso de dicha función? Si la respuesta es no, pues esta publicación es para ti.&lt;/p&gt;

&lt;h2&gt;
  
  
  Definiendo las &lt;em&gt;scope functions&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Según la &lt;a href="https://kotlinlang.org/docs/scope-functions.html" rel="noopener noreferrer"&gt;documentación oficial&lt;/a&gt; son funciones cuyo único propósito es "ejecutar un bloque de código dentro del contexto de un objeto". Creo que la explicación es algo sencilla, básicamente es una forma de organizar nuestro código y hacerlo mejor leíble.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Qué &lt;em&gt;scope functions&lt;/em&gt; trae Kotlin?
&lt;/h2&gt;

&lt;p&gt;Básicamente tenemos 5 &lt;em&gt;scope functions&lt;/em&gt;: &lt;code&gt;let&lt;/code&gt;, &lt;code&gt;with&lt;/code&gt;, &lt;code&gt;run&lt;/code&gt;, &lt;code&gt;apply&lt;/code&gt; y &lt;code&gt;also&lt;/code&gt;; adicionalmente hay 2 funciones extras &lt;code&gt;takeIf&lt;/code&gt; y &lt;code&gt;takeUnless&lt;/code&gt; que nos permitirán adicionar algunas validaciones de estado en medio de una cadena de invocación (o en tras palabras cuando concatenas varias &lt;em&gt;scope functions&lt;/em&gt;) y que suelen emplearse junto con las 5 anteriores.&lt;/p&gt;

&lt;p&gt;Como la documentación lo señala hay dos elementos que distinguen a una &lt;em&gt;scope function&lt;/em&gt; de otra:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;la forma en que se referencia al objeto de contexto&lt;/li&gt;
&lt;li&gt;el valor de retorno&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Por la forma en que se referencia al objeto de contexto
&lt;/h3&gt;

&lt;p&gt;La razón por la que tenemos un &lt;code&gt;it&lt;/code&gt; o un &lt;code&gt;this&lt;/code&gt; es para simplificar o acortar el nombre de la variable original y,  básicamente, te dirá si el objeto va a recibir la lambda (para el caso del &lt;code&gt;this&lt;/code&gt;) o si el objeto va a pasarse como argumento de la lambda (para el caso de &lt;code&gt;it&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Entonces, ¿en qué casos el objeto de contexto va a recibir la lambda? &lt;code&gt;run&lt;/code&gt;, &lt;code&gt;with&lt;/code&gt; y &lt;code&gt;apply&lt;/code&gt; y ¿en qué casos el objeto de contexto se pasará como argumento? &lt;code&gt;let&lt;/code&gt; y &lt;code&gt;also&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Por el valor de retorno
&lt;/h3&gt;

&lt;p&gt;Para esta situación tendremos el retorno del mismo objeto de contexto para el caso de &lt;code&gt;apply&lt;/code&gt; y &lt;code&gt;also&lt;/code&gt; o el valor de resultado de la lambda en el caso de &lt;code&gt;let&lt;/code&gt;, &lt;code&gt;run&lt;/code&gt; y &lt;code&gt;with&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Qué función elegir?
&lt;/h2&gt;

&lt;p&gt;Teniendo estas dos distinciones en mente, &lt;a href="https://kotlinlang.org/docs/scope-functions.html#functions" rel="noopener noreferrer"&gt;la documentación oficial detalla cada una de las funciones&lt;/a&gt; y los posibles escenarios de uso. De esa misma documentación saqué este cuadro que ayuda mucho a resumir lo dicho anteriormente:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Función&lt;/th&gt;
&lt;th&gt;Referencia de objeto&lt;/th&gt;
&lt;th&gt;Valor de retorno&lt;/th&gt;
&lt;th&gt;¿Es una &lt;em&gt;extension function&lt;/em&gt;?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;let&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;it&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Resultado de la lambda&lt;/td&gt;
&lt;td&gt;Sí&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;run&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;this&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Resultado de la lambda&lt;/td&gt;
&lt;td&gt;Sí&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;run&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Resultado de la lambda&lt;/td&gt;
&lt;td&gt;No, cuando se llama sin el objeto de contexto&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;with&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;this&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Resultado de la lambda&lt;/td&gt;
&lt;td&gt;No, porque toma el objeto de contexto como argumento&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;apply&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;this&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Objeto de contexto&lt;/td&gt;
&lt;td&gt;Sí&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;also&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;it&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Objeto de contexto&lt;/td&gt;
&lt;td&gt;Sí&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;También la documentación brinda una pequeña guía de referencia:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ejecución de una lambda en objetos non-null: &lt;code&gt;let&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Introducir una expresión como una variable en un ámbito local: &lt;code&gt;let&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Configuración de un objeto: &lt;code&gt;apply&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Configuración de un objeto y computación del resultado: &lt;code&gt;run&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Ejecutar sentencias donde una expresión es requerida: &lt;code&gt;run&lt;/code&gt; a secas sin objeto de contexto ni como función extendida&lt;/li&gt;
&lt;li&gt;Efectos adicionales: &lt;code&gt;also&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Agrupar llamadas a funciones en un objeto: &lt;code&gt;with&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  takeIf y takeUnless
&lt;/h2&gt;

&lt;p&gt;Estas dos funciones que vienen en la biblioteca estandar de Kotlin, no son consideradas &lt;em&gt;scope functions&lt;/em&gt;; sin embargo, se emplean junto con ellas y sirven para realizar algunas validaciones. &lt;/p&gt;

&lt;p&gt;La documentación oficial &lt;a href="https://kotlinlang.org/docs/scope-functions.html#takeif-and-takeunless" rel="noopener noreferrer"&gt;tiene algunos buenos ejemplos&lt;/a&gt; de cómo emplearlas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusión
&lt;/h2&gt;

&lt;p&gt;Me resultan super interesantes las &lt;em&gt;scope functions&lt;/em&gt; y este artículo tiene por intención hacer un breve resumen de en qué consisten y cuando emplearlas. Es importante practicar su uso para acostumbrarse a sus diferencias y casos de uso, dado que por lo similares que son resultado fácil confundirlas y equivocar su uso.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>scopefunction</category>
      <category>spanish</category>
    </item>
    <item>
      <title>¿Qué nos dejó el 2020 y que nos traerá el 2021?</title>
      <dc:creator>Armando Picón</dc:creator>
      <pubDate>Fri, 01 Jan 2021 03:36:42 +0000</pubDate>
      <link>https://dev.to/devpicon/que-nos-dejo-el-2020-y-que-nos-traera-el-2021-2kgp</link>
      <guid>https://dev.to/devpicon/que-nos-dejo-el-2020-y-que-nos-traera-el-2021-2kgp</guid>
      <description>&lt;p&gt;Hola amigos y amigas de Codalot Podcast, que año para complicado el que nos tocó pasar, cierto? y quería empezar expresando mis condolencias a todos los que sufrieron de la pérdida de algún ser querido o de alguna persona cercana producto de esta pandemia.&lt;/p&gt;

&lt;p&gt;Si bien es cierto que diversos sectores económicos sufrieron de un gran impacto producto de la crisis que se generó por causa de las medidas que tomó cada gobierno, producto de ello se perdieron muchos puestos de trabajo; sin embargo, dentro de las ocupaciones que no sufrieron tanto durante esta pandemia estuvieron los programadores y los desarrolladores de software.&lt;/p&gt;

&lt;p&gt;Si fuiste uno de estos, puedes sentirte privilegiado y a su vez puedes sentirte feliz de haber tomado la decisión de dedicarte a esto.&lt;/p&gt;

&lt;h2&gt;
  
  
  El año del trabajo remoto
&lt;/h2&gt;

&lt;p&gt;Este año que termina ha sido un año lleno de desafíos y de cambios significativos para cada uno de nosotros. Muy aparte de las cuarentenas y demás restricciones dictaminadas por cada gobierno, fue el año del trabajo remoto o home office.&lt;br&gt;
Un problema latente con el trabajo remoto en este tiempo es que, sumado al hecho de que muchos de nosotros no podíamos salir, puede llegar a ser tedioso y muy agotador. Por eso se torna importante contar con rutinas, generar hábitos, aprender a desconectarse y aprender a ser creativos para no llegar al colapso.&lt;/p&gt;

&lt;p&gt;Sin embargo, para los escépticos del trabajo remoto, ahora ya se tiene evidencia de que esta dinámica bien implementada puede ser no solo beneficiosa sino también altamente productiva. Lo que abriría las puertas para que se armen espacios de trabajo híbridos donde el que quiera pueda ir a una oficina a trabajar y el que no lo desee pueda trabajar desde donde le plazca. Evidentemente, esto dependerá de la forma en que vaya evolucionando la crisis.&lt;/p&gt;

&lt;h2&gt;
  
  
  El mundo Android en el 2020
&lt;/h2&gt;

&lt;p&gt;En el mundo Android, este fue el año de las llamadas habilidades de Desarrollo Moderno en Android o conocido en inglés como “MAD skills”, se ha hecho mucho esfuerzo en la adopción de Kotlin por parte de Android en las diferentes dependencias o bibliotecas que se han venido trabajando. Esto ha permitido potenciar el conjunto de bibliotecas que yacen bajo el nombre de Jetpack y que forman parte de la manera en que Google recomienda la construcción de las nuevas aplicaciones, hecho que nos debe llevar a una necesaria actualización y adopción.&lt;/p&gt;

&lt;p&gt;También, se ha hecho incapié en el uso de App Bundle, una nueva forma de distribución que permite la optimización de los recursos de nuestras aplicaciones.&lt;/p&gt;

&lt;p&gt;Adicionalmente, han habido mejoras significativas en la construcción de las UIs mediante el uso de componentes como ConstraintLayout 2 y MotionLayout. Los cuales han de estar muy vigentes también durante el próximo año.&lt;/p&gt;

&lt;p&gt;Finalmente, con las nuevas actualizaciones de Android como sistema operativo han llegado nuevas restricciones al acceso a data sensible que debemos tomar en consideración si deseamos mantener nuestras aplicaciones vigentes.&lt;/p&gt;

&lt;p&gt;De hecho, tras el apoyo de Google hace 3 años, hoy en día no cabe duda del valor que Kotlin tiene para nosotros y, de hecho, si no estás familiarizado con dicho lenguaje tus opciones de empleabilidad van a caer dramáticamente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Las comunidades se reinventaron
&lt;/h2&gt;

&lt;p&gt;Las comunidades de desarrolladores también se vieron en la necesidad de adaptarse; tuvieron que pasar de organizar eventos de forma presencial con toda la logística que ello conlleva, a organizarlas de manera virtual. Pasaron de buscar espacios físicos a buscar plataformas de streaming que facilitaran la organización de pláticas.&lt;/p&gt;

&lt;p&gt;Lo anterior también llevó a replantearse la necesidad de cobrar por el acceso a los eventos, dado que ya no se cuenta con el atractivo inherente de un evento físico, algunos eventos pasaron a cobrar algo simbólico o simplemente a liberar la entrada.&lt;/p&gt;

&lt;p&gt;Esto también provocó el replanteamiento de las charlas brindadas por los speakers, dado que anteriormente tenía sentido compartir la misma presentación en múltiples lugares con público distinto en cada una y ahora una charla dada en un evento tranquilamente es accesible a través de las grabaciones que podían quedar en Youtube, lo que los lleva a presentar algo nuevo en cada presentación.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Qué vendrá en el 2021?
&lt;/h2&gt;

&lt;p&gt;Este año que viene será el año de Compose, una nueva forma de construir interfaces de manera declarativa y que viene siendo escrita enteramente en Kotlin, y de Hilt, una dependencia que crea una capa de abstracción sobre Dagger, y que nos permitirá efectuar la inyección de dependencias de una forma más natural. Ambos, van a contar con sus primeros releases estables durante este próximo año.&lt;/p&gt;

&lt;p&gt;No podemos dejar de lado la mención a Kotlin Multiplatform, una solución que permite compartir código durante el desarrollo de aplicaciones multiplataforma y que se ha venido cocinando durante un buen tiempo, ahora mismo se encuentra aún en alpha, pero podríamos ver sus primeros betas en el trascurso de este 2021 también.&lt;/p&gt;

&lt;p&gt;Otro cambio más que se nos viene será la adopción de nuevos nombres código o codenames para las nuevas versiones de Android Studio empezando por la versión 4.3 que además de cambiar el esquema de versionamiento que estará más alineado con lo que se viene haciendo con IntelliJ Idea nos traerá un nuevo nombre código Android Studio Artic Fox.&lt;br&gt;
&lt;a href="https://android-developers.googleblog.com/2020/12/announcing-android-studio-arctic-fox.html" rel="noopener noreferrer"&gt;https://android-developers.googleblog.com/2020/12/announcing-android-studio-arctic-fox.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Este nuevo año traerá consigo muchas adaptaciones de lo viejo a lo nuevo; la entera adopción de Kotlin, la adopción de los MAD skills, etc.&lt;br&gt;
&lt;a href="https://developer.android.com/series/mad-skills" rel="noopener noreferrer"&gt;https://developer.android.com/series/mad-skills&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Claro está que más allá de las novedades, lo que no cambiará será la necesidad de conocer cosas tan fundamentales como los buenos principios de programación, la adopción de patrones de diseño de software, entre otros conocimientos que nos ayudarán a escribir y estructurar nuestro código de mejor forma, y que a su vez nos llevará a desarrollar mejores aplicaciones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cierre
&lt;/h2&gt;

&lt;p&gt;Espero que este nuevo año traiga consigo cosas positivas, que puedan seguir cuidándose a ustedes y a sus familias. Y que podamos seguir creciendo en medio de la adversidad que hoy vivimos. Un abrazo a todos y les deseo un Feliz Año Nuevo 2021.&lt;/p&gt;

&lt;p&gt;Adios!&lt;/p&gt;

</description>
      <category>codalot</category>
      <category>podcast</category>
      <category>android</category>
      <category>community</category>
    </item>
    <item>
      <title>Kotlin Coroutines 101 — Jobs</title>
      <dc:creator>Armando Picón</dc:creator>
      <pubDate>Sun, 17 May 2020 18:33:01 +0000</pubDate>
      <link>https://dev.to/devpicon/kotlin-coroutines-101-jobs-1d33</link>
      <guid>https://dev.to/devpicon/kotlin-coroutines-101-jobs-1d33</guid>
      <description>&lt;h3&gt;
  
  
  Kotlin Coroutines 101 — Jobs
&lt;/h3&gt;

&lt;p&gt;En este artículo estaré vamos a mencionar algunos aspectos que encuentro interesantes sobre los Jobs.&lt;/p&gt;

&lt;h4&gt;
  
  
  ¿Qué es un Job?
&lt;/h4&gt;

&lt;p&gt;En el primer artículo que escribí tuvimos ocasión de lanzar una corutina mediante la acción de un botón. Pero ¿qué pasa si por algún motivo queremos verificar el estado de la corutina o cancelarla, si fuera necesario?. Es aquí donde entra a tallar la tarea de un &lt;em&gt;Job&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Un &lt;em&gt;Job&lt;/em&gt; es una representación de una corutina y nos va a permitir realizar algunas acciones sobre ella.&lt;/p&gt;

&lt;h4&gt;
  
  
  ¿Cuáles son sus estados?
&lt;/h4&gt;

&lt;p&gt;Los &lt;em&gt;Jobs&lt;/em&gt; cuentan con estados, los cuales listaré a continuación:&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flofz6nb82txfo6w14ju2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flofz6nb82txfo6w14ju2.png" width="800" height="434"&gt;&lt;/a&gt;Ciclo de vida de un &lt;em&gt;Job&lt;/em&gt;&lt;p&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;New&lt;/strong&gt; : representa el estado de creación de una corutina y la asociación de un &lt;em&gt;Job&lt;/em&gt; a ella.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Active&lt;/strong&gt; : este estado representa el momento en el que se inicia ejecución y mantiene la ejecución de una corutina.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Completing&lt;/strong&gt; : este estado representa el momento en que la corutina ha finalizado su ejecución y precisa esperar a que otras corutinas asociadas a ella o en una relación de padre-hijo finalicen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Completed&lt;/strong&gt; : este estado representa el estado final de una corutina, el momento en el que toda su ejecución, incluyendo las corutinas hijas, ha finalizado.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cancelling&lt;/strong&gt; : este estado representa el momento en el que se ha cancelado la corutina y se espera la cancelación de los &lt;em&gt;Jobs&lt;/em&gt; asociados a ella.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cancelled&lt;/strong&gt; : este es el estado final una vez que se ha procedido a la cancelación total o completa.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Debido a la existencia de estos estados, es posible mediante un &lt;em&gt;Job&lt;/em&gt; acceder a las siguientes propiedades:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;isActive&lt;/strong&gt; : esta propiedad nos permitirá saber si un &lt;em&gt;Job&lt;/em&gt; se encuentra en estado activo o no.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;isCancelled&lt;/strong&gt; : esta propiedad nos permitirá saber si un &lt;em&gt;Job&lt;/em&gt; ha sido cancelado o no.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;isCompleted&lt;/strong&gt; : esta propiedad nos permitirá saber si un &lt;em&gt;Job&lt;/em&gt; ha finalizado o ha completado todas sus operaciones.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;¿Cuándo emplear estas propiedades? La respuesta sencilla sería emplearlas en momentos en los que la ejecución o lógica de tu corutina demande saber si debe continuar o no si el estado de la misma cambia.&lt;/p&gt;

&lt;h4&gt;
  
  
  La función invokeOnCompletion
&lt;/h4&gt;

&lt;p&gt;Un aspecto interesante de los &lt;em&gt;Jobs&lt;/em&gt; es que es posible ejecutar código una vez que estos finalizan y esto es gracias a la función invokeOnCompletion(), esta función recibe como parámetro un objeto &lt;em&gt;nullable&lt;/em&gt; de tipo Throwable? que nos permitirá validar la causa por la que una corutina fue cancelada.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;job1.invokeOnCompletion { throwable -&amp;gt;  
    log("Complete my job 1")
    throwable?.let {  
        log("There is a throwable object!")
        when ( it ) {
            is CancellationException -&amp;gt; {
                log( it.message ?: "Without message")
            }
        }
    }  
}  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ¿Cómo cancelar un job?
&lt;/h4&gt;

&lt;p&gt;Cancelar un &lt;em&gt;Job&lt;/em&gt; se puede conseguir mediante la invocación de su función cancel(). Sin embargo, te va a pedir que pases una instancia de la clase CancellationException, preferiblemente hay que agregarle un mensaje que indique la causa por la que se está cancelando el &lt;em&gt;Job&lt;/em&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  ¿Cómo concatenar jobs?
&lt;/h4&gt;

&lt;p&gt;Concatenar &lt;em&gt;Jobs&lt;/em&gt; es relativamente sencillo, para ello requerimos ejecutar la función join(). El comportamiento por defecto de la función join() implica que la corutina que lo invoque se suspenda hasta que este &lt;em&gt;Job&lt;/em&gt; esté completo. Sin embargo, si al builder de turno le pasamos el parámetro start=CoroutineStart.LAZY lo que acontecerá es que dicho &lt;em&gt;Job&lt;/em&gt; pasará a un estado activo en el punto en el que join() sea invocado.&lt;/p&gt;

&lt;h4&gt;
  
  
  ¿Cómo definir una relación de padre/hijo entre Jobs?
&lt;/h4&gt;

&lt;p&gt;Definir una relación de padre e hijos es posible si pasas el &lt;em&gt;Job&lt;/em&gt; como parte del contexto de la corutina que quieres definir como &lt;em&gt;Job&lt;/em&gt; hijo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val job1 = coroutineScope.launch(start = CoroutineStart.LAZY, 
                                 context = handlerException) {...}

val childCoroutineScope = CoroutineScope(Dispatchers.Main + job1)
val job2 = childCoroutineScope.launch{...}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ¿Cómo manejar una excepción en un job?
&lt;/h4&gt;

&lt;p&gt;Para manejar excepciones tienes dos forma; la primera, de forma tradicional usando un try/catch en la sección de código que quieres asegurar; la otra, es declarando una instancia de CoroutineExceptionHandler, el cual recibe como parámetros tanto un objeto conteniendo información sobre el contexto de la corutina y un objeto del tipo Throwable. Este objeto debe pasarse como parte del contexto de la corutina.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val handlerException = CoroutineExceptionHandler {
    coroutineContext, throwable -&amp;gt;  
        run {  
                log("$coroutineContext - ${throwable.message}")
        }  
}  

val job1 = coroutineScope.launch(context = handlerException ) {...}  

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

&lt;/div&gt;



&lt;p&gt;Para complementar les dejo un video para complementar el contenido de este artículo, regálenme un Me gusta si les es de utilidad y un comentario en el video.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/T3cj6NGU4w0"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;




</description>
      <category>android</category>
      <category>coroutine</category>
      <category>programación</category>
      <category>kotlin</category>
    </item>
  </channel>
</rss>
