<?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: Shai Almog</title>
    <description>The latest articles on DEV Community by Shai Almog (@codenameone).</description>
    <link>https://dev.to/codenameone</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%2F417973%2Fa7b22dd9-5565-48f5-bfab-7e5035b3888f.png</url>
      <title>DEV Community: Shai Almog</title>
      <link>https://dev.to/codenameone</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/codenameone"/>
    <language>en</language>
    <item>
      <title>Native Linux, Apple Watch, A Game Builder And Crash Protection</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Fri, 26 Jun 2026 13:40:18 +0000</pubDate>
      <link>https://dev.to/codenameone/native-linux-apple-watch-a-game-builder-and-crash-protection-2i4c</link>
      <guid>https://dev.to/codenameone/native-linux-apple-watch-a-game-builder-and-crash-protection-2i4c</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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F97pw5hzprfce034h9nnh.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F97pw5hzprfce034h9nnh.jpg" alt="Native Linux, Apple Watch, A Game Builder And Crash Protection" width="799" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This week brings a native Linux desktop port, an Apple Watch and Wear OS port, a visual Game Builder with a high-level gaming API, and a new crash-protection system, with a tutorial following each one over the coming days. There is also a large piece of work that you mostly should not have noticed: we rebuilt the build cloud, and that rebuild caused a few failed builds along the way. More on that below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A native Linux desktop port
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5239" rel="noopener noreferrer"&gt;PR #5239&lt;/a&gt; adds a native Linux port, the structural twin of the native Windows port we shipped last week. The same ParparVM pipeline that turns your Java/Kotlin bytecode into C, here targeting Linux through GTK3, Cairo, Pango and GdkPixbuf for rendering, OpenGL ES for 3D, GStreamer for media and camera, and WebKitGTK for the browser component. There is no JVM on the target machine: it is a single self-contained ELF you launch like any other program.&lt;/p&gt;

&lt;p&gt;We expected this to be painful, and parts of it were, but Linux turned out to be mostly developer friendly. A non-trivial app fits in 5MB, and on my Linux machine it started faster than most of the GNOME native apps already installed; thanks to the default material design theme it tends to look better 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fogbxkkohh9kx34fwfdjt.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fogbxkkohh9kx34fwfdjt.png" alt="A Codename One app rendering natively on Linux via GTK3 and Cairo" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most things just work, including the camera and 3D. The hard part of Linux is not the rendering, it is packaging and dependencies. We deliberately stayed out of the package-manager rabbit hole and ship one native binary you launch directly, the same model as Windows. The other classic Linux problem is glibc; if that sentence means nothing to you, consider yourself fortunate. We solved it by compiling against a very old glibc (around &lt;code&gt;GLIBC_2.17&lt;/code&gt;, from 2013) and linking GTK3 dynamically, both of which are present on essentially every desktop. We also support &lt;strong&gt;musl for Alpine&lt;/strong&gt;, and both &lt;strong&gt;x64 and arm64&lt;/strong&gt;. Like the other ports, this is not a local-only path: the Linux target builds on the build cloud, and new projects from the Initializr come wired for it. The architecture and the build are covered in .&lt;/p&gt;

&lt;h2&gt;
  
  
  Codename One on your wrist: Apple Watch and Wear OS
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5252" rel="noopener noreferrer"&gt;PR #5252&lt;/a&gt; adds a proper native Apple Watch (watchOS) port, alongside Wear OS support on the Android side. Apple treats watch programming as a completely separate discipline from phone or desktop programming: a different API, a different lifecycle, and UI metaphors we take for granted that simply do not exist on the watch (no text field as you know it, and no browser). That is a defensible design, and it raises a fair question: what is the point of a watch API at all?&lt;/p&gt;

&lt;p&gt;The answer is that reuse still happens. Many well known apps skip the watch entirely because it is such a chore, yet the amount of work a watch UI actually needs is small, and smaller still with Codename One. watchOS has no UIKit views, no OpenGL ES and no Metal, so the port ships a dedicated &lt;strong&gt;Core Graphics rendering backend&lt;/strong&gt; and hosts the Codename One runtime inside a SwiftUI shell. The same Java code, branched with &lt;code&gt;CN.isWatch()&lt;/code&gt;, renders real Codename One UI on the watch:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz07cjtigdzvp8t9kj7ps.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz07cjtigdzvp8t9kj7ps.png" alt="A Codename One UI rendered on the Apple Watch simulator through the Core Graphics backend" width="440" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is a screenshot from our test framework, which was never designed for a watch: it still has a text field. Because that is a Codename One text field it renders correctly and "just works" right up until you try to edit in it, which on a watch would not give the result you want; a real watch UI would simply leave it out.&lt;/p&gt;

&lt;p&gt;Wear OS is simpler: a Wear OS app is an ordinary Android app, so the existing Android port renders it with the same pipeline it uses on phones. You enable each side with one build hint, and with the hints off your phone build is byte-for-byte unchanged. Both wearables are covered in detail in .&lt;/p&gt;

&lt;h2&gt;
  
  
  A visual Game Builder
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5253" rel="noopener noreferrer"&gt;PR #5253&lt;/a&gt; adds a visual game level editor on top of the &lt;code&gt;com.codename1.gaming&lt;/code&gt; API from last week, plus a high-level data model and a streaming engine for large worlds. Instead of hand-placing every sprite in code, you draw the level, tag objects with the numbers your game needs (&lt;code&gt;lives&lt;/code&gt;, &lt;code&gt;value&lt;/code&gt;, &lt;code&gt;speed&lt;/code&gt;), and the editor saves a small data file the runtime plays. Your code shrinks to the part that is actually yours: the rules.&lt;/p&gt;

&lt;p&gt;This is the special case in this week's release. Tuesday's post is a full overview of the editor, the data model, and the streaming engine for large worlds, and then &lt;strong&gt;a three-part tutorial series starts Thursday&lt;/strong&gt; rather than a single follow-up. The first tutorial builds a playable 2D platformer, "Duke's Coffee Run", from an empty scene to a running game, including the part most tutorials skip: bringing in real art and slicing an animated sprite sheet for Duke.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5g2uyn0ijwcxewqx5u4w.gif" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5g2uyn0ijwcxewqx5u4w.gif" alt="Duke's Coffee Run, a 2D platformer built with the Game Builder" width="420" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two more parts follow on the next Thursdays: a blackjack card game, then a first-person 3D dungeon that scales up to large streaming worlds. The full picture of what the builder is and how it fits together is in . One important caveat: &lt;strong&gt;the Game Builder and its high-level APIs are beta&lt;/strong&gt;. We are actively improving them and we want your feedback on the editor, the API shape, and the asset workflow. Tell us what works and what gets in your way through the &lt;a href="https://github.com/codenameone/CodenameOne/issues" rel="noopener noreferrer"&gt;issue tracker&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Seamless crash protection
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5001" rel="noopener noreferrer"&gt;PR #5001&lt;/a&gt; replaces the old email-based crash protection tool with a new on-device client, &lt;code&gt;com.codename1.crash&lt;/code&gt;, that we think leapfrogs the dedicated tools in this space. As we add more platforms, testing across all of them becomes a bigger challenge; this is the tool for dealing with that, and it is built to be seamless.&lt;/p&gt;

&lt;p&gt;It is seamless in three senses. You do not wire up anything complex: the build servers do the heavy lifting. It connects to GitHub issues instead of sending a barrage of emails. And it symbolicates native crashes on every operating system we support, so a faulting address on iOS, Android, Windows or Linux comes back as a readable stack. On the device, reports are written to storage before they are sent and only deleted after the server confirms receipt, so nothing is lost to a flaky connection; a failed send is retried on the next launch and the server deduplicates 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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBBWyJBcHAgY3Jhc2hlcyBvbiBkZXZpY2UiXSAtLT4gQlsiV3JpdGUgcmVwb3J0IHRvIFN0b3JhZ2Ugd2l0aCBldmVudElkIl0KICAgIEIgLS0-IENbIlBPU1QgdG8gdGhlIGNyYXNoIGVuZHBvaW50Il0KICAgIEMgLS0-fDJ4eCBjb25maXJtZWR8IERbIkRlbGV0ZSB0aGUgc3RvcmVkIHJlcG9ydCJdCiAgICBDIC0tPnxvZmZsaW5lIG9yIGZhaWxlZHwgRVsiUmV0cnkgb24gbmV4dCBsYXVuY2gsIHNlcnZlciBkZWR1cHMgYnkgZXZlbnRJZCJdCiAgICBGWyJSZWxlYXNlIGJ1aWxkIG9uIHRoZSBjbG91ZCJdIC0tPiBHWyJVcGxvYWQgbWFwcGluZy50eHQgLyBkU1lNIHN5bWJvbHMiXQogICAgQyAtLT4gSFsiU2VydmVyIHN5bWJvbGljYXRlcyB0aGUgbmF0aXZlIHN0YWNrIl0KICAgIEcgLS0-IEgKICAgIEggLS0-IElbIkEgR2l0SHViIGlzc3VlLCBub3QgYW4gZW1haWwiXQ%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" 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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBBWyJBcHAgY3Jhc2hlcyBvbiBkZXZpY2UiXSAtLT4gQlsiV3JpdGUgcmVwb3J0IHRvIFN0b3JhZ2Ugd2l0aCBldmVudElkIl0KICAgIEIgLS0-IENbIlBPU1QgdG8gdGhlIGNyYXNoIGVuZHBvaW50Il0KICAgIEMgLS0-fDJ4eCBjb25maXJtZWR8IERbIkRlbGV0ZSB0aGUgc3RvcmVkIHJlcG9ydCJdCiAgICBDIC0tPnxvZmZsaW5lIG9yIGZhaWxlZHwgRVsiUmV0cnkgb24gbmV4dCBsYXVuY2gsIHNlcnZlciBkZWR1cHMgYnkgZXZlbnRJZCJdCiAgICBGWyJSZWxlYXNlIGJ1aWxkIG9uIHRoZSBjbG91ZCJdIC0tPiBHWyJVcGxvYWQgbWFwcGluZy50eHQgLyBkU1lNIHN5bWJvbHMiXQogICAgQyAtLT4gSFsiU2VydmVyIHN5bWJvbGljYXRlcyB0aGUgbmF0aXZlIHN0YWNrIl0KICAgIEcgLS0-IEgKICAgIEggLS0-IElbIkEgR2l0SHViIGlzc3VlLCBub3QgYW4gZW1haWwiXQ%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="874" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Personal data is scrubbed on the device before anything leaves it: emails are partially redacted and long digit runs are collapsed, with rules you can override. It is opt-in and off by default; turning it on is two lines, and the full walkthrough is in .&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;CrashProtection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;install&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;CrashProtection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setEnabled&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Behind the scenes: a rebuilt build cloud
&lt;/h2&gt;

&lt;p&gt;Codename One was founded in 2012, back when Docker was a brand-new alpha product that few people had heard of, and a lot of the infrastructure underneath the build cloud dates back to that era. This week we did a major rebuild and re-architecture of it. The transition caused a few failed builds, and if one of yours was among them, we are sorry for the disruption.&lt;/p&gt;

&lt;p&gt;The end result is completely new infrastructure that is far easier to update going forward, with better isolation and security. We are nearly finished with the Mac side (that work continues over this weekend), and every other platform is done except for UWP and the Windows desktop builds. If a build behaves differently from how it did before, please tell us right away, ideally through the chat on our website, so we can act quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initializr now runs on the JavaScript port
&lt;/h2&gt;

&lt;p&gt;We quietly switched the &lt;a href="https://start.codenameone.com" rel="noopener noreferrer"&gt;Codename One Initializr&lt;/a&gt; over to the new JavaScript port, and we hope you did not notice the difference. That is the goal: the JavaScript port should feel like every other port.&lt;/p&gt;

&lt;p&gt;It is also one of the hardest ports we have ever worked on. Every percentage point of compatibility and every bit of performance is a genuine fight. The work is ongoing, and the aim is to get the JavaScript port aligned with the rest and launch it in the near future.&lt;/p&gt;

&lt;h2&gt;
  
  
  From the community
&lt;/h2&gt;

&lt;p&gt;Francesco Galgani published another thoughtful piece, &lt;a href="https://www.informatica-libera.net/content/java-was-supposed-to-free-us-from-the-operating-system-today-codename-one-is-getting-there" rel="noopener noreferrer"&gt;Java was supposed to free us from the operating system; today Codename One is getting there&lt;/a&gt;. It is worth your time, and we are grateful for the writing and the perspective.&lt;/p&gt;

&lt;p&gt;A note on cadence: community activity is slowing down as everyone heads into the summer holidays, and we are not sure how quickly progress will continue through these months. We are still very much here, and we are looking for feedback on all of the above, so this is a good time to send it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upcoming attractions
&lt;/h2&gt;

&lt;p&gt;The tutorials follow this post; each link below goes live on its day:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Saturday.&lt;/strong&gt; . PR &lt;a href="https://github.com/codenameone/CodenameOne/pull/5239" rel="noopener noreferrer"&gt;#5239&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sunday.&lt;/strong&gt; . PR &lt;a href="https://github.com/codenameone/CodenameOne/pull/5252" rel="noopener noreferrer"&gt;#5252&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monday.&lt;/strong&gt; . PR &lt;a href="https://github.com/codenameone/CodenameOne/pull/5001" rel="noopener noreferrer"&gt;#5001&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tuesday.&lt;/strong&gt; . PR &lt;a href="https://github.com/codenameone/CodenameOne/pull/5253" rel="noopener noreferrer"&gt;#5253&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thursday.&lt;/strong&gt; . The first of three parts. PR &lt;a href="https://github.com/codenameone/CodenameOne/pull/5253" rel="noopener noreferrer"&gt;#5253&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;The issue tracker is &lt;a href="https://github.com/codenameone/CodenameOne/issues" rel="noopener noreferrer"&gt;here&lt;/a&gt; and it is the best place to reach us right now. The discussion forum is &lt;a href="https://www.codenameone.com/discussion-forum.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;, and the Build Cloud console is at &lt;a href="https://cloud.codenameone.com/console/index.html" rel="noopener noreferrer"&gt;&lt;code&gt;/console/&lt;/code&gt;&lt;/a&gt;. The &lt;a href="https://www.codenameone.com/playground/" rel="noopener noreferrer"&gt;Playground&lt;/a&gt;, &lt;a href="https://www.codenameone.com/initializr/" rel="noopener noreferrer"&gt;Initializr&lt;/a&gt;, and &lt;a href="https://www.codenameone.com/skindesigner/" rel="noopener noreferrer"&gt;Skin Designer&lt;/a&gt; are where they have always been.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Print Anywhere, And Put Your Cards In Apple Wallet</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Tue, 23 Jun 2026 13:51:48 +0000</pubDate>
      <link>https://dev.to/codenameone/print-anywhere-and-put-your-cards-in-apple-wallet-24on</link>
      <guid>https://dev.to/codenameone/print-anywhere-and-put-your-cards-in-apple-wallet-24on</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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn6lcn0da5wq1cf61npyq.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn6lcn0da5wq1cf61npyq.jpg" alt="Print Anywhere, And Put Your Cards In Apple Wallet" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last two items in this week's release are platform integrations that business apps ask for constantly: printing a document, and getting a payment card into Apple Wallet. Both are the kind of feature where the cross-platform story usually ends and a pile of per-platform native code begins. Both are now core APIs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Printing: one call, five platforms
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5217" rel="noopener noreferrer"&gt;PR #5217&lt;/a&gt; adds &lt;code&gt;com.codename1.printing&lt;/code&gt;, a deliberately small API: hand a document to the platform's printing system, typically through the native print dialog, and hear back once about how it went.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Printer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isPrintingSupported&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Printer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printPDF&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reportPath&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isFailed&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;ToastBar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;showErrorMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Print failed: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getError&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;});&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Printer.printPDF(path, listener)&lt;/code&gt; and &lt;code&gt;Printer.print(path, mimeType, listener)&lt;/code&gt; print a file from &lt;code&gt;FileSystemStorage&lt;/code&gt;; &lt;code&gt;Printer.printImage(image, listener)&lt;/code&gt; takes any Codename One &lt;code&gt;Image&lt;/code&gt; and handles the encoding for you. The listener is invoked exactly once, on the EDT, with a &lt;code&gt;PrintResult&lt;/code&gt; that is completed, cancelled, or failed.&lt;/p&gt;

&lt;p&gt;What backs the API on each platform:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;UIPrintInteractionController&lt;/code&gt;, presented properly on iPad&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Android&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PrintManager&lt;/code&gt; print framework: PDFs stream through a print adapter, images go through the platform print helper&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simulator / desktop&lt;/td&gt;
&lt;td&gt;The Java printing pipeline with the native dialog, with real cancel detection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web&lt;/td&gt;
&lt;td&gt;The browser's print flow via a hidden frame&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows native&lt;/td&gt;
&lt;td&gt;The Win32 print dialog, with PDFs rendered at printer resolution through the Windows PDF engine&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That last row is worth a beat: the printing API shipped in the same week as &lt;a href="https://www.codenameone.com/blog/native-windows-port-no-jvm/" rel="noopener noreferrer"&gt;the native Windows port&lt;/a&gt; and already covers it, dialog, spooling, PDF rasterization and all.&lt;/p&gt;

&lt;p&gt;One caveat the JavaDoc spells out per method: "completed" means the document was handed to the printing system. Some platforms don't expose what happened inside the native dialog afterward, so completion there is reported best-effort. Design your UX around handing off, not around confirming pages hit paper.&lt;/p&gt;

&lt;p&gt;There is a &lt;code&gt;PrinterSample&lt;/code&gt; in the repository that prints a generated image and a downloaded PDF if you want a starting point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apple Wallet: provision cards from inside the Wallet app
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5227" rel="noopener noreferrer"&gt;PR #5227&lt;/a&gt; is for card issuers: banks and fintechs whose users want to add their cards to Apple Wallet. Apple supports this from inside the Wallet app itself (the "From apps on your iPhone" section), but it requires shipping an &lt;em&gt;issuer-provisioning app extension&lt;/em&gt;, a separate binary with a strict contract. That has historically meant Xcode, Objective-C, and a long appendix of plist incantations.&lt;/p&gt;

&lt;p&gt;Now it means build hints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;ios.wallet.extension&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;ios.wallet.appGroup&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;group.com.mybank.app&lt;/span&gt;
&lt;span class="py"&gt;ios.wallet.issuerEndpoint&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;https://api.mybank.com/wallet/provision&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With those set, the iOS build generates Apple's extension pair as fixed, framework-maintained Objective-C. Add &lt;code&gt;ios.wallet.includeUI=true&lt;/code&gt; and &lt;code&gt;ios.wallet.authEndpoint=...&lt;/code&gt; and you also get the optional in-Wallet login screen.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works under the hood
&lt;/h3&gt;

&lt;p&gt;Wallet extensions run in a separate process under a 100-millisecond response deadline, so they cannot spin up your Java code. The design splits the work: your app publishes its card data ahead of time through the new &lt;code&gt;com.codename1.payment.WalletExtension&lt;/code&gt; API, and the extension reads it from the shared App Group when Wallet asks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;WalletExtension&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isSupported&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;WalletExtension&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPassEntries&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WalletPassEntry&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;WalletPassEntry&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"card-1234"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Platinum Card"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cardholderName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Jane Doe"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;primaryAccountSuffix&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1234"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;paymentNetwork&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"visa"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;artPng&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cardArt&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;});&lt;/span&gt;
    &lt;span class="nc"&gt;WalletExtension&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setRequiresAuthentication&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;WalletExtension&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setAuthToken&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The one step that genuinely needs your backend, producing the encrypted pass payload from Apple's certificates and nonce, is a JSON POST to the issuer endpoint you host. And for teams with special requirements, ten injection hints let you insert custom Objective-C at marked points in every extension callback without forking the generated code.&lt;/p&gt;

&lt;p&gt;On the token in that snippet: treat it like any credential. Fetch it from your backend at runtime, hand it to &lt;code&gt;WalletExtension&lt;/code&gt; for the extension to use, and never hard-code it or check it into source. Anything baked into a shipped binary is effectively public.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bring your own extension
&lt;/h3&gt;

&lt;p&gt;Some issuers receive a prebuilt extension from an SDK vendor. The generic iOS app-extension mechanism (&lt;code&gt;ios/app_extensions/&amp;lt;Name&amp;gt;/&lt;/code&gt; in your project) covers that path, and this release improves it: it now works without CocoaPods, and each extension can carry its own provisioning profile, either as a file in its folder or through a per-extension hint. A bug that could duplicate extension targets in the generated Xcode project when build phases re-ran is also fixed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before you start
&lt;/h3&gt;

&lt;p&gt;Apple gates issuer provisioning behind the restricted &lt;code&gt;payment-pass-provisioning&lt;/code&gt; entitlement, which your developer account must be granted; the new "Apple Wallet Extension" chapter in the developer guide walks through the prerequisites, the endpoint contracts, and both integration modes.&lt;/p&gt;

&lt;p&gt;That wraps the week. Yesterday's post covered &lt;a href="https://www.codenameone.com/blog/native-windows-port-no-jvm/" rel="noopener noreferrer"&gt;the native Windows port&lt;/a&gt;, and the &lt;a href="https://www.codenameone.com/blog/native-java-win32-3d-gaming-printing-and-wallet/" rel="noopener noreferrer"&gt;release post&lt;/a&gt; has the full index: the portable 3D API, the game development API, native Windows, and these two. As always, the &lt;a href="https://github.com/codenameone/CodenameOne/issues" rel="noopener noreferrer"&gt;issue tracker&lt;/a&gt; is the best place to reach us.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Java To A Native Windows EXE: No JVM, 5MB, x64 And Arm</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Mon, 22 Jun 2026 14:53:40 +0000</pubDate>
      <link>https://dev.to/codenameone/java-to-a-native-windows-exe-no-jvm-5mb-x64-and-arm-4fel</link>
      <guid>https://dev.to/codenameone/java-to-a-native-windows-exe-no-jvm-5mb-x64-and-arm-4fel</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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftfe0o0sl99nblqs6pw2c.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftfe0o0sl99nblqs6pw2c.jpg" alt="Java To A Native Windows EXE: No JVM, 5MB, x64 And Arm" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you were around Java forums in the late nineties you remember the threads. "How do I compile my Java program to an EXE?" was asked constantly, answered badly, and locked periodically. The real answer for most of three decades was: you don't, you ship a JVM. Wrapper tools bundled a runtime next to your jar; the result was a directory pretending to be a program.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This week, &lt;a href="https://github.com/codenameone/CodenameOne/pull/5144" rel="noopener noreferrer"&gt;PR #5144&lt;/a&gt; and &lt;a href="https://github.com/codenameone/CodenameOne/pull/5209" rel="noopener noreferrer"&gt;PR #5209&lt;/a&gt; deliver the answer that the forum threads wanted all along: your Codename One app now compiles to a &lt;strong&gt;standalone native Windows executable with no JVM anywhere&lt;/strong&gt;. Not bundled, not embedded, not downloaded on first run. The same ParparVM pipeline that has compiled our iOS apps to native code for over a decade now translates your Java/Kotlin bytecode to C, compiles it with &lt;code&gt;clang-cl&lt;/code&gt;, and links a single Win32 &lt;code&gt;.exe&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To be explicit about what that executable contains, because "Java desktop app" carries decades of associations: there is no Swing in it, no AWT, no JavaFX, and no runtime to install. It is the full Codename One framework compiled to machine code, and everything in the framework works there, including &lt;a href="https://www.codenameone.com/blog/native-java-win32-3d-gaming-printing-and-wallet/" rel="noopener noreferrer"&gt;the new printing API&lt;/a&gt;, the 3D layer, media, networking, storage, and the browser component.&lt;/p&gt;

&lt;p&gt;A hello world is around 4MB. The complete Initializr application, the real app from our own site, is around 8MB, and here it is running as a native Windows executable:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Edit:&lt;/strong&gt; Since publishing this post we found the binaries were carrying their debug and stack-unwind tables inline. We now split that data into a separate symbol file (kept only to symbolize crashes) and dead-strip code the app never reaches, which brings the sizes down further -- the figures above reflect the leaner build.&lt;/p&gt;
&lt;/blockquote&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feevnhpwy8onbnvyxdk66.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feevnhpwy8onbnvyxdk66.png" alt="The Initializr running as a native Windows executable" width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One build produces &lt;strong&gt;both x64 and arm64&lt;/strong&gt; executables, so the new wave of Arm-based Windows laptops is a first-class target, not an afterthought under emulation.&lt;/p&gt;

&lt;p&gt;This also completes a story we started two weeks ago. &lt;a href="https://www.codenameone.com/blog/mac-native-builds-and-desktop-integration/" rel="noopener noreferrer"&gt;The Mac native target&lt;/a&gt; brought the same no-JVM compilation to macOS through the iOS pipeline; with Windows joining it, the same Java code base now produces real native binaries for both major desktops, plus iOS, Android, and the web. Write once, run anywhere was Java's original promise for the desktop, and it took compiling the JVM out of the picture to actually deliver it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How this differs from GraalVM
&lt;/h2&gt;

&lt;p&gt;GraalVM native-image is a remarkable piece of engineering and the comparison is worth making precisely, because it is not "GraalVM ships a JVM". It doesn't, and it doesn't support reflection everywhere either; it compiles ahead of time against a closed world, much like we do. The difference is scope: GraalVM aims to support as much of the Java language and platform semantics as possible, for any Java workload, with server-side frameworks as the marquee use case. That ambition is what makes it far more complex, and it is why its binaries, while impressive for what they carry, are heavy by app-distribution standards. It is a tool designed for a different purpose, and it is very good at that purpose.&lt;/p&gt;

&lt;p&gt;Our goal is narrower and that is the point. ParparVM compiles &lt;em&gt;your application&lt;/em&gt; against a compact runtime designed for shipping apps to end users: no JIT, no class loader machinery at runtime, a concurrent GC, and only the code your app actually reaches. The output is a lean executable that looks and behaves like a program written natively for the machine, because at that point it is one. It is the same trade-off our iOS port has been making since 2012, battle-tested by every app we have ever shipped through it.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real rendering stack, not a port of a port
&lt;/h2&gt;

&lt;p&gt;The Windows port renders through the platform's own stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Direct2D&lt;/strong&gt; for graphics, with the full Codename One drawing pipeline on top&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DirectWrite&lt;/strong&gt; for text shaping and fonts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct3D 11&lt;/strong&gt; for the &lt;a href="https://www.codenameone.com/blog/portable-3d-graphics-api/" rel="noopener noreferrer"&gt;new portable 3D API&lt;/a&gt;, with HLSL generated and compiled at runtime&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WIC&lt;/strong&gt; for image decoding, &lt;strong&gt;WinHTTP&lt;/strong&gt; for networking, Win32 for storage and the clipboard&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebView2&lt;/strong&gt; backing &lt;code&gt;BrowserComponent&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the ChatView sample rendering through Direct2D and DirectWrite, from the same Java code that produces the iOS and Android versions:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flfkgggu0xljjigbvbdyl.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flfkgggu0xljjigbvbdyl.png" alt="A Codename One app rendering through Direct2D and DirectWrite on Windows" width="784" height="561"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the 3D API on Direct3D 11:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F80tdua8yvzoy8r6tyr62.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F80tdua8yvzoy8r6tyr62.png" alt="The portable 3D API rendering through Direct3D 11 on Windows" width="784" height="561"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The desktop details that make an app feel native are in place too. Mouse-wheel scrolling maps onto Codename One's own tensile scrolling physics through a new shared core API (the JavaSE simulator was refactored onto the same code path, so every desktop port scrolls identically). &lt;code&gt;execute(url)&lt;/code&gt; opens the default browser through the real shell launch services, and &lt;code&gt;dial()&lt;/code&gt; and the messaging APIs hand off to whatever the user has registered for &lt;code&gt;tel:&lt;/code&gt;, &lt;code&gt;sms:&lt;/code&gt;, and &lt;code&gt;mailto:&lt;/code&gt; links (Phone Link, for instance); when no handler is registered they report failure instead of pretending. The file picker is the actual Windows file dialog, and printing (covered in ) goes through the native print dialog.&lt;/p&gt;

&lt;h2&gt;
  
  
  How complete is it?
&lt;/h2&gt;

&lt;p&gt;The port is new, and it should be treated as such; we expect to be shooting down teething issues over the coming weeks, and we want to hear about every one of them. With that said, it covers far more ground than "new port" usually implies. The same screenshot test suite that gates iOS, Android, the web, and the Mac target runs against the Windows port in CI on every change, on both x64 and arm64 runners, with more than 120 screenshot baselines covering components, themes, transitions, graphics, charts, and the 3D API. Almost anything that is viable for a desktop app is expected to work.&lt;/p&gt;

&lt;p&gt;Where a capability genuinely doesn't exist on a desktop machine, the port follows a strict rule: phone-hardware APIs (camera-as-sensor flows, GPS-grade location, contacts, push, biometrics) report themselves as unsupported rather than fabricating data. Existing cross-platform feature-detection code keeps working.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building one
&lt;/h2&gt;

&lt;p&gt;The cloud target is &lt;code&gt;windows-device&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn &lt;span class="nt"&gt;-pl&lt;/span&gt; common package &lt;span class="nt"&gt;-Dcodename1&lt;/span&gt;.platform&lt;span class="o"&gt;=&lt;/span&gt;windows &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-Dcodename1&lt;/span&gt;.buildTarget&lt;span class="o"&gt;=&lt;/span&gt;windows-device
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A regular build returns x64 and arm64 release executables; set the &lt;code&gt;windows.debug&lt;/code&gt; build hint for a single x64 debug build. The build runs on our Linux build servers, which cross-compile the Windows PE directly, and if you're on a Windows machine with the toolchain installed, &lt;code&gt;local-windows-device&lt;/code&gt; does the same build locally.&lt;/p&gt;

&lt;p&gt;There is history here for long-time followers. Codename One has shipped Windows apps before, through UWP and through the JVM-bundled desktop target. This port replaces neither today, but it is the first time the output is what those old forum threads were really asking for: one file, native code, no runtime, double-click and it runs.&lt;/p&gt;

&lt;p&gt;Yesterday's post covered &lt;a href="https://www.codenameone.com/blog/game-development-api-box2d/" rel="noopener noreferrer"&gt;the game development API&lt;/a&gt;, and the &lt;a href="https://www.codenameone.com/blog/native-java-win32-3d-gaming-printing-and-wallet/" rel="noopener noreferrer"&gt;release post&lt;/a&gt; has the full index. The week wraps up with printing and Apple Wallet in .&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Build Games In Java: Sprites, Box2D Physics And Low-Latency Sound</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Sun, 21 Jun 2026 13:33:41 +0000</pubDate>
      <link>https://dev.to/codenameone/build-games-in-java-sprites-box2d-physics-and-low-latency-sound-341i</link>
      <guid>https://dev.to/codenameone/build-games-in-java-sprites-box2d-physics-and-low-latency-sound-341i</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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fogivjca18msitl0sbjk9.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fogivjca18msitl0sbjk9.jpg" alt="Build Games In Java: Sprites, Box2D Physics And Low-Latency Sound" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A confession before the feature tour: for years I was very much against adding gaming to Codename One. I used to work in the gaming industry (&lt;a href="https://en.wikipedia.org/wiki/Jane%27s_USAF" rel="noopener noreferrer"&gt;Jane's USAF&lt;/a&gt;, among others), so this was never about disinterest or not understanding the domain. The opposite: I knew exactly how much a real gaming stack demands, and I felt that tackling it would dilute our focus on being the best cross-platform app framework. But at the rate we have been building up Codename One lately, it has become a manageable and realistic target.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There is also a bigger reason. Java is the ideal game development platform, as proven by Minecraft. It also has serious problems as a game platform, as likewise proven by Minecraft: a heavyweight runtime between you and the machine, painful distribution, and platforms it simply can't reach. With these APIs, and with native compilation that ships your game as a real iOS app, a real Android app, and now &lt;a href="https://www.codenameone.com/blog/native-java-win32-3d-gaming-printing-and-wallet/" rel="noopener noreferrer"&gt;a real Windows executable with no JVM&lt;/a&gt;, we can solve most of those core problems while giving indie developers a royalty-free platform for their games. No engine fees, no revenue share, no install-time runtime: Java in, native game out.&lt;/p&gt;

&lt;p&gt;So here it is. &lt;a href="https://github.com/codenameone/CodenameOne/pull/5166" rel="noopener noreferrer"&gt;PR #5166&lt;/a&gt; adds &lt;code&gt;com.codename1.gaming&lt;/code&gt;: a game loop, sprites, pollable input, sound effects that fire without latency, and physics, built on the &lt;a href="https://www.codenameone.com/blog/portable-3d-graphics-api/" rel="noopener noreferrer"&gt;portable 3D API we introduced yesterday&lt;/a&gt; and the existing media and animation systems, rather than replacing any of them.&lt;/p&gt;

&lt;p&gt;Judging by &lt;a href="https://github.com/codenameone/CodenameOne/issues/5215" rel="noopener noreferrer"&gt;the reactions in the issue tracker&lt;/a&gt;, some of you have been waiting for this one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The sixty-second tour
&lt;/h2&gt;

&lt;p&gt;You subclass &lt;code&gt;GameView&lt;/code&gt;, implement &lt;code&gt;update(double dt)&lt;/code&gt;, and you have a game. Here is the complete logic of a demo where balls drop, fall under gravity, and bounce off the floor and walls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PhysicsDemoView&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;GameView&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;PhysicsWorld&lt;/span&gt; &lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt; &lt;span class="n"&gt;ballImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;makeBall&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xffff5a5f&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nc"&gt;PhysicsDemoView&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;setClearColor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0xff101826&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setupWorld&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getWidth&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getHeight&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;world&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PhysicsWorld&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// gravity in pixels/s^2, downward&lt;/span&gt;
        &lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createBox&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BodyType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;STATIC&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// floor&lt;/span&gt;
        &lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createBox&lt;/span&gt;&lt;span class="o"&gt;(-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BodyType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;STATIC&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// walls&lt;/span&gt;
        &lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createBox&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BodyType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;STATIC&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;dropBall&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;PhysicsBody&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createCircle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BodyType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DYNAMIC&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setRestitution&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.7f&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;Sprite&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Sprite&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ballImage&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPosition&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setLinkedSprite&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;getScene&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getInput&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;wasPointerPressed&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;dropBall&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getInput&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getPointerX&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;getInput&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getPointerY&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what's absent: there is no render code. The &lt;code&gt;Scene&lt;/code&gt; draws its sprites, and each sprite tracks the physics body it is linked to. This is the demo running in the simulator:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1p4okuusfx2y7q0fl4y6.gif" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1p4okuusfx2y7q0fl4y6.gif" alt="Box2D bodies driving sprites in a GameView" width="360" height="779"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The view drops into a normal form like any component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Form&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Gaming Demo"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BorderLayout&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="nc"&gt;PhysicsDemoView&lt;/span&gt; &lt;span class="n"&gt;game&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PhysicsDemoView&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BorderLayout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CENTER&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The game loop
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;GameView&lt;/code&gt; drives its loop through the existing animation system, so it cooperates with the rest of the UI instead of fighting it. It manages the frame rate while running and restores the global value when it stops, and it releases everything automatically when the view is detached from the form.&lt;/p&gt;

&lt;p&gt;For deterministic simulation there is a fixed-timestep mode: set &lt;code&gt;setFixedTimestep(1.0 / 60)&lt;/code&gt; and &lt;code&gt;update&lt;/code&gt; is called with exactly that delta as often as needed, with &lt;code&gt;getInterpolationAlpha()&lt;/code&gt; available for smooth rendering between simulation steps. This is the standard pattern for physics-driven games, and it is one method call here.&lt;/p&gt;

&lt;p&gt;Input is pollable, the way game code wants it. &lt;code&gt;getInput()&lt;/code&gt; exposes level state (&lt;code&gt;isKeyDown&lt;/code&gt;, &lt;code&gt;isPointerDown&lt;/code&gt;, &lt;code&gt;getPointerX/Y&lt;/code&gt;) and per-frame edges (&lt;code&gt;wasKeyPressed&lt;/code&gt;, &lt;code&gt;wasPointerPressed&lt;/code&gt;), aware of game actions (fire, directional pad) across platforms. No listeners, no event-versus-frame mismatch: you ask, every frame, and act.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sprites and scenes
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Sprite&lt;/code&gt; carries position, anchor, rotation, scale, and alpha, plus an axis-aligned bounding box for simple overlap tests. &lt;code&gt;SpriteSheet&lt;/code&gt; slices a packed texture into cached frames, &lt;code&gt;AnimatedSprite&lt;/code&gt; plays them back, and &lt;code&gt;Scene&lt;/code&gt; holds everything in z-order with a camera, so scrolling a level means moving the camera and not repositioning the world.&lt;/p&gt;

&lt;p&gt;It matters where those sprites run. &lt;code&gt;GameView&lt;/code&gt; extends the &lt;code&gt;RenderView&lt;/code&gt; from &lt;a href="https://www.codenameone.com/blog/portable-3d-graphics-api/" rel="noopener noreferrer"&gt;the portable 3D API&lt;/a&gt;, so sprites are not painted pixel by pixel on the CPU: each one is a textured quad composited by the GPU, through Metal on iOS and Mac, OpenGL ES on Android, WebGL on the web, Direct3D 11 on native Windows, and OpenGL through JOGL in the simulator. A sprite's rotation, scale, and alpha are transformation and blending parameters the GPU applies for free, which is why a scene full of spinning, fading, overlapping sprites holds full frame rate, and why the starfield sample below can animate seventy twinkling stars plus particles without breaking a sweat. Where no GPU backend exists, the same code falls back to the software renderer, so correctness never depends on the hardware.&lt;/p&gt;

&lt;p&gt;Because the surface is the 3D surface, the same &lt;code&gt;GameView&lt;/code&gt; also scales up to real 3D: a &lt;code&gt;GameCamera&lt;/code&gt; in perspective mode, &lt;code&gt;Model&lt;/code&gt; instances for meshes, and billboarded sprites that always face the camera. There is no separate "3D mode" to migrate to later; mixing flat sprites and 3D models in one scene is the normal case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Physics: Box2D in the core
&lt;/h2&gt;

&lt;p&gt;We did not write a physics engine, and that is the point: we took the industry standard. Box2D is &lt;a href="https://box2d.org/" rel="noopener noreferrer"&gt;Erin Catto&lt;/a&gt;'s rigid-body engine, the one behind an entire generation of 2D hits, and &lt;a href="https://github.com/jbox2d/jbox2d" rel="noopener noreferrer"&gt;JBox2D&lt;/a&gt; is its faithful Java port. It ships shaded into &lt;code&gt;com.codename1.gaming.physics.box2d&lt;/code&gt;, inside the core with no dependency to add, with the BSD license retained and full attribution in the project &lt;code&gt;NOTICE&lt;/code&gt;. On top of it sits an idiomatic wrapper, &lt;code&gt;PhysicsWorld&lt;/code&gt; / &lt;code&gt;PhysicsBody&lt;/code&gt; / &lt;code&gt;ContactListener&lt;/code&gt;, that makes the two classic Box2D paper cuts disappear: the pixels-to-meters conversion and the flipped y-axis are centralized, so your game code stays entirely in screen coordinates.&lt;/p&gt;

&lt;p&gt;Bodies are created from boxes, circles, polygons, or any Codename One &lt;code&gt;Shape&lt;/code&gt;. Collisions arrive through &lt;code&gt;ContactListener&lt;/code&gt;. And &lt;code&gt;setLinkedSprite&lt;/code&gt; closes the loop: after &lt;code&gt;world.step()&lt;/code&gt;, every linked sprite has already moved to where its body is.&lt;/p&gt;

&lt;p&gt;Pure Java matters here. Because the engine is plain bytecode, it runs unchanged through ParparVM on iOS, through the new Windows port, on Android, and on the web. There is no native physics library to bind, version, or debug per platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sound effects that keep up
&lt;/h2&gt;

&lt;p&gt;Music and sound effects have opposite requirements: music wants streaming, effects want zero latency and overlapping playback. &lt;code&gt;MediaManager&lt;/code&gt; always covered the former; the new &lt;code&gt;SoundPool&lt;/code&gt; covers the latter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;SoundPool&lt;/span&gt; &lt;span class="n"&gt;sfx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SoundPool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// up to 12 overlapping voices&lt;/span&gt;
&lt;span class="nc"&gt;SoundEffect&lt;/span&gt; &lt;span class="n"&gt;blip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sfx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;load&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"audio/wav"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// later, per bounce, with per-drop pitch:&lt;/span&gt;
&lt;span class="n"&gt;sfx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;play&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blip&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.9f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// volume, pan, rate, loop&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each platform backs this with its native low-latency path: &lt;code&gt;android.media.SoundPool&lt;/code&gt; on Android, an &lt;code&gt;AVAudioPlayer&lt;/code&gt; pool on iOS, and a software mixer with volume, pan, and rate control on the desktop. Where no native backend exists, a pure cross-platform fallback over &lt;code&gt;MediaManager&lt;/code&gt; keeps the API functional, and &lt;code&gt;isNativeAccelerated()&lt;/code&gt; tells you which path you got.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can you build?
&lt;/h2&gt;

&lt;p&gt;The repository ships six game samples, each a complete, playable game in a few hundred lines, and each generating every pixel of its art and every sound at runtime, so there are no assets to manage. They double as a tour of the API, one genre at a time. Every capture below is the actual sample being played in the simulator.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/tree/master/Samples/samples/CasualGameSample" rel="noopener noreferrer"&gt;CasualGameSample&lt;/a&gt; is an arena collector: pilot a ship around a starfield with the on-screen joystick, scoop up spinning gems, dodge drifting asteroids. It exercises the breadth of the 2D layer: many sprites in one &lt;code&gt;Scene&lt;/code&gt;, per-sprite animation, particle bursts, collision, and a &lt;code&gt;TouchControls&lt;/code&gt; joystick that drives the same &lt;code&gt;GameInput&lt;/code&gt; a keyboard would.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fla3kgszg2t2nu5334umq.gif" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fla3kgszg2t2nu5334umq.gif" alt="The casual arena collector sample, the ship patrolling the starfield" width="360" height="780"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/tree/master/Samples/samples/ScrollerGameSample" rel="noopener noreferrer"&gt;ScrollerGameSample&lt;/a&gt; is the side-scroller hello world: run, jump, collect coins. The &lt;code&gt;Scene&lt;/code&gt; camera follows the player so the level slides past, an &lt;code&gt;AnimatedSprite&lt;/code&gt; walk cycle flips to face the run direction, and a cheap parallax backdrop (fixed sun, drifting clouds, half-speed hills) sells the depth. Its gravity is a few hand-rolled lines, proof you don't need the physics engine for a platformer feel.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3ralz94fu846qikesklz.gif" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3ralz94fu846qikesklz.gif" alt="The side-scroller sample, running, jumping, and collecting coins" width="360" height="780"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/tree/master/Samples/samples/CardGameSample" rel="noopener noreferrer"&gt;CardGameSample&lt;/a&gt; is Memory (Concentration): a sprite per card, a horizontal flip animation that swaps the face at the midpoint, tap hit-testing through &lt;code&gt;GameInput&lt;/code&gt;, and a small game state machine. The structure transfers directly to any card or tile game.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flkg9z160axtgipc0i1e8.gif" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flkg9z160axtgipc0i1e8.gif" alt="The memory card game sample, flipping cards into a match, a mismatch, and another match" width="360" height="780"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/tree/master/Samples/samples/BoardGameSample" rel="noopener noreferrer"&gt;BoardGameSample&lt;/a&gt; is checkers against a small built-in AI, on an isometric board. Every tile and piece is a flat &lt;code&gt;Sprite&lt;/code&gt;, but the 2:1 diamond projection and raised pieces give a convincing 3D look with no camera or models, the classic "faux 3D" trick, and the cell-to-pixel mapping it demonstrates underlies every isometric game.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffahkpjkj94f5bn0adts9.gif" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffahkpjkj94f5bn0adts9.gif" alt="The isometric checkers sample, trading moves with the built-in AI" width="360" height="780"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/tree/master/Samples/samples/Gaming3DDemoSample" rel="noopener noreferrer"&gt;Gaming3DDemoSample&lt;/a&gt; crosses into real 3D: a lit, spinning model on a ground plane, a ring of billboarded coin sprites that always face the orbiting perspective camera, and touch controls, in about two hundred lines.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6yw4sev59s2r69zqgg0x.gif" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6yw4sev59s2r69zqgg0x.gif" alt="The 3D gaming demo sample, the camera orbiting a lit model and billboarded coins" width="360" height="780"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And &lt;a href="https://github.com/codenameone/CodenameOne/tree/master/Samples/samples/GamingDemoSample" rel="noopener noreferrer"&gt;GamingDemoSample&lt;/a&gt; is the physics demo this post opened with: Box2D bodies, linked sprites, and a &lt;code&gt;SoundPool&lt;/code&gt; blip whose pitch varies per drop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to go from here
&lt;/h2&gt;

&lt;p&gt;The new &lt;a href="https://www.codenameone.com/developer-guide/#_game_development" rel="noopener noreferrer"&gt;Game Development chapter&lt;/a&gt; in the developer guide covers the loop, sprites, physics, and audio in detail, including &lt;a href="https://www.codenameone.com/developer-guide/#_sample_games" rel="noopener noreferrer"&gt;case studies&lt;/a&gt; that walk through the card game and the isometric checkers above section by section.&lt;/p&gt;

&lt;p&gt;If you build something with this, we genuinely want to see it, and if you hit a wall, &lt;a href="https://github.com/codenameone/CodenameOne/issues" rel="noopener noreferrer"&gt;file an issue&lt;/a&gt; with the smallest game that reproduces it.&lt;/p&gt;

&lt;p&gt;Yesterday's post covered &lt;a href="https://www.codenameone.com/blog/portable-3d-graphics-api/" rel="noopener noreferrer"&gt;the portable 3D API&lt;/a&gt; this is built on, and the &lt;a href="https://www.codenameone.com/blog/native-java-win32-3d-gaming-printing-and-wallet/" rel="noopener noreferrer"&gt;release post&lt;/a&gt; has the full index. Your Java becomes a native Windows executable with no JVM in .&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>3D Graphics Without Writing Shaders: The Portable GPU API</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Sat, 20 Jun 2026 13:33:27 +0000</pubDate>
      <link>https://dev.to/codenameone/3d-graphics-without-writing-shaders-the-portable-gpu-api-3ach</link>
      <guid>https://dev.to/codenameone/3d-graphics-without-writing-shaders-the-portable-gpu-api-3ach</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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ful8ovviyn9v5x1bm2dus.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ful8ovviyn9v5x1bm2dus.jpg" alt="3D Graphics Without Writing Shaders: The Portable GPU API" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cross-platform 3D is one of those problems that looks impossible when you list the constraints. iOS wants Metal and Metal Shading Language. Android wants OpenGL ES and GLSL. The web wants WebGL. Windows wants Direct3D and HLSL. Every one of these has its own shader language, its own pipeline model, and its own buffer semantics. Most portable engines solve this by making you write shaders multiple times, or by adopting a giant dependency that becomes your whole application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5151" rel="noopener noreferrer"&gt;PR #5151&lt;/a&gt; takes a different path. The new &lt;code&gt;com.codename1.gpu&lt;/code&gt; package is a portable 3D API where &lt;strong&gt;applications never write shader source at all&lt;/strong&gt;. You describe a &lt;code&gt;Material&lt;/code&gt;: its lighting model, color, texture, shininess. Per-platform generators emit the actual shader, GLSL ES on Android and WebGL, Metal Shading Language on iOS and Mac, HLSL on the new native Windows port. One Java code base, five GPU backends.&lt;/p&gt;

&lt;h2&gt;
  
  
  A cube in one screen of code
&lt;/h2&gt;

&lt;p&gt;The API centers on a &lt;code&gt;Renderer&lt;/code&gt; you implement and a &lt;code&gt;RenderView&lt;/code&gt; that hosts it. Here is a complete Phong-lit cube:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;RenderView&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RenderView&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Renderer&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Camera&lt;/span&gt; &lt;span class="n"&gt;camera&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Camera&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Mesh&lt;/span&gt; &lt;span class="n"&gt;cube&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Material&lt;/span&gt; &lt;span class="n"&gt;material&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onInit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GraphicsDevice&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;cube&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Primitives&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cube&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.6f&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;material&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Material&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Material&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PHONG&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setColor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0xff3366ff&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setShininess&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;camera&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPerspective&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.1f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPosition&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.6f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.1f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;3.4f&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTarget&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setLight&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Light&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;setDirection&lt;/span&gt;&lt;span class="o"&gt;(-&lt;/span&gt;&lt;span class="mf"&gt;0.4f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.55f&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onResize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GraphicsDevice&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;camera&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setAspect&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setViewport&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onFrame&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GraphicsDevice&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;clear&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0xff101018&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setCamera&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;camera&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Matrix4&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rotation&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toRadians&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="mf"&gt;0.35f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.12f&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;draw&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cube&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;material&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onDispose&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GraphicsDevice&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BorderLayout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CENTER&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;RenderView&lt;/code&gt; is a regular Codename One component. It participates in layout, sits next to buttons and labels, and the surrounding form keeps working exactly as before. This is a textured variant of the same code running on an iPhone through Metal:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwr0huqvbduztrmpsfbhc.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwr0huqvbduztrmpsfbhc.png" alt="A textured cube rendered through Metal on iOS" width="480" height="1040"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Real models, not just primitives
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Primitives&lt;/code&gt; gives you cubes, spheres, and friends for getting started, but real content comes from 3D tools. The &lt;code&gt;GltfLoader&lt;/code&gt; reads binary glTF (&lt;code&gt;.glb&lt;/code&gt;), the de-facto standard interchange format that Blender and practically every modern 3D tool export:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;GltfLoader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;GltfModel&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GltfLoader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;loadModel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getResourceAsStream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getClass&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"/boombox.glb"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="nc"&gt;Mesh&lt;/span&gt; &lt;span class="n"&gt;mesh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMesh&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;Material&lt;/span&gt; &lt;span class="n"&gt;material&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Material&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Material&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PHONG&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTexture&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBaseColorTexture&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model ships as a regular project resource, so the same asset loads on every platform. Here is the Khronos BoomBox sample (about 6,000 triangles with its own base-color texture) rendering on the native Mac 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwbbemd3fwr42xw6d7zsx.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwbbemd3fwr42xw6d7zsx.png" alt="The Khronos BoomBox glTF model rendered on the native Mac target" width="700" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What runs where
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Backend&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;iOS / Mac native&lt;/td&gt;
&lt;td&gt;Metal (&lt;code&gt;CAMetalLayer&lt;/code&gt;), runtime MSL compilation, pipeline cache&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Android&lt;/td&gt;
&lt;td&gt;OpenGL ES 2 via a &lt;code&gt;GLSurfaceView&lt;/code&gt; peer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web (JavaScript port)&lt;/td&gt;
&lt;td&gt;WebGL on a canvas peer, reusing the core GLSL generator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows native&lt;/td&gt;
&lt;td&gt;Direct3D 11, HLSL generated and compiled at runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simulator (JavaSE)&lt;/td&gt;
&lt;td&gt;OpenGL via JOGL by default, with a pure-Java software rasterizer as the fallback&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The simulator's fallback deserves a note: it is a complete depth-buffered, perspective-correct, textured, lit rasterizer written in plain Java. When OpenGL isn't available, headless CI machines for instance, rendering keeps working and stays deterministic, which is how our screenshot test suite gates 3D output on every platform.&lt;/p&gt;

&lt;p&gt;On iOS there is one more trick. Vertex and index buffers are backed by SIMD-aligned arrays, so the mesh data sits at a fixed, aligned C address that is handed to Metal directly with no intermediate copy. Java arrays in, GPU buffers out, nothing in between.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two levels of API
&lt;/h2&gt;

&lt;p&gt;The hybrid design goes deeper than materials. Most applications stay on the high level: &lt;code&gt;Mesh&lt;/code&gt;, &lt;code&gt;Material&lt;/code&gt;, &lt;code&gt;Camera&lt;/code&gt;, &lt;code&gt;Light&lt;/code&gt;, and &lt;code&gt;Primitives&lt;/code&gt;, with &lt;code&gt;Matrix4&lt;/code&gt; providing the usual transform helpers (perspective, look-at, rotation, scaling, multiplication) as plain &lt;code&gt;float[16]&lt;/code&gt; arrays with no object churn per frame.&lt;/p&gt;

&lt;p&gt;Underneath sits a command layer for people who know exactly what they want. &lt;code&gt;VertexBuffer&lt;/code&gt; and &lt;code&gt;IndexBuffer&lt;/code&gt; hold your geometry, &lt;code&gt;VertexFormat&lt;/code&gt; describes its layout through typed &lt;code&gt;VertexAttribute&lt;/code&gt; usages (position, normal, texture coordinates, color), and &lt;code&gt;Texture&lt;/code&gt; exposes wrap and filter modes. &lt;code&gt;RenderState&lt;/code&gt; controls depth testing, culling, and blend modes per draw call. Everything funnels through &lt;code&gt;GraphicsDevice&lt;/code&gt;, which is the one object a &lt;code&gt;Renderer&lt;/code&gt; ever talks to, and &lt;code&gt;GpuCapabilities&lt;/code&gt; reports what the device underneath can actually do, maximum texture size for instance, so you can scale content to the hardware.&lt;/p&gt;

&lt;p&gt;Two details matter for performance. Pipelines are cached by descriptor, so a thousand draws with the same material compile one shader, not a thousand. And rendering is on-demand by default: a static scene renders when something changes (&lt;code&gt;requestRender()&lt;/code&gt;), while &lt;code&gt;setContinuous(true)&lt;/code&gt; switches to a steady frame loop for animation. On-demand is the difference between a 3D product view that sips battery and one that drains it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designed to degrade
&lt;/h2&gt;

&lt;p&gt;Not every environment has a GPU backend. &lt;code&gt;RenderView.isSupported()&lt;/code&gt; tells you whether real 3D is available, and where it isn't the view renders a placeholder rather than failing. The seam into the platform layer mirrors how &lt;code&gt;BrowserComponent&lt;/code&gt; works internally, so ports that don't implement 3D simply report it as unsupported and everything else keeps running.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is a foundation
&lt;/h2&gt;

&lt;p&gt;A portable 3D API is useful on its own, for product viewers, data visualization, and the occasional spinning logo. But the reason it exists is tomorrow's post: a game development API that builds sprites, scenes, physics, and sound on top of this layer.&lt;/p&gt;

&lt;p&gt;If you render something and it doesn't look right on one of the backends, please &lt;a href="https://github.com/codenameone/CodenameOne/issues" rel="noopener noreferrer"&gt;file an issue&lt;/a&gt; with the model or code attached. Five GPU backends mean five ways for an edge case to hide, and real reports are how the generators improve.&lt;/p&gt;

&lt;p&gt;The new &lt;a href="https://www.codenameone.com/developer-guide/#_3d_graphics_and_shaders" rel="noopener noreferrer"&gt;3D Graphics and Shaders chapter&lt;/a&gt; in the developer guide covers both API levels in depth, including the shader generation model and per-platform notes. &lt;a href="https://www.codenameone.com/blog/native-java-win32-3d-gaming-printing-and-wallet/" rel="noopener noreferrer"&gt;Friday's release post&lt;/a&gt; has the full index of this week's posts, and  builds games on top of this API.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Native Java Win32, 3D Gaming, Printing and Wallet</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Fri, 19 Jun 2026 14:02:25 +0000</pubDate>
      <link>https://dev.to/codenameone/native-java-win32-3d-gaming-printing-and-wallet-3chd</link>
      <guid>https://dev.to/codenameone/native-java-win32-3d-gaming-printing-and-wallet-3chd</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%2F4gag5s6kc0idzhwj7utl.jpg" 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%2F4gag5s6kc0idzhwj7utl.jpg" alt="Native Java Win32, 3D Gaming, Printing and Wallet" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This week we're introducing native Windows support (no JVM!), a 3D graphics API, a gaming API, support for Apple Wallet, printing and more in what is probably our biggest update ever… But that's not the thing that excites me the most.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thing that excites me the most
&lt;/h2&gt;

&lt;p&gt;I won't tease you about this too much. The thing that excited me the most this week is &lt;a href="https://www.baeldung.com/java-codename-one-cross-platform" rel="noopener noreferrer"&gt;this new article by Francesco Galgani introducing Codename One&lt;/a&gt;, published on Baeldung. Beyond the great writing and form, the excitement is about the community: the way you're all using Codename One and helping us build it. The issues, the tracking, and the enthusiasm about upcoming features, like &lt;a href="https://github.com/codenameone/CodenameOne/issues/5215" rel="noopener noreferrer"&gt;this one&lt;/a&gt; where a request to consolidate documentation chapters opens with a triple thank-you for the game development API before it even gets to the point.&lt;/p&gt;

&lt;p&gt;Again, thank you all for being a part of this. We literally wouldn't be doing this without you!&lt;/p&gt;

&lt;h2&gt;
  
  
  What shipped this week
&lt;/h2&gt;

&lt;p&gt;Version 7.0.251 is live, and the four big items each get a full tutorial over the next few days. Here is a deeper look at what's coming.&lt;/p&gt;

&lt;h3&gt;
  
  
  A portable 3D graphics API
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;com.codename1.gpu&lt;/code&gt; is a GPU-accelerated 3D API where the same Java code renders through &lt;strong&gt;Metal on iOS and Mac, OpenGL ES on Android, WebGL on the web, Direct3D 11 on the new native Windows port&lt;/strong&gt;, and a dependency-free software rasterizer in the simulator. The trick that makes this portable is that you never write shader source: you describe a &lt;code&gt;Material&lt;/code&gt; (lighting model, color, texture, shininess) and the engine generates the platform shader, GLSL, Metal Shading Language, or HLSL, depending on where it lands.&lt;/p&gt;

&lt;p&gt;A lit, spinning cube is this short:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onInit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GraphicsDevice&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;cube&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Primitives&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cube&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.6f&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;material&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Material&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Material&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PHONG&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setColor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0xff3366ff&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setShininess&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;camera&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPerspective&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.1f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPosition&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.6f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.1f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;3.4f&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTarget&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setLight&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Light&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;setDirection&lt;/span&gt;&lt;span class="o"&gt;(-&lt;/span&gt;&lt;span class="mf"&gt;0.4f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.55f&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onFrame&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GraphicsDevice&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;clear&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0xff101018&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setCamera&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;camera&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;draw&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cube&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;material&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Matrix4&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rotation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.35f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.12f&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it isn't limited to primitives. A binary glTF model authored in any 3D tool loads with one call and renders with its own textures. This is the Khronos BoomBox sample model rendering on the native Mac 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwbbemd3fwr42xw6d7zsx.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwbbemd3fwr42xw6d7zsx.png" alt="A glTF model rendered by the portable 3D API on the native Mac target" width="700" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On iOS the vertex buffers are SIMD-aligned so the data is handed to Metal with no copy in between. The &lt;code&gt;RenderView&lt;/code&gt; hosting all of this is a regular component, so a 3D view drops into a normal form next to buttons and text. We walk through the whole API in .&lt;/p&gt;

&lt;h3&gt;
  
  
  Game development in the core
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;com.codename1.gaming&lt;/code&gt; builds a game surface on top of that 3D layer: a &lt;code&gt;GameView&lt;/code&gt; with a tight &lt;code&gt;update(dt)&lt;/code&gt; loop, sprites, scenes with cameras, pollable input, a low-latency &lt;code&gt;SoundPool&lt;/code&gt;, and rigid-body physics powered by a Box2D engine that ships inside the core. This is the complete logic of a physics demo where every tap drops a bouncing ball:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getInput&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;wasPointerPressed&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;PhysicsBody&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createCircle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;getInput&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getPointerX&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;getInput&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getPointerY&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
            &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BodyType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DYNAMIC&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setRestitution&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.7f&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;Sprite&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Sprite&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ballImage&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setLinkedSprite&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;getScene&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No render code in sight: the linked sprites track their physics bodies and the scene draws itself. Here it is running in the simulator:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1p4okuusfx2y7q0fl4y6.gif" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1p4okuusfx2y7q0fl4y6.gif" alt="The gaming API physics demo, Box2D bodies driving sprites" width="360" height="779"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The physics engine is JBox2D repackaged under &lt;code&gt;com.codename1.gaming.physics.box2d&lt;/code&gt;, with an idiomatic wrapper that keeps your code in screen pixels and hides the meters and the flipped y-axis. Because everything is pure Java where it matters, it runs unchanged on every target, including iOS. For years we said no to gaming APIs in Codename One;  also explains what changed our mind.&lt;/p&gt;

&lt;h3&gt;
  
  
  Your Java app as a native Windows executable, no JVM
&lt;/h3&gt;

&lt;p&gt;The new Windows target translates your Java/Kotlin bytecode to C through ParparVM, compiles it with &lt;code&gt;clang-cl&lt;/code&gt;, and links a standalone Win32 &lt;code&gt;.exe&lt;/code&gt;. Rendering is Direct2D, text is DirectWrite, networking is WinHTTP, the browser component is WebView2, and there is no JVM anywhere: not bundled, not downloaded, not required on the user's machine. A hello world is around 4MB and the full Initializr app is around 8MB. One cloud build produces &lt;strong&gt;both x64 and arm64&lt;/strong&gt; executables.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Edit:&lt;/strong&gt; Since publishing this post we found the binaries were carrying their debug and stack-unwind tables inline. We now split that data into a separate symbol file (kept only to symbolize crashes) and dead-strip code the app never reaches, which brings the sizes down further -- the figures above reflect the leaner build.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To be clear about what this is, because "Java on Windows" carries old associations: there is no Swing here, no AWT, no JavaFX, and no bundled runtime. It is the full Codename One framework compiled to native code, and everything in it works there, including the new printing API and the 3D layer. This is the same app from the same code base, rendered by Direct2D and DirectWrite on Windows:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flfkgggu0xljjigbvbdyl.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flfkgggu0xljjigbvbdyl.png" alt="A Codename One app rendering natively on Windows via Direct2D" width="784" height="561"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;People have been asking how to turn Java into a real &lt;code&gt;.exe&lt;/code&gt; since the late nineties, and the modern answer, GraalVM, is a tool designed for a different purpose. The architecture, the history, and how this completes the native desktop story the Mac target started are all in .&lt;/p&gt;

&lt;h3&gt;
  
  
  Printing and Apple Wallet
&lt;/h3&gt;

&lt;p&gt;Two platform integrations that apps keep needing. &lt;code&gt;com.codename1.printing&lt;/code&gt; hands a PDF or image to the native print dialog on every port, including the new Windows one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Printer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isPrintingSupported&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Printer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printPDF&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reportPath&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isFailed&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;ToastBar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;showErrorMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Print failed: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getError&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;});&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And card issuers can now let users add cards to Apple Wallet from inside the Wallet app itself. The iOS build generates Apple's issuer-provisioning extension pair from build hints, with zero native code on your side:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;ios.wallet.extension&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;ios.wallet.appGroup&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;group.com.mybank.app&lt;/span&gt;
&lt;span class="py"&gt;ios.wallet.issuerEndpoint&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;https://api.mybank.com/wallet/provision&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your app publishes its card list through the new &lt;code&gt;WalletExtension&lt;/code&gt; API and the extension picks it up from the shared App Group. Both features get the full treatment in .&lt;/p&gt;

&lt;h2&gt;
  
  
  Behind the scenes: the build cloud was rebuilt
&lt;/h2&gt;

&lt;p&gt;Codename One was founded in 2012, when Docker was still a "new thing", and a lot of the infrastructure we built back then was duct-taped together as we were working in startup mode. A big part of this week's work happened at that infrastructure level: the build servers underwent a major rebuild and re-architecture, and more is going on there than is apparent from the outside.&lt;/p&gt;

&lt;p&gt;We're telling you this so you're prepared: if a build behaves differently or something breaks where it didn't before, please let us know right away through the &lt;a href="https://github.com/codenameone/CodenameOne/issues" rel="noopener noreferrer"&gt;issue tracker&lt;/a&gt; so we can fix it quickly. The goal of all this is a faster, more maintainable build cloud, and the transition is the risky part.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simulator UX improvements
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5211" rel="noopener noreferrer"&gt;PR #5211&lt;/a&gt; reworks the simulator chrome around several long-standing annoyances:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Slow Motion is back.&lt;/strong&gt; The 50x animation slowdown toggle had been commented out years ago; it's now under Tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rotation keeps your zoom.&lt;/strong&gt; Rotating the device used to discard the fit-to-screen factor, forcing you to re-toggle Zoom after every rotation. All rotate/zoom paths now share the same fit logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The standalone simulator is the default.&lt;/strong&gt; The plain device window is what you get out of the box; the multi-panel app frame is still available through the Single Window Mode preference, and explicit settings are honored.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The menus make sense.&lt;/strong&gt; The confusing Simulator vs Simulate split is gone: the device and its window live under one menu, simulated device state (location, push, biometrics, dark mode) under another, and developer tools under Tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A verification problem that was very hard to track
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5216" rel="noopener noreferrer"&gt;PR #5216&lt;/a&gt; fixes a bug in the build pipeline's bytecode compliance check that could silently lose its API rewrites when classes were recompiled with unchanged sources. The symptom was an iOS build failing deep inside Xcode with an error about an undeclared &lt;code&gt;String.replaceAll&lt;/code&gt; function, and the only recovery was wiping the build directory. The check now detects recompiled classes and re-runs, and a failed check can no longer be skipped on the next build. If you ever hit that mystery error locally, this was it.&lt;/p&gt;

&lt;h2&gt;
  
  
  UIScene apps start up smoother
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5218" rel="noopener noreferrer"&gt;PR #5218&lt;/a&gt; fixes the black flash at iOS app launch that arrived with the UIScene default. iOS wasn't rendering the launch storyboard for scene-based apps, and the render surface was empty until the first form painted. The build now generates the modern launch-screen configuration and the native code covers the hand-off with a placeholder that matches the launch screen exactly, fading into the first form. The result: icon tap, launch screen, invisible hand-off, first form, with no black at any point. If you need the old behavior, the &lt;code&gt;ios.launchPlaceholder=false&lt;/code&gt; build hint opts out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pin the browser appearance on iOS
&lt;/h2&gt;

&lt;p&gt;Embedded web views on iOS follow the device's light/dark appearance, and since the UIScene transition the app-wide plist override stopped affecting them. &lt;a href="https://github.com/codenameone/CodenameOne/pull/5203" rel="noopener noreferrer"&gt;PR #5203&lt;/a&gt; adds a per-component property to pin it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BrowserComponent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BROWSER_PROPERTY_INTERFACE_STYLE&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"light"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Valid values are &lt;code&gt;light&lt;/code&gt;, &lt;code&gt;dark&lt;/code&gt;, and &lt;code&gt;auto&lt;/code&gt;. It's safe to call in the constructor; the property is applied when the native peer is ready.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upcoming attractions
&lt;/h2&gt;

&lt;p&gt;Four tutorials follow this post, one per day; each link below goes live on its day:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Saturday.&lt;/strong&gt; . PR &lt;a href="https://github.com/codenameone/CodenameOne/pull/5151" rel="noopener noreferrer"&gt;#5151&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sunday.&lt;/strong&gt; . PR &lt;a href="https://github.com/codenameone/CodenameOne/pull/5166" rel="noopener noreferrer"&gt;#5166&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monday.&lt;/strong&gt; . PRs &lt;a href="https://github.com/codenameone/CodenameOne/pull/5144" rel="noopener noreferrer"&gt;#5144&lt;/a&gt;, &lt;a href="https://github.com/codenameone/CodenameOne/pull/5209" rel="noopener noreferrer"&gt;#5209&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tuesday.&lt;/strong&gt; . PRs &lt;a href="https://github.com/codenameone/CodenameOne/pull/5217" rel="noopener noreferrer"&gt;#5217&lt;/a&gt;, &lt;a href="https://github.com/codenameone/CodenameOne/pull/5227" rel="noopener noreferrer"&gt;#5227&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;The issue tracker is &lt;a href="https://github.com/codenameone/CodenameOne/issues" rel="noopener noreferrer"&gt;here&lt;/a&gt; and it is the best place to reach us right now. The discussion forum is &lt;a href="https://www.codenameone.com/discussion-forum.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;, and the Build Cloud console is at &lt;a href="https://cloud.codenameone.com/console/index.html" rel="noopener noreferrer"&gt;&lt;code&gt;/console/&lt;/code&gt;&lt;/a&gt;. The &lt;a href="https://www.codenameone.com/playground/" rel="noopener noreferrer"&gt;Playground&lt;/a&gt;, &lt;a href="https://www.codenameone.com/initializr/" rel="noopener noreferrer"&gt;Initializr&lt;/a&gt;, and &lt;a href="https://www.codenameone.com/skindesigner/" rel="noopener noreferrer"&gt;Skin Designer&lt;/a&gt; are where they have always been.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Background Work, Push Topics, And Richer Notifications</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Tue, 16 Jun 2026 14:37:12 +0000</pubDate>
      <link>https://dev.to/codenameone/background-work-push-topics-and-richer-notifications-2gah</link>
      <guid>https://dev.to/codenameone/background-work-push-topics-and-richer-notifications-2gah</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%2Fsf39prwf4vrv567cqe3a.jpg" 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%2Fsf39prwf4vrv567cqe3a.jpg" alt="Background Work, Push Topics, And Richer Notifications" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The work that happens while your app is not in the foreground has always been the fiddly part of mobile development, and Codename One's coverage of it had gaps. &lt;a href="https://github.com/codenameone/CodenameOne/pull/5142" rel="noopener noreferrer"&gt;PR #5142&lt;/a&gt; modernizes local notifications, push, background execution, and shared content across the core, JavaSE, Android, and iOS, and importantly it makes all of it work in the simulator so you can iterate without a device.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background work with constraints
&lt;/h2&gt;

&lt;p&gt;The new &lt;code&gt;com.codename1.background&lt;/code&gt; package schedules work that the OS runs when its conditions are met, mapping to Android &lt;code&gt;JobScheduler&lt;/code&gt; and iOS &lt;code&gt;BGTaskScheduler&lt;/code&gt; underneath. You describe what the work needs, not when to poll:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;WorkRequest&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WorkRequest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"daily-sync"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;SyncWorker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setRequiresNetwork&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setRequiresCharging&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPeriodic&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000L&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;BackgroundWork&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The worker is a small class with a no-argument constructor that the platform instantiates when it runs your task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SyncWorker&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;BackgroundWorker&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;performWork&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;workId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;inputData&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                            &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;deadline&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Callback&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;onComplete&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pullLatestData&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;onComplete&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onSucess&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The builder covers the usual constraint set: network, unmetered network, charging, idle, battery-not-low, periodic intervals, an initial delay, and input data. For longer foreground operations there is &lt;code&gt;ForegroundService.start(...)&lt;/code&gt;, which runs a JVM task behind a persistent notification on Android, and for heavier iOS background processing &lt;code&gt;BackgroundTask.scheduleProcessing(...)&lt;/code&gt; maps to &lt;code&gt;BGProcessingTaskRequest&lt;/code&gt;. The iOS background-processing identifiers are declared with the &lt;code&gt;ios.backgroundProcessingIds&lt;/code&gt; build hint, which the builder turns into the matching &lt;code&gt;Info.plist&lt;/code&gt; entries for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notifications got a lot richer
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;LocalNotification&lt;/code&gt; gained a long list of capabilities, all added backward-compatibly so every existing field, getter, and setter behaves exactly as before. New on top of that: an image attachment, multiple action buttons, inline quick reply, per-channel sound, grouping with a summary, full-screen intent, time-sensitive delivery, ongoing and progress notifications, a custom view, and a messaging-conversation style.&lt;/p&gt;

&lt;p&gt;A download-progress notification, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;LocalNotification&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LocalNotification&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"download"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setAlertTitle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Downloading"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setAlertBody&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"episode-12.mp4"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setOngoing&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setProgress&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;scheduleLocalNotification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;LocalNotification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;REPEAT_NONE&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or a notification with an inline reply, the kind a messaging app uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addInputAction&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"reply"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Reply"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Type a message"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Send"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On newer Android devices, notification channels are now first-class through a builder routed via &lt;code&gt;Display&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;registerNotificationChannel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotificationChannelBuilder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"messages"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Messages"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;importance&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;NotificationChannelBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;IMPORTANCE_HIGH&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;enableVibration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lockscreenVisibility&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;NotificationChannelBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;VISIBILITY_PRIVATE&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Permission requests are explicit too, with &lt;code&gt;Display.requestNotificationPermission(...)&lt;/code&gt; taking a &lt;code&gt;NotificationPermissionRequest&lt;/code&gt; (provisional, critical, time-sensitive, or the Android &lt;code&gt;POST_NOTIFICATIONS&lt;/code&gt; permission) and returning a &lt;code&gt;NotificationPermissionResult&lt;/code&gt; you can check with &lt;code&gt;isGranted()&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Push topics
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Push&lt;/code&gt; now supports topic subscriptions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Push&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;subscribeToTopic&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sports"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;Push&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unsubscribeFromTopic&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sports"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Android these map to FCM topics. On iOS they are a documented no-op, because raw APNs has no topic concept; the call is safe to make on both so your code stays cross-platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Receiving shared content
&lt;/h2&gt;

&lt;p&gt;If a user shares text, a URL, a file, or an image into your app from another app, it now arrives through a single lifecycle hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onReceivedSharedContent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SharedContent&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// content carries text / url / file / image items&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Android this is backed by a share-receiver activity that handles &lt;code&gt;SEND&lt;/code&gt; and &lt;code&gt;SEND_MULTIPLE&lt;/code&gt; and hands files off through app storage. On iOS it reuses the share extension that landed two weeks ago and reads the App-Group payload when the app activates; the App Group is configured with the &lt;code&gt;ios.shareAppGroup&lt;/code&gt; build hint. The build plugin wires the manifest entries, services, and intent filters automatically based on a classpath scan, so turning these features on does not mean hand-editing platform descriptors.&lt;/p&gt;

&lt;h2&gt;
  
  
  All of it runs in the simulator
&lt;/h2&gt;

&lt;p&gt;The piece that makes this practical day to day is the JavaSE support. There is a new "Notifications and Background" entry in the Simulate menu with constraint toggles, a run-the-work-now button, a channel inspector, and shared-content injection, plus a rich notification panel that renders images, actions, inline quick reply, and progress and routes taps back to your &lt;code&gt;LocalNotificationCallback&lt;/code&gt; and &lt;code&gt;PushContent&lt;/code&gt; on the same code path the device uses. You can build and debug these flows entirely on your desktop before you ever make a build.&lt;/p&gt;

&lt;p&gt;A control screen like this, with the scheduled job and the actions wired to the calls above, runs and renders in the simulator:&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%2Fzq9d7obptkh3nbrzdz28.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%2Fzq9d7obptkh3nbrzdz28.png" alt="A background and push control screen, rendered in the simulator" width="460" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The previous deep dive was about &lt;a href="https://www.codenameone.com/blog/modern-advertising-api/" rel="noopener noreferrer"&gt;the new advertising API&lt;/a&gt;, and the &lt;a href="https://www.codenameone.com/blog/mac-native-grpc-graphql-and-fewer-open-issues/" rel="noopener noreferrer"&gt;release post&lt;/a&gt; has the full index for the week, including the smaller fixes and the note about how we are handling contributions now. Keep an eye for our next release this Friday.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>A New Advertising API, Built From The Ground Up</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Mon, 15 Jun 2026 15:03:04 +0000</pubDate>
      <link>https://dev.to/codenameone/a-new-advertising-api-built-from-the-ground-up-3olm</link>
      <guid>https://dev.to/codenameone/a-new-advertising-api-built-from-the-ground-up-3olm</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%2Fq5iijxau5c6si708d9b6.jpg" 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%2Fq5iijxau5c6si708d9b6.jpg" alt="A New Advertising API, Built From The Ground Up" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Advertising support in Codename One had quietly rotted. It was spread across three mechanisms that no longer work: the original &lt;code&gt;com.codename1.ads&lt;/code&gt; and &lt;code&gt;FullScreenAdService&lt;/code&gt; APIs built on the long-dead InnerActive and V-Serv networks, the decade-old &lt;code&gt;google.adUnitId&lt;/code&gt; and &lt;code&gt;mopubId&lt;/code&gt; banner build hints (MoPub is gone), and an AdMob cn1lib that was interstitial-only, built on iOS APIs Google has since removed, busy-polled, and had no consent flow. None of them supported rewarded, rewarded-interstitial, app-open, or native formats, GDPR consent, iOS App Tracking Transparency, server-side reward verification, or mediation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5169" rel="noopener noreferrer"&gt;PR #5169&lt;/a&gt; replaces all of it with one pluggable, format-complete advertising subsystem in the core, plus modern reference providers that actually work!&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%2Fa1mn0ote1x32zpoifinn.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%2Fa1mn0ote1x32zpoifinn.png" alt="A native ad in a feed plus a banner, rendered by the new API using mock ads" width="460" height="997"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  One API, every format
&lt;/h2&gt;

&lt;p&gt;The public API lives in &lt;code&gt;com.codename1.ads&lt;/code&gt;. &lt;code&gt;AdManager&lt;/code&gt; is the entry point, and there is a type per format: &lt;code&gt;InterstitialAd&lt;/code&gt;, &lt;code&gt;RewardedAd&lt;/code&gt;, &lt;code&gt;RewardedInterstitialAd&lt;/code&gt;, &lt;code&gt;AppOpenAd&lt;/code&gt;, &lt;code&gt;BannerAd&lt;/code&gt;, and &lt;code&gt;NativeAdLoader&lt;/code&gt;. Consent is handled by &lt;code&gt;AdConsent&lt;/code&gt; (UMP plus iOS ATT), and &lt;code&gt;AdConfig&lt;/code&gt;, &lt;code&gt;AdRequest&lt;/code&gt;, and &lt;code&gt;AdListener&lt;/code&gt; round out the surface. The whole thing is event-driven and every callback is marshaled onto the EDT, so you never touch ad SDK threading.&lt;/p&gt;

&lt;p&gt;A rewarded ad, the format people most often ask about, reads like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;AdConfig&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AdConfig&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;testMode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;AdManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initialize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="nc"&gt;RewardedAd&lt;/span&gt; &lt;span class="n"&gt;ad&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RewardedAd&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"your-rewarded-ad-unit-id"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;ad&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setAdListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AdListener&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onLoaded&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;ad&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reward&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="n"&gt;grantCoins&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reward&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getType&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;reward&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAmount&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;});&lt;/span&gt;
    &lt;span class="n"&gt;ad&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;load&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Consent is a first-class step rather than an afterthought:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;AdConsent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requestConsent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AdConsent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;canRequestAds&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;loadAds&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Pluggable by design
&lt;/h2&gt;

&lt;p&gt;The provider side is an SPI (Service Provider Interface) in &lt;code&gt;com.codename1.ads.spi&lt;/code&gt;: a network-agnostic &lt;code&gt;AdProvider&lt;/code&gt; plus session interfaces. Discovery is zero-wiring. &lt;code&gt;AdManager&lt;/code&gt; resolves the provider through a single call to &lt;code&gt;install()&lt;/code&gt;, so adding an ad cn1lib to your project auto-registers it with no setup code. AdMob, AppLovin MAX, Unity LevelPlay, or your own mediation layer can be swapped without touching app code.&lt;/p&gt;

&lt;p&gt;A few things only the core can do, and they are wired in directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Show an interstitial on form transitions, no more often than every two minutes&lt;/span&gt;
&lt;span class="nc"&gt;AdManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bindInterstitialOnTransition&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interstitial&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;120000&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Show an app-open ad when the app returns to the foreground&lt;/span&gt;
&lt;span class="nc"&gt;AdManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;enableAppOpenAds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;appOpenAd&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Banners are peer-backed components, so they sit in your layout like any other component.&lt;/p&gt;

&lt;h2&gt;
  
  
  AdMob as the reference provider
&lt;/h2&gt;

&lt;p&gt;The reference provider is a real, modern AdMob integration shipped as &lt;code&gt;maven/cn1-admob&lt;/code&gt;, modeled on the ML Kit cn1libs. It targets Google Mobile Ads v24 and up on Android, the current &lt;code&gt;GAD*&lt;/code&gt; APIs with UMP and ATT on iOS, and AdMob mediation works transparently behind it. You will need to add these to your build hints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;android.xapplication=&amp;lt;meta-data android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="ca-app-pub-XXXXXXXX~YYYYYYYY"/&amp;gt;
ios.plistInject=&amp;lt;key&amp;gt;GADApplicationIdentifier&amp;lt;/key&amp;gt;&amp;lt;string&amp;gt;ca-app-pub-XXXXXXXX~YYYYYYYY&amp;lt;/string&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those hints set the Android &lt;code&gt;APPLICATION_ID&lt;/code&gt; manifest meta-data and the iOS &lt;code&gt;GADApplicationIdentifier&lt;/code&gt;. For iOS you should also declare your &lt;code&gt;SKAdNetworkItems&lt;/code&gt; and an &lt;code&gt;NSUserTrackingUsageDescription&lt;/code&gt; (the ATT prompt copy) the same way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging Ads
&lt;/h2&gt;

&lt;p&gt;The simulator ships a placeholder provider so you can exercise every format and the consent flow, without a device. The &lt;code&gt;AdsSample&lt;/code&gt; in the samples repo runs every format against the simulator placeholder if you want to see the flows before you wire up a real account.&lt;/p&gt;

&lt;p&gt;A reminder that applies to any ad integration: if you do server-side reward verification, the verification has to happen on your server. Do not trust a reward granted purely on the client, and do not embed any server secret in the app.&lt;/p&gt;

&lt;p&gt;The previous deep dive covered &lt;a href="https://www.codenameone.com/blog/websockets-grpc-and-graphql/" rel="noopener noreferrer"&gt;WebSockets, gRPC, and GraphQL in the core&lt;/a&gt;, and the &lt;a href="https://www.codenameone.com/blog/mac-native-grpc-graphql-and-fewer-open-issues/" rel="noopener noreferrer"&gt;release post&lt;/a&gt; has the full index. Tomorrow's post, the last of the run, is about background work, push topics, and richer notifications.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>WebSockets, gRPC, And GraphQL In The Core</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Sun, 14 Jun 2026 13:32:59 +0000</pubDate>
      <link>https://dev.to/codenameone/websockets-grpc-and-graphql-in-the-core-1pp</link>
      <guid>https://dev.to/codenameone/websockets-grpc-and-graphql-in-the-core-1pp</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%2F0om00pyww2i1lqsw0qbu.jpg" 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%2F0om00pyww2i1lqsw0qbu.jpg" alt="WebSockets, gRPC, And GraphQL In The Core" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Three connectivity features landed together this week, and they belong in one place because they build on each other. WebSockets moved into the core; the GraphQL client uses that same WebSocket support for subscriptions; and gRPC reuses the exact code-generation pattern GraphQL and OpenAPI already follow. This post is a tutorial for all three. By the end, you will have a live chat, a typed GraphQL client, and a typed gRPC client, and you will see how little code each one takes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These features come from &lt;a href="https://github.com/codenameone/CodenameOne/pull/5133" rel="noopener noreferrer"&gt;PR #5133&lt;/a&gt; (WebSockets) and &lt;a href="https://github.com/codenameone/CodenameOne/pull/5141" rel="noopener noreferrer"&gt;PR #5141&lt;/a&gt; plus &lt;a href="https://github.com/codenameone/CodenameOne/pull/5099" rel="noopener noreferrer"&gt;PR #5099&lt;/a&gt; (the typed clients).&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1: WebSockets, no cn1lib required
&lt;/h2&gt;

&lt;p&gt;WebSockets used to require the &lt;code&gt;cn1-websockets&lt;/code&gt; cn1lib. They are now part of the framework as &lt;code&gt;com.codename1.io.WebSocket&lt;/code&gt;, implemented natively on every port (a hand-rolled RFC 6455 handshake on JavaSE and Android, &lt;code&gt;NSURLSessionWebSocketTask&lt;/code&gt; on iOS, the browser &lt;code&gt;WebSocket&lt;/code&gt; on JavaScript), with no third-party dependencies pulled into your build.&lt;/p&gt;

&lt;p&gt;If you're using &lt;code&gt;cn1-websockets&lt;/code&gt; you can keep using it. There's no change required from you. We moved the package up one level, so there's no conflict.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: open a connection
&lt;/h3&gt;

&lt;p&gt;The new API is a final, fluent class with lambda handlers. You build it, attach handlers, and connect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Good practice although in reality all current Codename One Platforms support WebSockets&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isSupported&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;WebSocket&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"wss://echo.example.com/socket"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onConnect&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"connected"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onTextMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;addIncoming&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onClose&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"closed "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onError&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;connect&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no URL-in-constructor subclassing trap from the old API; the connection is an object you hold. &lt;code&gt;send(...)&lt;/code&gt; has a &lt;code&gt;String&lt;/code&gt; and a &lt;code&gt;byte[]&lt;/code&gt; overload, &lt;code&gt;getReadyState()&lt;/code&gt; returns a &lt;code&gt;WebSocketState&lt;/code&gt;, and &lt;code&gt;close()&lt;/code&gt; does a clean close handshake.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: build the chat screen
&lt;/h3&gt;

&lt;p&gt;Here is a compact chat form. Outgoing messages are added immediately; incoming ones arrive on the &lt;code&gt;onTextMessage&lt;/code&gt; handler, and because the handler can touch the UI we wrap that in &lt;code&gt;callSerially&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Container&lt;/span&gt; &lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;showChat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Form&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Form&lt;/span&gt; &lt;span class="n"&gt;chat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Live Chat"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BoxLayout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;conversation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getContentPane&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="nc"&gt;TextField&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextField&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Message"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TextField&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ANY&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Send"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addActionListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getText&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;addBubble&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;clear&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;});&lt;/span&gt;
    &lt;span class="nc"&gt;Container&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BorderLayout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;centerEastWest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BorderLayout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SOUTH&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"wss://chat.example.com/room/general"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onTextMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;callSerially&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;addBubble&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;connect&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;addBubble&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;mine&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Label&lt;/span&gt; &lt;span class="n"&gt;bubble&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Label&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;bubble&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setUIID&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mine&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"ChatBubbleMe"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"ChatBubbleThem"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;Container&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FlowLayout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encloseIn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bubble&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getStyle&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;setAlignment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mine&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RIGHT&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LEFT&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;animateLayout&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is a working real-time chat. The screen it produces, rendered in the simulator:&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%2Fivp6v9llamtlde2a07te.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%2Fivp6v9llamtlde2a07te.png" alt="The chat screen built in this section, rendered in the simulator" width="460" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: negotiate a subprotocol when you need one
&lt;/h3&gt;

&lt;p&gt;If your server speaks a named subprotocol, set it during the handshake and read back what the server chose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;WebSocket&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;subprotocols&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"graphql-transport-ws"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onConnect&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"using "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSelectedSubprotocol&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;connect&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;graphql-transport-ws&lt;/code&gt; value is not an accident; it is exactly what the GraphQL subscriptions in the next part use.&lt;/p&gt;

&lt;p&gt;One reason to trust this implementation: our own screenshot CI now runs on it. The pipeline that ships rendered PNGs from each device back to the host machine uses a WebSocket as its transport, so the same code your app calls is carrying the binary payloads that validate the framework on every commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: a typed GraphQL client
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;cn1:generate-graphql&lt;/code&gt; turns a GraphQL schema into a typed client, and &lt;code&gt;@GraphQLClient&lt;/code&gt; is the interface you write against. The runtime lives in &lt;code&gt;com.codename1.io.graphql&lt;/code&gt;, and a &lt;code&gt;GraphQLResponse&amp;lt;T&amp;gt;&lt;/code&gt; carries data and errors together so partial results survive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: declare the client
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@GraphQLClient&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://swapi.example.com/graphql"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;StarWarsApi&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"query HeroName($episode: Episode) { hero(episode: $episode) { name homeworld { name } species { name } filmConnection { totalCount } } }"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;hero&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"episode"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Episode&lt;/span&gt; &lt;span class="n"&gt;episode&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
              &lt;span class="nc"&gt;OnComplete&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;GraphQLResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HeroData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;@Subscription&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"subscription OnReview($ep: Episode!) { reviewAdded(episode: $ep) { stars } }"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;GraphQLSubscription&lt;/span&gt; &lt;span class="nf"&gt;onReview&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ep"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Episode&lt;/span&gt; &lt;span class="n"&gt;ep&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                 &lt;span class="nc"&gt;GraphQLSubscription&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ReviewData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;StarWarsApi&lt;/span&gt; &lt;span class="nf"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;GraphQLClients&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;StarWarsApi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The build-time processor emits the implementation and a bootstrap that registers it; you never write the HTTP plumbing. The generator has two modes. The precise operations mode emits per-selection types from your operation documents; the schema-only quick-start mode auto-selects fields to a bounded depth (&lt;code&gt;cn1.graphql.maxDepth&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: call it and render the result
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;StarWarsApi&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StarWarsApi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://swapi.example.com/graphql"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hero&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Episode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;EMPIRE&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isOk&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="nc"&gt;Container&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;heroForm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getContentPane&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Hero&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getResponseData&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;heroes&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;MultiButton&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MultiButton&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTextLine2&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;homeworld&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" . "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;species&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setUIID&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"HeroRow"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;heroForm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;revalidate&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The list this populates, rendered in the simulator:&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%2Fwy2j80q603t77d3d45uc.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%2Fwy2j80q603t77d3d45uc.png" alt="The hero list this GraphQL client populates, rendered in the simulator" width="460" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: subscriptions ride the core WebSocket
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;@Subscription&lt;/code&gt; returns a &lt;code&gt;GraphQLSubscription&lt;/code&gt; backed by the core &lt;code&gt;WebSocket&lt;/code&gt; using the &lt;code&gt;graphql-transport-ws&lt;/code&gt; protocol from Part 1. New events arrive on the handler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;GraphQLSubscription&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onReview&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Episode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;JEDI&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;callSerially&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;showStars&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stars&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
&lt;span class="c1"&gt;// later&lt;/span&gt;
&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the payoff of putting WebSockets in the core: the GraphQL layer did not need its own socket implementation, it just used the frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 3: a typed gRPC client
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;cn1:generate-grpc&lt;/code&gt; does the same trick for proto3. Point it at your &lt;code&gt;.proto&lt;/code&gt; files and it emits hand-editable &lt;code&gt;@ProtoMessage&lt;/code&gt;, &lt;code&gt;@ProtoEnum&lt;/code&gt;, and &lt;code&gt;@GrpcClient&lt;/code&gt; sources; the annotation processor generates the binary protobuf codecs and call sites into &lt;code&gt;target/generated-sources&lt;/code&gt; so your source tree stays clean. There is no &lt;code&gt;protoc&lt;/code&gt; dependency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: the proto
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="na"&gt;syntax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"proto3"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;service&lt;/span&gt; &lt;span class="n"&gt;Greeter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;SayHello&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HelloRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HelloReply&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;HelloRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;HelloReply&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&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;
  
  
  Step 2: call the generated client
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;GreeterGrpc&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GreeterGrpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://api.example.com"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;HelloRequest&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HelloRequest&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"world"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sayHello&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Bearer "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isOk&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;renderGreeting&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getResponseData&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The wire protocol is gRPC-Web binary (&lt;code&gt;application/grpc-web+proto&lt;/code&gt;), the standard variant for mobile and browser clients, which works with Envoy, the official &lt;code&gt;grpcweb&lt;/code&gt; Go proxy, and the gRPC-Web filter in modern gRPC servers. Version one covers unary RPCs, all scalar types, nested messages, enums, and &lt;code&gt;repeated&lt;/code&gt; fields; streaming, &lt;code&gt;map&amp;lt;K,V&amp;gt;&lt;/code&gt;, well-known types, and &lt;code&gt;import&lt;/code&gt; are out for now and the parser errors cleanly when it meets one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enums bind across all of it
&lt;/h2&gt;

&lt;p&gt;All three connectors share the build-time JSON and XML mapper, and that mapper now binds enums. Previously an enum field was treated as a nested reference, found no mapper, and silently did not serialize. It now writes with &lt;code&gt;name()&lt;/code&gt; and reads with &lt;code&gt;valueOf&lt;/code&gt; (unknown values decode to &lt;code&gt;null&lt;/code&gt;), and it handles &lt;code&gt;List&amp;lt;Enum&amp;gt;&lt;/code&gt;, across both JSON and XML. That is why the GraphQL &lt;code&gt;Episode&lt;/code&gt; above is a real enum rather than a &lt;code&gt;String&lt;/code&gt;, and it is a welcome fix for anyone using &lt;code&gt;@Mapped&lt;/code&gt; directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep your tokens out of the binary
&lt;/h2&gt;

&lt;p&gt;The gRPC and GraphQL samples pass a bearer token, so the rule bears repeating: never hard-code a token, and never check it into source or embed it in the app. Fetch it from your backend at runtime and store it with &lt;code&gt;SecureStorage&lt;/code&gt;. A shipped binary can be unpacked, so anything baked into it is effectively public.&lt;/p&gt;

&lt;p&gt;These connectors learn from real specs. If a schema or a proto file does not generate the client you expected, please file an issue at &lt;a href="https://github.com/codenameone/CodenameOne/issues" rel="noopener noreferrer"&gt;github.com/codenameone/CodenameOne/issues&lt;/a&gt; with the source attached.&lt;/p&gt;

&lt;p&gt;The previous deep dive covered &lt;a href="https://www.codenameone.com/blog/mac-native-builds-and-desktop-integration/" rel="noopener noreferrer"&gt;native Mac builds and desktop integration&lt;/a&gt;, and the &lt;a href="https://www.codenameone.com/blog/mac-native-grpc-graphql-and-fewer-open-issues/" rel="noopener noreferrer"&gt;release post&lt;/a&gt; has the full index. Tomorrow's post is the new advertising API.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Your Codename One App, Now A Native Mac App</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Sat, 13 Jun 2026 13:33:30 +0000</pubDate>
      <link>https://dev.to/codenameone/your-codename-one-app-now-a-native-mac-app-4gio</link>
      <guid>https://dev.to/codenameone/your-codename-one-app-now-a-native-mac-app-4gio</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%2F1vyvk6n9vfv3p8nnrfw3.jpg" 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%2F1vyvk6n9vfv3p8nnrfw3.jpg" alt="Your Codename One App, Now A Native Mac App" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Codename One has run on the desktop for a long time through the JavaSE target, which is the same engine that powers the simulator. What it did not have was a &lt;strong&gt;real&lt;/strong&gt; native Mac binary, and the desktop output still carried a lot of phone-shaped habits: a drawn toolbar where the OS menu bar belongs, scrollbars you could not grab, no place in the menu for Preferences or Quit. With version 7.0.250 we finally have an actual native macOS application target that doesn't bundle a JVM and is as native as our iOS target. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A native Mac build from the iOS pipeline
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5053" rel="noopener noreferrer"&gt;PR #5053&lt;/a&gt; adds a &lt;code&gt;Mac Native&lt;/code&gt; target that takes the existing project through the same build as the iPhone builder and the ParparVM pipeline that produces an iOS app. In this case it emits a native Mac variant of it. &lt;/p&gt;

&lt;p&gt;We can find these targets in the standard maven menu in IntelliJ as "Mac Native Build" to send a build cloud build:&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%2Fjx34qqte79yrlwod5vz9.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%2Fjx34qqte79yrlwod5vz9.png" alt="Mac Native Build" width="800" height="114"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or as "Mac Native Project" to generate an Xcode project:&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%2Flcdoi8kfe5qa9kj7t6os.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%2Flcdoi8kfe5qa9kj7t6os.png" alt="Mac Native Project" width="800" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These targets should work in the same way as the equivalent iOS targets.&lt;/p&gt;

&lt;p&gt;Thanks to our switch to Metal the code for the native Mac build is very similar. That means the code of the Mac native target is mostly battle tested.&lt;/p&gt;

&lt;p&gt;We use Mac Catalyst, which is an iOS/Mac porting framework from Apple. The user-facing name is "Mac native," and a future phase might add an AppKit target sharing the same Metal renderer without changing the surface you build against. &lt;/p&gt;

&lt;p&gt;One thing to keep in mind is that the iOS native interfaces would be the same for the desktop target, this might work out fine but in case it doesn't you can use &lt;code&gt;#ifdef&lt;/code&gt; to adapt code for the Mac target.&lt;/p&gt;

&lt;p&gt;Here is a Codename One sample running as a native Mac app, the same Java code that produces the iOS and Android builds (it uses the new advertising API covered later this week):&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%2Fkgume1dlmfmadpgsltxw.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%2Fkgume1dlmfmadpgsltxw.png" alt="A Codename One app running as a native Mac app" width="760" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Certificates
&lt;/h3&gt;

&lt;p&gt;There's one major gap with the Mac target: signing. Right now our certificate wizard, settings etc. are geared towards iOS/Android. Mac uses a different store and different signing tools. We didn't update all of that infrastructure yet, and it might take some time to update. As a short-term solution, we support some build hints to configure this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hint&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;codename1.mac.appid&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mac bundle identifier (the App Store Connect record is distinct from the iOS one).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;codename1.mac.certificate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Path to the &lt;code&gt;.p12&lt;/code&gt; containing the Mac signing certificate. Bundle both &lt;em&gt;Mac App Distribution&lt;/em&gt; and &lt;em&gt;Developer ID Application&lt;/em&gt; into a single P12 when targeting both channels.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;codename1.mac.certificatePassword&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Password to unlock the P12.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;codename1.mac.provision&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Path to the Mac &lt;code&gt;.provisionprofile&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Desktop integration
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5136" rel="noopener noreferrer"&gt;PR #5136&lt;/a&gt; and the follow-up &lt;a href="https://github.com/codenameone/CodenameOne/pull/5170" rel="noopener noreferrer"&gt;PR #5170&lt;/a&gt; make a desktop target behave like a desktop app rather than a tablet app in a window. Everything here is opt-in, on by default for newly generated apps, and completely inert on mobile or when disabled. It spans the core plus desktop ports, JavaSE, Mac, and future ports.&lt;/p&gt;

&lt;h3&gt;
  
  
  Window chrome and the OS title bar
&lt;/h3&gt;

&lt;p&gt;A new build hint chooses how the window is framed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;desktop.titleBar=native
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;native&lt;/code&gt; mode the Codename One &lt;code&gt;Toolbar&lt;/code&gt; is suppressed, the form title goes to the OS title bar, and your commands are bridged to a real native menu bar (a Swing &lt;code&gt;JMenuBar&lt;/code&gt; that becomes the macOS screen menu on JavaSE, a &lt;code&gt;UIMenuBuilder&lt;/code&gt; menu on Mac Catalyst). &lt;code&gt;custom&lt;/code&gt; gives you an undecorated window with Codename One drawn caption buttons and window drag; &lt;code&gt;toolbar&lt;/code&gt; keeps the classic behavior. Together these modes let you control how the app looks in a deeply customized way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Commands land in the right menu
&lt;/h3&gt;

&lt;p&gt;Instead of every command piling into one synthetic menu, a command can declare where it belongs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Command&lt;/span&gt; &lt;span class="n"&gt;prefs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Command&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Preferences..."&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;showPreferences&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;prefs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDesktopMenu&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Command&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DESKTOP_MENU_PREFERENCES&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;prefs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDesktopShortcut&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;','&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Command&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DESKTOP_SHORTCUT_MODIFIER_PRIMARY&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;Command&lt;/span&gt; &lt;span class="n"&gt;save&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Command&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Save"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDesktopMenu&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Command&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DESKTOP_MENU_FILE&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDesktopShortcut&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;'s'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Command&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DESKTOP_SHORTCUT_MODIFIER_PRIMARY&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;setDesktopMenu(...)&lt;/code&gt; takes any of &lt;code&gt;DESKTOP_MENU_APP&lt;/code&gt;, &lt;code&gt;ABOUT&lt;/code&gt;, &lt;code&gt;PREFERENCES&lt;/code&gt;, &lt;code&gt;QUIT&lt;/code&gt;, &lt;code&gt;FILE&lt;/code&gt;, &lt;code&gt;EDIT&lt;/code&gt;, &lt;code&gt;VIEW&lt;/code&gt;, &lt;code&gt;WINDOW&lt;/code&gt;, &lt;code&gt;HELP&lt;/code&gt;, or a custom top-level title string, so Preferences and Quit show up where a Mac user expects them. &lt;code&gt;setDesktopShortcut(...)&lt;/code&gt; attaches a keyboard accelerator; &lt;code&gt;DESKTOP_SHORTCUT_MODIFIER_PRIMARY&lt;/code&gt; is Command on macOS and Control elsewhere, so the same code does the right thing on each desktop. The accelerator both appears next to the menu item and fires from the keyboard.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interactive scrollbars
&lt;/h3&gt;

&lt;p&gt;Desktop scrollbars are now grab-and-drag with a draggable thumb, click-track paging, and an always-visible track, following the macOS and Material conventions. The thumb shows its hover style under the pointer and its pressed style while dragged, and a minimum thumb size keeps it grabbable on very long content. This is gated by the &lt;code&gt;interactiveScrollBool&lt;/code&gt; theme constant and uses dedicated &lt;code&gt;Desktop*&lt;/code&gt; UIIDs, so mobile styling is untouched.&lt;/p&gt;

&lt;h3&gt;
  
  
  Desktop notifications
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5170" rel="noopener noreferrer"&gt;PR #5170&lt;/a&gt; makes the standard &lt;code&gt;LocalNotification&lt;/code&gt; API work on a real desktop build, not just in the simulator. On JavaSE a scheduled notification surfaces through a persistent system-tray icon as a native OS notification, and clicking it dispatches to your &lt;code&gt;LocalNotificationCallback&lt;/code&gt; on the same code path mobile uses. Mac Catalyst keeps using the iOS notification path. The same notification code you already wrote for mobile now runs on the desktop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generated apps get this for free
&lt;/h2&gt;

&lt;p&gt;New projects from the archetype and the Initializr default to &lt;code&gt;desktop.titleBar=native&lt;/code&gt; with interactive scrollbars on, and the modern themes ship the &lt;code&gt;Desktop*&lt;/code&gt; and &lt;code&gt;Window*&lt;/code&gt; UIIDs in light and dark (macOS conventions in &lt;code&gt;ios-modern&lt;/code&gt;, Material in &lt;code&gt;android-material&lt;/code&gt;). If you have an existing app, opt in with the two hints above and check the new UIIDs against your theme.&lt;/p&gt;

&lt;p&gt;This was validated end to end on both desktop builds: the JavaSE fat jar and the Mac Catalyst &lt;code&gt;.app&lt;/code&gt; were each driven through the same AppleScript robot test for window title, menu placement, and native-menu command firing. The full Desktop Integration chapter in the developer guide covers the details.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.codenameone.com/blog/mac-native-grpc-graphql-and-fewer-open-issues/" rel="noopener noreferrer"&gt;release post&lt;/a&gt; has the full week's index. Tomorrow's deep dive covers WebSockets, gRPC, and GraphQL in the core, the same theme of giving a Codename One app better ways to talk to the outside world.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Mac Native Builds, Live Protocols, And Open Issues Under 350</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Fri, 12 Jun 2026 14:02:19 +0000</pubDate>
      <link>https://dev.to/codenameone/mac-native-builds-live-protocols-and-open-issues-under-350-1fj5</link>
      <guid>https://dev.to/codenameone/mac-native-builds-live-protocols-and-open-issues-under-350-1fj5</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%2F4gag5s6kc0idzhwj7utl.jpg" 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%2F4gag5s6kc0idzhwj7utl.jpg" alt="Mac Native Builds, Live Protocols, And Open Issues Under 350" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our focus was all over the place this week with work that targeted many different directions: desktop, monetization, communication, media, and more. This fits with our roadmap of one platform that delivers the promise Java never delivered: WORA for Everything Everywhere. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But before we dig into the new features, there's one number I'm particularly proud of…&lt;/p&gt;

&lt;h2&gt;
  
  
  Open issues are under 350
&lt;/h2&gt;

&lt;p&gt;The open issue count is now below 350 (332 at the moment of this writing). That is the result of a deliberate pass through the tracker, closing things that were already fixed, reproducing the ones that were not, and fixing a batch of them outright. Some of the reports we closed this week had been open since 2015. We got a little side-tracked in the process (it is hard to read an old report without wanting to fix it on the spot), but the direction is set: we want this number to keep dropping, and by a lot.&lt;/p&gt;

&lt;p&gt;We went over the issue tracker starting with the oldest issues and working our way back. You may recall that when we started tracking this, going under the 500-issue mark was a major milestone and that was just a few weeks back!&lt;/p&gt;

&lt;h2&gt;
  
  
  What shipped this week
&lt;/h2&gt;

&lt;p&gt;Every one of the bigger items has its own deep-dive tutorial. Here is the tour, with the links to the full posts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Your app is now a native Mac app
&lt;/h3&gt;

&lt;p&gt;Your existing Codename One app can ship as a &lt;strong&gt;100% native Mac app today, with zero porting effort&lt;/strong&gt;. No rewrite, no bundled JVM, no Electron shell: the project you already have produces a lean native Mac binary the same way it produces your iPhone app, on the same Metal renderer and battle-tested native pipeline. And it arrives feeling like a real Mac app, not a phone in a window: native title bar, native menu bar, interactive scrollbars, and desktop notifications come with it. Two of this week's features in one shot: the sample below uses the new advertising API covered later in this post, running as a native Mac app from the same Java code that produces the iOS and Android builds:&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%2Fkgume1dlmfmadpgsltxw.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%2Fkgume1dlmfmadpgsltxw.png" alt="The advertising API sample running as a native Mac app" width="760" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The full tutorial, including the new desktop menu and shortcut APIs and the Mac signing hints, is in &lt;a href="https://www.codenameone.com/blog/mac-native-builds-and-desktop-integration/" rel="noopener noreferrer"&gt;Your Codename One App, Now A Native Mac App&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  WebSockets in the core
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;com.codename1.io.WebSocket&lt;/code&gt; is now part of the framework with no &lt;code&gt;cn1lib&lt;/code&gt; required, implemented natively on every port. A live connection is a fluent one-liner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;WebSocket&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"wss://chat.example.com/room/general"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onConnect&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"connected"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onTextMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;callSerially&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;addBubble&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;connect&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the foundation the GraphQL subscriptions below ride on, and it is trusted enough that our own screenshot CI uses it as the transport for device renders. The hands-on tutorial that builds a live chat with it is &lt;a href="https://www.codenameone.com/blog/websockets-grpc-and-graphql/" rel="noopener noreferrer"&gt;WebSockets, gRPC, And GraphQL In The Core&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  A typed GraphQL client from your schema
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;cn1:generate-graphql&lt;/code&gt; turns a GraphQL schema into a typed client: you declare an interface against your operations and the build emits the implementation, with zero HTTP plumbing on your side. A &lt;code&gt;@Subscription&lt;/code&gt; gets you live server-pushed updates over the new core WebSocket:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@GraphQLClient&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://swapi.example.com/graphql"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;StarWarsApi&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"query HeroName($episode: Episode) { hero(episode: $episode) { name } }"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;hero&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"episode"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Episode&lt;/span&gt; &lt;span class="n"&gt;episode&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
              &lt;span class="nc"&gt;OnComplete&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;GraphQLResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HeroData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;@Subscription&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"subscription OnReview($ep: Episode!) { reviewAdded(episode: $ep) { stars } }"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;GraphQLSubscription&lt;/span&gt; &lt;span class="nf"&gt;onReview&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ep"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Episode&lt;/span&gt; &lt;span class="n"&gt;ep&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                 &lt;span class="nc"&gt;GraphQLSubscription&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ReviewData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that &lt;code&gt;Episode&lt;/code&gt; is a real enum: enum binding landed in the JSON/XML mapper alongside this work, fixing a long-standing gap for &lt;code&gt;@Mapped&lt;/code&gt; users too. The full tutorial is in &lt;a href="https://www.codenameone.com/blog/websockets-grpc-and-graphql/" rel="noopener noreferrer"&gt;WebSockets, gRPC, And GraphQL In The Core&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  gRPC clients with no protoc
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;cn1:generate-grpc&lt;/code&gt; does the same for proto3. Point it at your &lt;code&gt;.proto&lt;/code&gt; files and it generates the messages, the binary protobuf codecs, and the client, speaking the standard gRPC-Web wire protocol that works with Envoy and modern gRPC servers. There is no &lt;code&gt;protoc&lt;/code&gt; to install and no native dependency; calling a service looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;GreeterGrpc&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GreeterGrpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://api.example.com"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;HelloRequest&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HelloRequest&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"world"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sayHello&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Bearer "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isOk&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;renderGreeting&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getResponseData&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Together with &lt;code&gt;cn1:generate-openapi&lt;/code&gt; and &lt;code&gt;cn1:generate-graphql&lt;/code&gt;, this means a typed client for practically any backend is one Maven goal away. The walkthrough from proto file to running call is in &lt;a href="https://www.codenameone.com/blog/websockets-grpc-and-graphql/" rel="noopener noreferrer"&gt;WebSockets, gRPC, And GraphQL In The Core&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  A new advertising API
&lt;/h3&gt;

&lt;p&gt;Advertising support had quietly rotted across three dead-end legacy mechanisms. A pluggable, format-complete &lt;code&gt;com.codename1.ads&lt;/code&gt; subsystem replaces all of them, with a modern AdMob reference provider, GDPR consent and iOS App Tracking Transparency built in, and a simulator placeholder provider so you can exercise every format without a device. A rewarded ad, the format people most often ask about, is now this short:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;RewardedAd&lt;/span&gt; &lt;span class="n"&gt;ad&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RewardedAd&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"your-rewarded-ad-unit-id"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;ad&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setAdListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AdListener&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onLoaded&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ad&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reward&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;grantCoins&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reward&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getType&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;reward&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAmount&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;span class="n"&gt;ad&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;load&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full story, including banners, native ads, app-open ads, and the provider SPI, is in &lt;a href="https://www.codenameone.com/blog/modern-advertising-api/" rel="noopener noreferrer"&gt;A New Advertising API, Built From The Ground Up&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Background execution and push
&lt;/h3&gt;

&lt;p&gt;Constraint-based background work, foreground services, push topics, shared-content handling, and a much richer local notification API, all of it usable in the simulator so you can debug these flows on your desktop. You describe what the work needs, not when to poll:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;WorkRequest&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WorkRequest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"daily-sync"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;SyncWorker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setRequiresNetwork&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setRequiresCharging&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPeriodic&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000L&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;BackgroundWork&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The walkthrough, from progress notifications and inline replies to push topics and shared content, is &lt;a href="https://www.codenameone.com/blog/background-execution-and-push/" rel="noopener noreferrer"&gt;Background Work, Push Topics, And Richer Notifications&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building screens from screenshots, and a simpler Initializr
&lt;/h2&gt;

&lt;p&gt;Generated apps ship with a &lt;code&gt;codename-one&lt;/code&gt; agent skill under &lt;code&gt;.claude/skills/&lt;/code&gt;, so an AI agent working in your project already knows how to build, test, and screenshot a Codename One UI. &lt;a href="https://github.com/codenameone/CodenameOne/pull/5161" rel="noopener noreferrer"&gt;PR #5161&lt;/a&gt; teaches it the single most common design task: "make this screen look like this mockup."&lt;/p&gt;

&lt;p&gt;The hard part of that task was that the agent had no objective measure of how close it had gotten. The existing screenshot test only compares a render to a baseline the system produced itself, which measures consistency, not correctness. The new &lt;code&gt;CompareToMockup&lt;/code&gt; tool is a single-file, pure-JDK CLI that scores a render against a target image and prints a similarity percentage: a &lt;code&gt;STRUCTURAL&lt;/code&gt; score (an SSIM-style perceptual measure, robust to font and anti-aliasing noise against a vector mockup) and a &lt;code&gt;PIXEL&lt;/code&gt; score (the fraction of pixels within the framework's own three-channel "same pixel" tolerance). It has a region mode so device chrome does not sabotage the score, a &lt;code&gt;--diff&lt;/code&gt; heatmap, and a &lt;code&gt;--min&lt;/code&gt; gate. That gives the agent a real signal: render, score, read the heatmap, adjust, repeat until the number stops climbing. A companion &lt;code&gt;DesignImport&lt;/code&gt; tool turns a Figma, Sketch, or Adobe XD file (and, after &lt;a href="https://github.com/codenameone/CodenameOne/pull/5168" rel="noopener noreferrer"&gt;PR #5168&lt;/a&gt;, the &lt;code&gt;tokens.css&lt;/code&gt; from an HTML or React mockup) into a starter &lt;code&gt;theme.css&lt;/code&gt; so the agent adjusts rather than starting from a blank page. The skill can also update itself now through an &lt;code&gt;UpdateSkills&lt;/code&gt; tool, so a project generated months ago can pull the current guidance instead of carrying a frozen copy.&lt;/p&gt;

&lt;p&gt;Agents can now automatically update the skills to the latest versions and also describe the content of Codename One GUIs. This is valuable as they review their work and don't need to use vision which is both more expensive and not as accurate. We also added the ability to check component alignment, which is often a problem that LLMs find difficult. There is also a new linter that I think we should expose to the human developers as well in the future. Right now you can see all of these tools and use them just as an agent would, but they are more CLI oriented.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5168" rel="noopener noreferrer"&gt;PR #5168&lt;/a&gt; also rebuilt the Initializr, the tool that scaffolds a new project, around the Codename One design language, and trimmed it so it is easier to approach. It leads with the essentials (main class, package, and a Java or Kotlin toggle) and tucks IDE, localization, Java version, and current settings into collapsible cards, with a live preview and a single generate bar at the bottom. The four-template picker became the Java/Kotlin toggle, and the accent, rounded-buttons, and custom-CSS controls were dropped. The project model behind it is unchanged, so generated projects are the same; this is purely about lowering the barrier to getting started.&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%2Fxcd85elx1z6xs74aj53r.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%2Fxcd85elx1z6xs74aj53r.png" alt="The redesigned Initializr" width="800" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Smaller fixes worth knowing about
&lt;/h2&gt;

&lt;p&gt;Several of these came straight out of the issue-tracker pass:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;cubic-bezier()&lt;/code&gt; motion now matches CSS.&lt;/strong&gt; &lt;a href="https://github.com/codenameone/CodenameOne/pull/5122" rel="noopener noreferrer"&gt;PR #5122&lt;/a&gt; fixes &lt;a href="https://github.com/codenameone/CodenameOne/issues/1524" rel="noopener noreferrer"&gt;#1524&lt;/a&gt;: &lt;code&gt;Motion.createCubicBezierMotion&lt;/code&gt; was feeding its control points into a 1D polynomial directly, so the curve did not match the CSS &lt;code&gt;cubic-bezier()&lt;/code&gt; it was modeled on. It does now so animations might act differently in some cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always-tensile on the X axis.&lt;/strong&gt; &lt;a href="https://github.com/codenameone/CodenameOne/pull/5112" rel="noopener noreferrer"&gt;PR #5112&lt;/a&gt; closes &lt;a href="https://github.com/codenameone/CodenameOne/issues/1399" rel="noopener noreferrer"&gt;#1399&lt;/a&gt;, the next-oldest open issue (filed March 2015): &lt;code&gt;setAlwaysTensile(true)&lt;/code&gt; now applies horizontally as well as vertically. This means you would see the rubber-band effect also on X axis scrolling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation highlights on tap-away.&lt;/strong&gt; &lt;a href="https://github.com/codenameone/CodenameOne/pull/5123" rel="noopener noreferrer"&gt;PR #5123&lt;/a&gt; closes &lt;a href="https://github.com/codenameone/CodenameOne/issues/1459" rel="noopener noreferrer"&gt;#1459&lt;/a&gt;: a field with an invalid value is now highlighted when you tap into a different field, not only when you press the virtual keyboard's next/enter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EncodedImage.dispose() actually frees memory.&lt;/strong&gt; &lt;a href="https://github.com/codenameone/CodenameOne/pull/5127" rel="noopener noreferrer"&gt;PR #5127&lt;/a&gt; makes &lt;code&gt;dispose()&lt;/code&gt; release the decoded image and the encoded bytes (it was a no-op before) and adds &lt;code&gt;isDisposed()&lt;/code&gt;. Closes &lt;a href="https://github.com/codenameone/CodenameOne/issues/3733" rel="noopener noreferrer"&gt;#3733&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NetworkManager.ping().&lt;/strong&gt; &lt;a href="https://github.com/codenameone/CodenameOne/pull/5130" rel="noopener noreferrer"&gt;PR #5130&lt;/a&gt; adds &lt;code&gt;ping(url, timeoutMillis)&lt;/code&gt;, a real server-reachability probe to pair with the device-side &lt;code&gt;isConnected()&lt;/code&gt;. Closes &lt;a href="https://github.com/codenameone/CodenameOne/issues/3669" rel="noopener noreferrer"&gt;#3669&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ImageViewer drag bubbling.&lt;/strong&gt; &lt;a href="https://github.com/codenameone/CodenameOne/pull/5132" rel="noopener noreferrer"&gt;PR #5132&lt;/a&gt;: a vertical drag on an &lt;code&gt;ImageViewer&lt;/code&gt; at zoom 1 now scrolls the parent container instead of being swallowed. Closes &lt;a href="https://github.com/codenameone/CodenameOne/issues/3700" rel="noopener noreferrer"&gt;#3700&lt;/a&gt;. This means you can now include many image viewers in a scrollable Y container.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graphics.isVisible().&lt;/strong&gt; &lt;a href="https://github.com/codenameone/CodenameOne/pull/5129" rel="noopener noreferrer"&gt;PR #5129&lt;/a&gt; adds a clip-intersection primitive so a zoomed canvas can cull off-screen content and skip the decode/scale. Closes &lt;a href="https://github.com/codenameone/CodenameOne/issues/3846" rel="noopener noreferrer"&gt;#3846&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Screenshot block now covers peers.&lt;/strong&gt; &lt;a href="https://github.com/codenameone/CodenameOne/pull/5107" rel="noopener noreferrer"&gt;PR #5107&lt;/a&gt;: &lt;code&gt;ios.blockScreenshotsOnEnterBackground=true&lt;/code&gt; was hiding the render surface but leaving peer components (such as a &lt;code&gt;BrowserComponent&lt;/code&gt;'s &lt;code&gt;WKWebView&lt;/code&gt;) visible in the app-switcher snapshot. Fixed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better site search.&lt;/strong&gt; &lt;a href="https://github.com/codenameone/CodenameOne/pull/5090" rel="noopener noreferrer"&gt;PR #5090&lt;/a&gt; sorts the on-site search index newest-first and stops the giant developer-guide page from crowding out every result. We also have dates visible next to blog post results in the search and a new highlight explaining we don't search the developer guide/Javadoc. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A note on contributions
&lt;/h2&gt;

&lt;p&gt;We stopped accepting community pull requests. We want to be precise about why, because it would be easy to read more into this than is there.&lt;/p&gt;

&lt;p&gt;This is &lt;strong&gt;not&lt;/strong&gt; about AI-generated PRs. We are not policing how a contribution was written. &lt;/p&gt;

&lt;p&gt;The real reason is mechanical: our CI does not run correctly against pull requests from forks. The screenshot pipeline, the device runners, and the protocol tests all need credentials and a setup that a forked PR cannot get, so a community PR cannot actually be validated by the same gates we hold our own work to. This isn't something we can easily solve without introducing major security vulnerabilities to our process. Recently, a change that looked completely safe slipped through and triggered a CI regression that took the builds down. That was on us, not on the contributor, but it convinced us that merging code we cannot fully run is the wrong trade.&lt;/p&gt;

&lt;p&gt;So the door is open the other way. &lt;strong&gt;Please keep filing issues.&lt;/strong&gt; A clear issue with a test case is genuinely no trouble for us to pick up, and as the number above shows, we are actively working the tracker. If you have a fix in mind, describe it in the issue, attach the failing case, and we will carry it through CI ourselves.&lt;/p&gt;

&lt;p&gt;To everyone who has sent us patches over the years, and especially the people who contributed recently: thank you. The effort was real and appreciated, and this decision is about our pipeline, not your work. &lt;/p&gt;

&lt;h2&gt;
  
  
  Upcoming attractions
&lt;/h2&gt;

&lt;p&gt;Four deep-dive tutorials follow this one, one per day:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Saturday.&lt;/strong&gt; Native Mac builds and deeper desktop integration. PRs &lt;a href="https://github.com/codenameone/CodenameOne/pull/5053" rel="noopener noreferrer"&gt;#5053&lt;/a&gt;, &lt;a href="https://github.com/codenameone/CodenameOne/pull/5136" rel="noopener noreferrer"&gt;#5136&lt;/a&gt;, &lt;a href="https://github.com/codenameone/CodenameOne/pull/5170" rel="noopener noreferrer"&gt;#5170&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sunday.&lt;/strong&gt; WebSockets, gRPC, and GraphQL in the core. PRs &lt;a href="https://github.com/codenameone/CodenameOne/pull/5133" rel="noopener noreferrer"&gt;#5133&lt;/a&gt;, &lt;a href="https://github.com/codenameone/CodenameOne/pull/5099" rel="noopener noreferrer"&gt;#5099&lt;/a&gt;, &lt;a href="https://github.com/codenameone/CodenameOne/pull/5141" rel="noopener noreferrer"&gt;#5141&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monday.&lt;/strong&gt; The new advertising API. PR &lt;a href="https://github.com/codenameone/CodenameOne/pull/5169" rel="noopener noreferrer"&gt;#5169&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tuesday.&lt;/strong&gt; Background work, push topics, and richer notifications. PR &lt;a href="https://github.com/codenameone/CodenameOne/pull/5142" rel="noopener noreferrer"&gt;#5142&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;The issue tracker is &lt;a href="https://github.com/codenameone/CodenameOne/issues" rel="noopener noreferrer"&gt;here&lt;/a&gt; and it is the best place to reach us right now. The discussion forum is &lt;a href="https://www.codenameone.com/discussion-forum.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;, and the Build Cloud console is at &lt;a href="https://cloud.codenameone.com/console/index.html" rel="noopener noreferrer"&gt;&lt;code&gt;/console/&lt;/code&gt;&lt;/a&gt;. The &lt;a href="https://www.codenameone.com/playground/" rel="noopener noreferrer"&gt;Playground&lt;/a&gt;, &lt;a href="https://www.codenameone.com/initializr/" rel="noopener noreferrer"&gt;Initializr&lt;/a&gt;, and &lt;a href="https://www.codenameone.com/skindesigner/" rel="noopener noreferrer"&gt;Skin Designer&lt;/a&gt; are where they have always been.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>We Will Not Sabotage Your Code</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Wed, 10 Jun 2026 14:07:54 +0000</pubDate>
      <link>https://dev.to/codenameone/we-will-not-sabotage-your-code-1k4k</link>
      <guid>https://dev.to/codenameone/we-will-not-sabotage-your-code-1k4k</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%2Fqhbc1h52djmojjib8r37.jpg" 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%2Fqhbc1h52djmojjib8r37.jpg" alt="We Will Not Sabotage Your Code" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a very low bar. I am genuinely saddened that I need to write it down at all, because until last week it seemed too obvious to say out loud.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Last week the &lt;a href="https://arstechnica.com/security/2026/05/fed-up-with-vibe-coders-dev-sneaks-data-nuking-prompt-injection-into-their-code/" rel="noopener noreferrer"&gt;news broke&lt;/a&gt; of an open source developer doing something I find reprehensible. The short version: fed up with "vibe coders," he embedded a hidden prompt aimed at AI agents inside his framework, with instructions designed to destroy data on the machines of people who used it.&lt;/p&gt;

&lt;p&gt;So let me say it plainly, on behalf of Codename One: &lt;strong&gt;we will not sabotage your code.&lt;/strong&gt; Not now, not as a protest, not ever. We won't do it for free users, we won't do it for paid users, and we won't do it if we disagree with you politically. &lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;From what I have read of the relevant laws, this is likely a crime in some jurisdictions. But beyond that, as an open source maintainer, I find it deeply offensive: it is a violation of trust which I find morally reprehensible.&lt;/p&gt;

&lt;p&gt;Trust is the greatest currency open source actually has. We spend years earning it one bug fix, one release, one honest answer on a forum at a time. When a maintainer weaponizes a dependency against the people who chose to rely on it, the damage is not contained to one project. It teaches every developer that the code they pull in might be quietly hostile, and that fear lands on all of us. We already have supply chain attacks to worry about. We do not need maintainers volunteering to become the next one.&lt;/p&gt;

&lt;p&gt;To be clear about the principle: a maintainer can do whatever the license allows. If you do not want AI agents touching your work, that is a legitimate position. Write a license that says so it probably can't be defined as "open source" though. That's an honest choice, and it's yours to make. What this developer chose instead was sabotage dressed up as a stand, and the fact that some people find that normal, or even justified, is the part that disheartens me most.&lt;/p&gt;

&lt;h2&gt;
  
  
  I understand the fear
&lt;/h2&gt;

&lt;p&gt;I do understand where the anger comes from.&lt;/p&gt;

&lt;p&gt;Calling AI agents a "disruptor" is a massive understatement. Many of us will lose the jobs we have today. Things may get worse for a while before they get better. That is real, and dismissing it would be dishonest.&lt;/p&gt;

&lt;p&gt;But we are engineers. Our entire profession is solving problems and building on top of whatever the new layer turns out to be. Lashing out at the people downstream of us is not engineering. It is the opposite of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I am optimistic anyway
&lt;/h2&gt;

&lt;p&gt;The potential of AI for open source is groundbreaking, and I do not use that word lightly. Before this current generation of agents, competing with a company like Google was a fantasy. They can put 100+ developers, designers, and technical writers on the exact field we are fighting in. We could only dream of keeping pace.&lt;/p&gt;

&lt;p&gt;I now genuinely believe we can surpass them by the end of the year.&lt;/p&gt;

&lt;p&gt;Large organizations are going to struggle to adopt AI properly. Process, politics, risk committees, and sheer inertia will slow them down. That difficulty is precisely the opening for the small, agile player working out of the proverbial garage. For the first time, leverage is on our side.&lt;/p&gt;

&lt;h2&gt;
  
  
  This means reinventing yourself, again
&lt;/h2&gt;

&lt;p&gt;None of this is free. The skills you need to work well with LLMs are radically different from the skills most of us built our careers on, and that adjustment is hard.&lt;/p&gt;

&lt;p&gt;My best analogy is my own start. When I began programming there were no IDEs, no code completion, no internet to search. My biggest asset was my memory. I read everything like a sponge, I could recall every book and every subject, and I always knew where to look for an answer. That was my edge.&lt;/p&gt;

&lt;p&gt;Then the world changed and that edge mattered less, so I reinvented myself for a newer age. I am doing it again right now, learning to work with these tools instead of against them. And I love this type of programming even more. I can focus on the stuff I enjoy, build huge ambitious tools and actually finish the work with testing and all the parts I used to hate writing.&lt;/p&gt;

&lt;p&gt;That is the spirit I want for this community. Not sabotage. Not fear turned into payloads hidden in someone else's build. We solve problems, we build on top, and we earn trust by being the kind of people others can safely rely on.&lt;/p&gt;

&lt;p&gt;We will not sabotage your code. We are too busy figuring out how to make it better.&lt;/p&gt;

&lt;p&gt;Thank you for being a part of our community.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
  </channel>
</rss>
