DEV Community

Nek.12
Nek.12

Posted on • Originally published at nek12.dev

I Built a Wear OS App in One Evening. Full Guide and All the Google API Pitfalls

Built a watch app in one evening.

Guide: How to Develop a Watch App for Android on Wear OS

Step 1: Creating the Project

Where you need to start: use a template, create an Android app following the official documentation. You'll have one app that you run. You make a second one the same way, just with a different SDK target and a couple of other tweaks.

⚠️ Critically Important: Package ID

Important, super important point: your app's package ID (not the Java one, but the Android one) must be exactly the same as your main app's, otherwise they won't recognize each other and won't connect.

Pay especially close attention if your debug and release builds have different package IDs. For example, you're using some package ID suffix. I had this problem where they didn't recognize each other. You won't get any errors, the package IDs are just different.

I was hoping to change my unsuccessful package ID to the nice one I've already changed everywhere except the Android package ID, since you can't change it. I'll have to use the old one.

Step 2: Choosing Libraries

Create this project, you can use Compose, add all the dependencies you need.

I recommend using Horologist for the Wear OS app, but not for the phone, if yours is super simple. If you're just sending data to Wear OS, you can get by with these listeners, write wrappers, use Kotlin serialization, and it'll basically work. That's what I decided for now, because there are bugs in Horologist, but you might want to pull Horologist into the phone too, because it essentially has the same goodies with coroutines, all these wrappers and listeners wrapped in coroutines.

Step 3: Setting Up wear.xml and capabilities

There's a special wear.xml file where you need to write down a key called capability.

For Horologist, use the ones they tell you to, because they use hardcoded strings in their source code that you must add to this XML file, otherwise nothing will run. If you're not using Horologist, you can write whatever you want in this XML for capability.

The main thing is that you have two of them: one for the phone, another for the watch. On the watch you check if there's a phone capability, and on the phone you check if there's a watch capability. You check if the device supports this capability - the ability to communicate with your app.

This will let you know if the app is installed on the watch and if it's installed on the phone. So you can show the person: "Hey, listen, you don't have the app installed on your phone, let me direct you to Google Play." And vice versa, of course.

You always need to think this through, because a person might install the app on their watch but not on their phone, or vice versa. How will they know you have a watch app? It's not shown anywhere. You need to create the flow yourself: "Yo, hey, we have a watch app, want to install it?" You'll need to correctly detect when it's done or not done.

Step 4: UI and Material Components

Add Compose Material 3, you need to use all your own components from scratch. Do it like in Horologist, I recommend stealing their source code, copying it to yourself, because they did the size constraints a bit wrong.

You need to check if the watch has a round screen or square, and you make different layouts depending on this. I don't recommend doing just one, because it'll look like a piece of shit.

Step 5: Data Layer API for State Synchronization

Connect the Wear OS Play SDK, and through it you'll have Data Manager, Data Layer. It's needed as a global broadcast to all devices - what data is currently there. Think of it as some state that's shared between your app and all apps on the watch, and vice versa.

The watch can send data, and the phone can send data. For me, the phone sends all the data, the watch only displays it. Made it simple.

Use this API for things like state that should sync between all watches. There might even be multiple watches, and there might be multiple phones. That's also a problem, you need to handle it somehow. I decided I'll just take the very first one - the first phone will be the main one, all the rest will just ignore the watch. Just for simplicity. Not many people will have multiple phones with one watch, that's nonsense.

Step 6: Messages API for RPC

If you need to send a message, you use the Messages API. There's also a separate thing with callbacks. This is basically just RPC - you can call functions.

You can add ProtoBuf and write it all out, but I don't want to add ProtoBuf, it pisses me off, it's very heavy. So I built everything through Kotlin serialization and events like with websockets. It just sends bytes that get parsed on the phone.

On the phone it's almost the same code, very similar. Make a singleton with your repository that will watch the watch in the needed flow. For example, my ritual starts, and you begin sending data through the Data Layer API. And the watch will send you events through the Messages API that you should use. That's how you build the interaction. Basically, nothing complicated.

Checklist: What You Must Check

Just don't forget to check:

  • First: presence of Google Play Services, otherwise everything crashes
  • Second: whether the app is installed on both devices
  • Third: whether the app is running on both devices
  • Fourth: whether the device is nearby, whether there's a connection between them, because it could be that the watch went too far and everything went to shit. For this you need to show separate UI that we lost connection
  • Fifth: correctly serialize and pass all this data between devices
  • Sixth: your package IDs and capabilities

That's the basics. Then just use everything you've already made, this infrastructure, build and do absolutely anything you want.

About Tokens and Authorization

If you need to go online from the watch or use authorization tokens, there are special APIs for this - identity token bundle. You can share tokens between devices thanks to this. This is the only way to do it safely. That's where a separate hassle begins.

So I recommend still making the watch app a regular slave app, and the phone app the data source. That is, it'll just be some piece of UI that will work on the watch.


My Experience in 2024:

Problem #1: Dependency on Google Play Services

Everything only works through Google Play Services. If the device doesn't have Google Play Services, or something's wrong with it, or it's an old Android version, then everything will go sideways. You need to handle this specifically and try to help the person solve this issue or hide this functionality from them so they don't leave a one-star review.

Problem #2: Ancient APIs and Zero Documentation

Everything there is written in Java, ancient Egyptian APIs through callbacks and listeners. Documentation is basically 0, just API surface, nothing is explained, everything's obfuscated in the code. In code labs nothing is clear, no specific use cases. Only authorization is described for some reason, and how to link it all together is also unclear.

Only separate pieces: here's a function, call it. Where to call it, when to call it, how to call it, how to get the result, how to create the objects needed for it? There are builders all the time, a bunch of unclear things that aren't documented at all.

Alternative: Horologist (but even here there are problems)

If you want something better in Kotlin, there's only one alternative - the Horologist library from Google itself on GitHub. Like Accompanist was a playground for the Google team back in the day, where they could push whatever they wanted to master and see what stuck, same thing here, just for watches.

They made some library, more like a project, where they threw a piece of their codebase into open source. Like, we needed this, so we added it. For example, they made a Kotlin function to watch results like with Flow, but it has 0 customization.

Everything is built on their tech stack: Firebase, DataStore, ProtoBuf, Preferences and ProtoLite, which crashes. Again, nothing is documented, many use cases aren't covered. Only what Google theoretically needed is covered, and because there's their own tech stack that needs to be somehow linked with the Respawn stack. We also have a lot of our own stuff written.

So this was a literal saga. The hardest part of developing the watch app was this.

Pros of Wear OS Development

It's basically an Android app. You can reuse almost everything: connect your modules, connect all the business logic and domain models, and write UI in regular Compose. You need separate components. Regular ones, of course, won't work, because the device form factor is very limited.

But still, in one evening, with God's help, Horologist source code and my own experience, I was able to create an app that shows the current ritual status, like a media player, like Spotify on watches. A very simple MVP companion app like that.

Top comments (0)