<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Codename One</title>
    <description>The latest articles on Forem by Codename One (@codename_one).</description>
    <link>https://forem.com/codename_one</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F2599%2F92e804eb-16d8-4e8e-8814-e30c1aad11e8.png</url>
      <title>Forem: Codename One</title>
      <link>https://forem.com/codename_one</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/codename_one"/>
    <language>en</language>
    <item>
      <title>Debugging in Production-How to move fast without breaking things</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Fri, 17 Sep 2021 06:56:17 +0000</pubDate>
      <link>https://forem.com/codename_one/debugging-in-production-how-to-move-fast-without-breaking-things-3hnl</link>
      <guid>https://forem.com/codename_one/debugging-in-production-how-to-move-fast-without-breaking-things-3hnl</guid>
      <description>&lt;p&gt;Mark Zuckerberg famously quipped that Facebook works under the "Move fast and break things" motto. We can write all the unit tests in the world, have the largest QA pipeline but still bugs slither into production. That's just a fact of life which he chose to celebrate. &lt;/p&gt;

&lt;p&gt;When dealing with an incredibly complex system like we have in &lt;a href="https://www.codenameone.com" rel="noopener noreferrer"&gt;Codename One&lt;/a&gt; this can be the difference between releasing an update and doing nothing. &lt;/p&gt;

&lt;p&gt;If you are a visual learner check out the following video:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/X92BW_v3ZhE"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Disaster Strikes
&lt;/h2&gt;

&lt;p&gt;The problem with this approach becomes apparent when we have a bug in production. Normally, a bug that would go through staging, QA and tests wouldn't be too horrible... But since it's a production only bug you're often faced with two options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Revert - That might not be an option for all cases&lt;/li&gt;
&lt;li&gt;Find/Implement a Fix&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice that I left out "reproduce it locally". This is often not an option for production bugs which work on a separate DB in "real world" conditions. In our case local debugging is very difficult due to the multiple separate servers that hand off tasks to one another.&lt;br&gt;
The second option is usually best but it includes a huge risk: what if the fix fails?&lt;/p&gt;

&lt;p&gt;Since production deployment is often a slow process that requires a QA cycle this compounds the problem. You make a fix then have to wait for hours only to find out that you got it wrong... This used to be us.&lt;/p&gt;
&lt;h2&gt;
  
  
  Enter Lightrun
&lt;/h2&gt;

&lt;p&gt;So a couple of years ago I met two young founders who had an idea on how to solve this problem. Essentially, it's a new kind of debugger that works very differently from a regular debugger. &lt;br&gt;
The gist of this is that your app constantly runs in a production debugging mode, the overhead is barely noticeable. A secure agent connects the app to the cloud and lets you debug in a special way. &lt;br&gt;
E.g. instead of breakpoints you have snapshots. They don't "break". They provide you with a stack trace of the thread and the variable state at the given time. You can also inject log statements, count executions and even do simple profiling on methods or blocks of code.&lt;/p&gt;

&lt;p&gt;I was so impressed by this idea that I decided to join the team and now hold two jobs (at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;Codename One&lt;/a&gt; and at &lt;a href="https://www.lightrun.com" rel="noopener noreferrer"&gt;Lightrun&lt;/a&gt;). The cool thing is that my job at Codename One is now much easier thanks to Lightrun.&lt;/p&gt;
&lt;h2&gt;
  
  
  Bugs in Production...
&lt;/h2&gt;

&lt;p&gt;90% of my work at Codename One is putting out fires. A subscriber writes to our support complaining about a failure of a build or push servers etc. This used to be very hard to debug. We would spend hours reading huge logs and guessing&lt;br&gt;
Then we'd deploy additional logs, ask the user to send a new build and then reread the logs to figure out what the hell went wrong. Some of the guessing is always there but with Lightrun the whole process is practically instant.&lt;br&gt;
With Lightrun we can just set a conditional snapshot e.g.&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%2Fu6ik4ha6b8cxbzhfpyno.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%2Fu6ik4ha6b8cxbzhfpyno.png" alt="Debugging a failure in push" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the case above I can grab a snapshot when a specific user sends a push. This is a conditional breakpoint, that's a useful tool when debugging locally. It's an indispensable tool when debugging in production. We only want to see the information related to a specific user and not all users...&lt;/p&gt;

&lt;p&gt;When the snapshot hits we end up with a stack trace similar to this one (I blacked out private information in the image).&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%2Fw29e7l9922xohoj7q5gm.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%2Fw29e7l9922xohoj7q5gm.png" alt="Snapshot Stack" width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that a Snapshot looks just like a regular breakpoint. It's missing the threads and you can't step over. But you can walk the stack and inspect the values of fields/variables etc. when the application is still running... &lt;br&gt;
Pretty darn useful!&lt;br&gt;
Instead of stepping over you can just add multiple snapshots or even inject logs to print information when hitting a specific line. Including simple expressions such as: &lt;code&gt;"The value is {obj.getValue()}"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;My immediate thought was "won't this be expensive?". &lt;br&gt;
It isn't. If we have a very complex/expensive expressions that prints too much per second or uses problematic (e.g. recursive) logic,  Lightrun is smart enough to limit itself so the expression won't take too much CPU. It protects you from shooting yourself in the foot... &lt;br&gt;
As a result of that there's no noticeable performance overhead and you can work without worrying. But I digress, lets go back to the push server example above.&lt;/p&gt;
&lt;h2&gt;
  
  
  Unexpected Bug
&lt;/h2&gt;

&lt;p&gt;When I started this post I wanted to reproduce a debugging session for a user problem but as I was grabbing the screenshots for the session I noticed something weird in the stack. The value of &lt;code&gt;subscriptionLevel&lt;/code&gt; was gibberish. It was way too large.&lt;/p&gt;

&lt;p&gt;Turns out we had a bug in reading the user subscription level when sending push messages. That meant that quotas and rate limits weren't applied at all in our push server!&lt;br&gt;
I'm sure we lost income because of this bug, users whose subscription elapsed could still send push messages at volume without a problem. Ugh!&lt;br&gt;
This is one of the coolest benefits of Lightrun, it lets you see clearly into the running system and verify your expectations. &lt;/p&gt;
&lt;h2&gt;
  
  
  Expected User Problem
&lt;/h2&gt;

&lt;p&gt;The real problem I debugged relates to that line above. A user stopped getting iOS push messages. I just placed a Snapshot (AKA breakpoint) with his token, then inspected the values sent to the push server.&lt;br&gt;
I could then see the URL of the push certificate and could instantly verify that it expired. This was easier to do than debugging locally!&lt;/p&gt;
&lt;h2&gt;
  
  
  The Deeper Pipeline
&lt;/h2&gt;

&lt;p&gt;User issues are very important but issues that no one is aware of are possibly even more important...&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%2F0n0f8mwsfdz0nl36uv00.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%2F0n0f8mwsfdz0nl36uv00.png" alt="Exception Monitoring" width="800" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lightrun also provides a way to detect exceptions (caught or uncaught). We can review the stack traces periodically to see if there are errors that we didn't detect. This isn't a unique feature to Lightrun, but it fits perfectly as you can easily trace a problem you saw in the stack traces.&lt;/p&gt;
&lt;h2&gt;
  
  
  Metrics
&lt;/h2&gt;

&lt;p&gt;Lightrun also includes metrics such as counters, tictocs and method duration measurements. This is super useful for micro-benchmarks in production but we don't need this as much in Codename One. Our backend is relatively simple and these never came up.&lt;/p&gt;
&lt;h2&gt;
  
  
  Installing Lightrun 
&lt;/h2&gt;

&lt;p&gt;So, there has to be a downside right?&lt;/p&gt;

&lt;p&gt;Installing the plugin in the IDE and signing up is very easy... But agent setup is still challenging. I spent a lot of time trying to get it to work on all our servers. Admittedly the Codename One architecture is pretty complex filled with a lot of legacy and pre-docker deployment choices. Prepare yourself for a bit of work. It might be trivial but you might need some help from the web chat support channel (which is super responsive)...&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%2Fz3ud1agmx6ve69acifel.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%2Fz3ud1agmx6ve69acifel.png" alt="Signup Page on app.lightrun.com" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you sign up at &lt;a href="https://app.lightrun.com/" rel="noopener noreferrer"&gt;https://app.lightrun.com/&lt;/a&gt; you get a wizard that verifies you performed the steps correctly:&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%2Fc9kitutrdqb1dkxk33q1.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%2Fc9kitutrdqb1dkxk33q1.png" alt="Install the Plugin and Login" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To pass the first step you need to install the IntelliJ plugin from the marketplace &lt;a href="https://plugins.jetbrains.com/plugin/16477-lightrun" rel="noopener noreferrer"&gt;https://plugins.jetbrains.com/plugin/16477-lightrun&lt;/a&gt;. You then need to restart the IDE, open the Lightrun tool window on the right hand side and press the login button.&lt;br&gt;
Once logged in, the Next button in the wizard will become enabled and you could move on to the agent installation step. This is the hard part...&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%2Fptpd7riteorb5quzisn3.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%2Fptpd7riteorb5quzisn3.jpeg" alt="Agent Install instructions, notice I erased private information" width="800" height="647"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The agent is typically installed on your server and not on your local machine. So when I reached this page "Mac" was selected. I had to explicitly select the Linux page and copy the script that installs the agent on Linux machines.&lt;br&gt;
 &lt;br&gt;
The next step is to SSH to the machine and run the script. It creates an "agent" directory which we'll use when binding the agent. The gist of this is that we need to add the &lt;code&gt;-agentpath&lt;/code&gt; argument to the JVM. That's very simple if your deployment has a "java" command invocation at some point but if your running as a service or within a container that might not be so simple.&lt;/p&gt;

&lt;p&gt;Once you do that the "Next" option will be enabled and you would be able to use Lightrun. &lt;br&gt;
But there are more complex cases, I would recommend reviewing the list &lt;a href="https://docs.lightrun.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;One example is our older Tomcat server used for push. I had to edit &lt;code&gt;catalina.sh&lt;/code&gt; and add something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;JAVA_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JAVA_OPTS&lt;/span&gt;&lt;span class="s2"&gt; -agentpath:/home/username/agent/lightrun_agent.so= - lightrun_extra_class_path=/home/username/apache-tomcat/webapps/myapp.war"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the extra option of &lt;code&gt;--lightrun_extra_class_path&lt;/code&gt; (that's 2 minus signs) which we use to explicitly state the classpath. You might need that if things aren't auto-detected properly.&lt;br&gt;
This let the agent run but I got no variables in my stack traces... Turns out I had to recompile the code with full debug options turned on (&lt;code&gt;-g&lt;/code&gt;). This was a bit of a challenge in Maven. The solution was to add these properties:&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;properties&amp;gt;&lt;/span&gt;
 ...
 &lt;span class="nt"&gt;&amp;lt;maven.compiler.debug&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/maven.compiler.debug&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;debug&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/debug&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;debugLevel&amp;gt;&lt;/span&gt;lines,vars,source&lt;span class="nt"&gt;&amp;lt;/debugLevel&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;maven.compiler.debuglevel&amp;gt;&lt;/span&gt;lines,vars,source&lt;span class="nt"&gt;&amp;lt;/maven.compiler.debuglevel&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can test that &lt;code&gt;-g&lt;/code&gt; is missing using code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;javap &lt;span class="nt"&gt;-classpath&lt;/span&gt; Project/target/classes &lt;span class="nt"&gt;-v&lt;/span&gt; pkg.ClassName | &lt;span class="nb"&gt;grep &lt;/span&gt;LocalVariableTable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this prints nothing then the class doesn't contain debug information.&lt;br&gt;
Again, this is an involved process. I strongly suggest engaging support while going through it.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;The reason we don't rush to production is the tedious and slow process of fixing production issues. But this creates an overly complex multi-branch support structure that ends up making matters worse. &lt;br&gt;
If we can debug quickly in the production and fix right away our overall stability increases while keeping a low overhead. IMO Deploying a server today without debugging tools is akin to coding without an IDE. You can do that but you're missing out on modern advancements in our field.&lt;br&gt;
At &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;Codename One&lt;/a&gt; our user support tasks are resolved much faster and more effectively thanks to &lt;a href="https://www.lightrun.com/" rel="noopener noreferrer"&gt;Lightrun&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>java</category>
      <category>cloud</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Build Cross-Platform Native Mobile Apps using Java/Kotlin for iOS, Android, Desktop &amp; Web</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Tue, 18 May 2021 19:24:05 +0000</pubDate>
      <link>https://forem.com/codename_one/build-cross-platform-native-mobile-apps-using-java-kotlin-for-ios-android-desktop-web-5g3j</link>
      <guid>https://forem.com/codename_one/build-cross-platform-native-mobile-apps-using-java-kotlin-for-ios-android-desktop-web-5g3j</guid>
      <description>&lt;p&gt;The new Codename One Maven build makes building native mobile and desktop apps trivial… It’s also free and open source to boot!&lt;/p&gt;

&lt;p&gt;We can get started by opening start.codenameone.com&lt;/p&gt;

&lt;p&gt;Where we select the package name for our app. This is very important as it can’t be changed once the app is submitted to a store!&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%2F40nes3b3g6syteibxjs5.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%2F40nes3b3g6syteibxjs5.jpeg" alt="start.codenameone.com" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We enter the name to the app and download a zip containing the maven project, notice that you need to pick Kotlin if you wish to build a Kotlin app…&lt;/p&gt;

&lt;p&gt;We can now open the app in any IDE, I used IntelliJ/IDEA but you can use Eclipse, NetBeans, or even VSCode. Most of the important code in the project is under the common directory.&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%2Fjavarevisited.blogspot.com%2F2018%2F09%2Ftop-5-courses-to-learn-intellij-idea-java-and-android-development.html%23axzz6A8Vy1sea" 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%2Fjavarevisited.blogspot.com%2F2018%2F09%2Ftop-5-courses-to-learn-intellij-idea-java-and-android-development.html%23axzz6A8Vy1sea" alt="The project opened in IntelliJ/IDEA. Form creation highlighted" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Right now it only has the main class under the package you selected which in my case is com.example.myapp. Let’s look at the start method within the main app which creates a hello world form.&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%2Fvyr7lnpntbbnsjs6ggsw.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%2Fvyr7lnpntbbnsjs6ggsw.png" alt="Main Source code" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This code creates a Form, which is the root component for a Codename One app, and shows it. It also adds a “Hello World” label under the “Hi World” title.&lt;/p&gt;

&lt;p&gt;The maven target “Run in Simulator” lets us run the simulator and produces this result:&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%2F3of4qd12y1hgjhgwtpni.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%2F3of4qd12y1hgjhgwtpni.jpeg" alt="Simulator" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are many other maven targets we can use to do pretty much everything you will need and I will go through a few of them, but first I want to show off the ease of Codename One programming…&lt;/p&gt;

&lt;p&gt;We can change the code above to this:&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%2Fi088wrdmwnbwwt4n6zk9.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%2Fi088wrdmwnbwwt4n6zk9.png" alt="Changes in the source" width="800" height="253"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We add a button called “sayHi” and add a listener to an event.&lt;/p&gt;

&lt;p&gt;In the event callback we show a dialog. This is pretty intuitive and works exactly as you would expect…&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%2Fi6x3rnbhvmxry1atovao.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%2Fi6x3rnbhvmxry1atovao.png" alt="Dialog showing after clicking the button" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can place a breakpoint, debug and even make changes to the code and see it update on the fly (this requires a bit of work but does in fact work). But one of the cooler things is the ease of styling.&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%2Fslfhtudiiwyjnc0388xx.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%2Fslfhtudiiwyjnc0388xx.jpeg" alt="theme.css enables styling the entire UI on the fly" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Setting the button color to red in the css produces an instant change without recompiling, reloading or anything:&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%2Ftbs7enkfjrvg799d06l0.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%2Ftbs7enkfjrvg799d06l0.png" alt="Results of styling the color to red" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To build a native app we can select the appropriate target such as Android build, iOS, Mac Windows etc. This sends a build to Codename One servers which have all the tools installed and let you build iOS apps without a Mac. You can also generate a local build which I’ll below. Notice I’m simplifying a bit, since you also need a certificate. That’s a bit of a painful subject in general. Codename One makes that relatively simple with wizards etc. but that’s a bit of a big subject.&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%2Fmtguh924fge561rfd58z.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%2Fmtguh924fge561rfd58z.jpeg" alt="Sending a native build" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can track the results in three places, the first is the build cloud from where you can download the resulting file, scan the QR code to install it on the device, copy or email the link.&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%2F9tdzzn8nh8y6hbaz5qyz.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%2F9tdzzn8nh8y6hbaz5qyz.png" alt="Cloud Build Results: https://cloud.codenameone.com/secure/index.html" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second way is through Codename One Settings which you can launch via maven too:&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%2F1eu3w55bn8fbg7sdb6ud.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%2F1eu3w55bn8fbg7sdb6ud.jpeg" alt="Launching Codename One Settings" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This launches a desktop app that’s a “one stop shop” for controlling Codename One. You can define project settings, generate certificates, monitor builds etc.&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%2Fk1ny53coow03pz7lbl2d.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%2Fk1ny53coow03pz7lbl2d.jpeg" alt="Device Builds section of Codename One Settings AKA Control Center" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There’s also a native Android app which you can use to track builds and install them. For iOS you can open the web URL which will work well on device.&lt;/p&gt;

&lt;p&gt;Finally, we can build a native app without the build cloud. We can run the Xcode iOS project target or the Gradle Android Project maven target.&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%2Fwdsplwrh380a6kjmjz3g.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%2Fwdsplwrh380a6kjmjz3g.jpeg" alt="xcode ios project maven target" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After running the Xcode project Apples Xcode environment launches seamlessly with the generated native iOS project which we can run in the native iOS simulator or on the device! Notice that this requires a Mac. The Android Studio equivalent works everywhere…&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%2Fsnwltdg7o8lwxgvgaewz.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%2Fsnwltdg7o8lwxgvgaewz.jpeg" alt="The native project in xcode" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All of the above is also in this tutorial video if you prefer that:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.youtube.com/watch?v=rl6z7DD2-vg" rel="noopener noreferrer"&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%2F6xljd9s4mv90wpbfcy85.jpg" alt="Build Cross-Platform Native Mobile Apps using Java/Kotlin for iOS, Android, Desktop &amp;amp; Web" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Note
&lt;/h2&gt;

&lt;p&gt;Everything I wrote about is open source in our github project where we discuss a lot more. If you think we did a good job we could use your help in &lt;a href="//github.com/codenameone/CodenameOne/"&gt;starring&lt;/a&gt; and promoting our work here.&lt;/p&gt;

&lt;p&gt;If you have any questions please follow the codenameone tag on stackoverflow and ask anything with that tag. I make an effort to answer everything I can.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>java</category>
      <category>opensource</category>
      <category>kotlin</category>
    </item>
  </channel>
</rss>
