# Watch Apps: One Codebase, Two Real Applications

A watch app is not a second form in the phone process. It is another application on another device, with its own storage, startup sequence, and periods when the other side is unreachable.

**What is Codename One?** 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 [codenameone.com](https://www.codenameone.com/).

[PR #5487](https://github.com/codenameone/CodenameOne/pull/5487) now builds an Apple Watch companion from `codename1.watchMain`. On Wear OS, the same entry point becomes the Android product when `codename1.watchStandalone=true`; a companion Wear APK beside the phone application is not generated yet. The release also adds one phone-to-watch API that maps to `WCSession` on Apple platforms and the Wearable Data Layer on Android.

For encrypted SQLite and the rest of this week's work, see the [weekly release overview](https://www.codenameone.com/blog/sqlite-portable-encrypted/).

## One entry point builds the watch application

The watch application starts from a fully qualified class name:

```properties
codename1.watchMain=com.example.MyWatchApp

# For a standalone Wear OS product:
codename1.watchStandalone=true
```

On Apple platforms, `watchMain` adds a companion target to the phone build. On Android, `watchStandalone=true` replaces the phone product with the Wear OS application rooted at `watchMain`. Without that flag, the Android build remains the phone application. The build logs that no companion Wear artifact was produced instead of quietly implying otherwise.

The Apple build derives the watch bundle identifier, deployment target, signing team, and display name from settings the project already has.

The phone and watch share source files, resources, CSS, and themes. They do not share runtime state. Each has its own `Storage`, `Preferences`, and SQLite files.

![Diagram](https://mermaid.ink/img/Zmxvd2NoYXJ0IExSCiAgICBBW1NoYXJlZCBKYXZhIHNvdXJjZTxici8-cmVzb3VyY2VzIGFuZCBDU1NdIC0tPiBCW1Bob25lIGFwcGxpY2F0aW9uXQogICAgQSAtLT4gQ1tXYXRjaCBhcHBsaWNhdGlvbjxici8-d2F0Y2hNYWluXQogICAgQiA8LS0-fHNlbmRNZXNzYWdlPGJyLz5saXZlIHJlcXVlc3QgYW5kIHJlcGx5fCBDCiAgICBCIDwtLT58cHV0RGF0YTxici8-bGF0ZXN0IHJlcGxpY2F0ZWQgc3RhdGV8IEMKICAgIEIgPC0tPnx0cmFuc2ZlckZpbGU8YnIvPmJhY2tncm91bmQgcGF5bG9hZHwgQw==?type=png&bgColor=ffffff align="center")

Wear OS reuses the Android port. watchOS uses a separate Core Graphics renderer because it has no UIKit view hierarchy, OpenGL ES, or Metal. The watch runtime sits inside a SwiftUI shell and runs its own ParparVM translation rooted at the watch entry point.

## A message and a value solve different problems

The platforms offer several transports because a watch spends much of its life asleep.

Use `putData()` for state that should converge when the watch next wakes:

```java
WearableConnection.putData(new WearableMessage("/steps")
        .put("count", stepCount)
        .put("goalReached", stepCount >= 10000));
```

Register the listener during `init()`. A payload can be the reason the platform started the process, so listeners attached from a later form may miss the replay window.

```java
WearableConnection.addDataListener(new WearableDataListener() {
    public void dataChanged(WearableMessage data) {
        if ("/steps".equals(data.getPath())) {
            stepsLabel.setText("" + data.getInt("count", 0));
        }
    }

    public void dataRemoved(String path) {
        if ("/steps".equals(path)) {
            stepsLabel.setText("--");
        }
    }
});
```

Each data path holds the latest value. Two rapid writes can arrive as one update. That is correct for a step count and wrong for a queue of events.

Use `sendMessage()` when both applications must be awake and the sender needs an answer now:

```java
WearableConnection.sendMessage(
        new WearableMessage("/workout/start"),
        new WearableReplyHandler() {
            public void replyReceived(WearableMessage reply) {
                showWorkout(reply.getString("id", null));
            }

            public void replyFailed(String message) {
                showReplicatedWorkoutState();
            }
        });
```

Failure is a normal branch. The phone may be asleep, out of range, or running an older version that does not know the message path. Do not use `isReachable()` as a preflight for a request with a fallback. Reachability can change after it is checked, and its first value during a cold start may still be unknown. Let `replyFailed()` select the replicated state instead. `transferFile()` covers files and large payloads that can arrive later.

## The simulator runs two processes

The **Watch > Launch Watch App** command starts the watch beside the phone. The applications run in separate processes and connect through the desktop bridge, so `sendMessage()` and `putData()` take the same asynchronous route the application code expects on a device.

The simulator includes Apple Watch 41 mm and 45 mm skins, plus round and square Wear skins. Test the round skin even if the first target is Apple Watch. It catches layouts that depend on rectangular corners.

`CN.isWatch()` selects the form-factor-specific UI. The `watch` theme override changes styling without forking the rest of the theme:

```java
Form form = new Form(BoxLayout.y());
if (CN.isWatch()) {
    form.add(new Label("Hi Watch"));
    form.getToolbar().setVisible(false);
} else {
    form.add(new SpanLabel("Welcome to the phone application"));
}
form.show();
```

## Complications reuse the surfaces model

A complication is a small system-rendered surface driven by a timeline. That is the same model Codename One uses for widgets, Live Activities, and Dynamic Island content.

```java
WidgetKind steps = new WidgetKind("steps")
        .setDisplayName("Steps")
        .addSupportedSize(WidgetSize.WATCH_CIRCULAR)
        .addSupportedSize(WidgetSize.WATCH_RECTANGULAR);
```

The watch sizes belong to `WidgetSize` instead of a second complication API. Application content can therefore share the same surface descriptors and timeline logic.

The system targets that render those watch families are not generated yet. watchOS still needs its WidgetKit extension target, and Wear OS still needs complication or tile services. The API establishes the common model without claiming those final platform adapters have shipped.

Android has one more current limit. Standalone Wear applications build today. A companion configuration does not yet produce a second Wear APK beside the phone APK. Apple Watch supports both companion and standalone targets, although standalone App Store submission still needs a manual archive step in Xcode.

## Share code without pretending the watch is a phone

The watch and phone are separate products. They can still share application rules, visual assets, and surface descriptions. `WearableConnection` keeps the connection between them visible in ordinary Java code.

Write once, run anywhere does not require pretending every screen has the same lifecycle. A phone message can fail. A replicated value can arrive after a relaunch. A complication can render while neither application is active. The shared code handles those cases without hiding them.

The [next post keeps the Codename One renderer while restoring browser-native text behavior](https://www.codenameone.com/blog/javascript-dom-text-search/).
