What you will want to know before writing an application for Apple Watch: our experience

A very slow platform awaits you, the transition to new frameworks, testing with a special atmosphere and notifications from the OS “hey, move” a second before the forced unloading of the thread.


Yes, this is my hairy hand.

Our train schedule looks at 600 thousand people a day. And every year more and more - through the mobile application. We thought and decided to make a version for the watch. The problem was that we did not know how many Apple Watch there are in the Russian market, and how many of them ride in electric trains. But in any case, it was necessary to figure out how to do this, so we took and wrote to Apple asking for statistics.

Oddly enough, they did not. Then they helped in every possible way further, but here with statistics there is a direct trouble. No one had normal data as of a year ago, and even studying sales reports did not help. Watches are brought in many ways to the “gray” and not from our region initially.

Why statistics? To understand how many generations there are on the market. In the end, we decided to support all the devices.


The last statistics we know for 2017 looked like this:

  • 1st gen (all) - 57%
  • Series 2 (all) - 32%
  • Series 1 (all) - 11%

Looking ahead, we had three months after the release in 2019 like this:

  • Apple Watch Series 3 (all) - 36%
  • Series 4 (all) - 31%
  • Series 5 (all) - 24%
  • 1st gen (all) - 4.5%
  • Series 2 (all) - 2.5%
  • Series 1 (all) - 2%

Screen size distribution of the last two revisions:

  • S4 large at 44 mm - 58%, small 40 - 42%.
  • S5 large - 60%, small 40%.

Previous models still have 38 mm, in the total number of all installs it is slightly less than 20%, mainly due to S3. By the way, we needed fonts for different screen sizes - our designers made adaptive ones.

Very slow platform


So, we seem to have not very many devices, but of which up to about 5% (as expected) will be very old. The first hours were generally conceived, it seems, like a device that shows pictures that are generated on the phone. Then there appeared its own developed OS, and in later revisions - also a Wi-Fi and a SIM card.

SIM card in Russia does not work. Perhaps, this year the operators will agree, but so far there is only wifi and communication via phone (the watch connects to the iPhone via Bluetooth, and then use its Internet connection).

The logic of working with the network, memory and disk is almost the same as in iOS.I mean, the logic itself is common, the services are common, but there is still an ambush with libraries - in fact, not many people are developing for hours, so there may not be any popular frameworks and tools on iOS / iPadOS. In our case, this meant the need to abandon the heap of libraries that we hooked to an iOS application with the usual hand movement.

Without exception, all watches make very slow disk accesses compared to how they could be read from memory. If on an iPhone you can not really bother where exactly the 40 Kb table is located, then on the watch it is critical to keep it in memory or receive it through the air. The most important thing is not to read anything from the disk.

Of course, the newer the clock, the faster it turns, but we, in fact, work for S3 with support for earlier devices and additional features for S4 and S5.

The alpha version of the application, which showed the route home, loaded for hours tens of seconds. The MCC schedule was shown for 20 seconds on the 4 nearest trains. The problem was in the table render - too many individual elements. Imagine a modern browser that is trying to render something on the 386SX core. So we had similar problems.

Naturally, that’s why she’s alpha to poke around with her. By beta, we spread the load across the threads (catching a feature along the way that when the clock starts on watchOS 4, the main entry point does not start on the mainstream). For "weak" hours, the number of cells in the interface was limited.


Each of them was rendered for about a second.



A few optimizations of the UI itself, and flew to S4, and for older devices had to reduce the number of displayed trains.

They inserted above and below “show the departed” and “show next”, and on the elect - “show all”. Since this schedule often changes, it was often necessary to redraw it: we are talking about situations when the composition of the table changes either because of a departed train, or because of a change in the schedule (we show real-time movement, that is, we know where exactly each particular train in fact, and how it will affect all further stations along the route).

Often there are situations when the data becomes outdated a second after the request. With the basic approach, the whole table was twisted, this is the standard way for iOS. There, the update is generally imperceptible. And here they’ve done a lot of crap based on processing the incoming array, which parses the data first, then builds the diff, then finds the UI elements that can be changed without touching the rest, and then only updates them. There are no suitable libraries for a complete solution on WatchOS, but there is a Differentiator Kit, which, according to the data array with markup, gives a list of how and what has changed by index. They wrote their code around it and set it on all the tables. This gave an increase in performance updates.

In general, the nearest N trains are quickly shown, and if you like to wait, but want a complete picture, you can click "show all".

As I said, the dial in the watch is large, but slow. First, we used a standard set of iOS libraries that worked through the disk. We got the schedule, recorded it on a disk, the service requested, took it from the disk, unwound it onto the screen. As it turned out, you need to store the cache in memory. And yet we are used to saving all the user’s work states to disk, and then reading them from there - we only had to record and store everything we could in memory.



Why is constant recording necessary? Because if the user switches the application, unloading occurs almost instantly. You usually don’t have time to write anything to the braking disk, so you need to do it constantly and iteratively while the user is working.

And then we are approaching the next ambush.

Manic Energy Conservation


The battery in the watch is small, and the entire operating system is sharpened in order to preserve power as much as possible. In practice, this may mean that the user is doing the following:

  1. Starts downloading something (for example, a full schedule)
  2. Lowers hand
  3. Is waiting
  4. Raises a hand to see what is loaded

In fact, the following happens:

  1. The user starts downloading the full schedule
  2. Lowers hand
  3. Watches understand that they do not work with them and put the process into an analogue of hybernate.
  4. The user waits, then raises his hand to look at the result.
  5. At the time of the ascent, the watch understands that they continue to work with them, wake up and wake up the process.
  6. The user sees the start of the download indicator.

At the same time, from the point of view of the thread, this can be either the right time or a “very long second” - if you write code at timeouts, you can suddenly catch even a year where half a second has been waiting. Therefore, just remember that time is relative.

The process of awakening threatens you with a long debugging process - you can catch crashes there either that the place is vacated, or something else interesting happens.

The process of falling asleep can turn into a process of unloading (without waking up), so you need to write some things to disk. The OS tells the "Go to Background" thread and calls the appropriate method. Usually you have about a second to quickly get out and dump in the background. When a user does this while writing to a disc (which, I recall, is hellishly slow on devices up to S4), the recording may not take place. To do this, you need to enter the “important operation” mode and, with the help of several special calls, keep the thread “conscious”. To do this, you need a separate handler that asks the recording process when it will end, and then releases the thread to sleep.

Architecture


Our first application in 2017 was just a blank, capable of showing the schedule generated on the phone. It was used to show data for a long-distance train on a track, a car and a place.

The modern version for trains is “adult” and autonomous, that is, it can live any time without a phone. Even from the moment of installation (although the usual way for the application to get into the watch is through the phone, and at this point it is very convenient to copy the profile data and all selected routes from the phone).

The architecture on the watch is probably the most modern in iOS patterns. Redux is not a problem. We, as in the "big" application, use the state machine. The standard MVC is this: there is a model on the one hand, there are views for this model, in the middle of the controller. The controller takes data from the model, converts, displays on the screen. In the opposite direction, the controller listens for view messages and provides data to the model. Based on this pattern, many types of architectures have been created.

We have a state where events can fly in and mutate it. Side effects, which do useful work, can depart from the state. From these results, events fly into the state. The full state of the application is stored in the story, such a single point of truth. The state can be broken into pieces and divided, but in any case, it stores the complete data set. The specific implementation of architecture depends on religion, we have RXFeedback.

The state machine is very well covered by tests. This saved us a lot of time just on the watch, because testing cannot be performed inside the watch itself. This is not an iPhone for you. Here you need to do this:

  1. Build the application on the emulator and see.
  2. . , . id Xcode .




I used S4 and S1 for testing. Since a lot of things are very difficult to check on the watch itself, it turned out to be incredibly convenient to isolate specific states (states), transfer them to another component and there already cover with tests from all sides.

The tenth Xcode (which was before October 2019) pleased that for some reason when the application started on the clock, it stopped working in the simulator. It was decided through chopping off the trade sanitizer, if anyone is interested.

Accordingly, when assembling targets with dedicated states and with common logic (there were a lot of them), you need to make them compatible with the clock. We accidentally hooked UIKit a couple of times, and this is the UI-framework of the phone, it is not supported on the clock. If you accidentally pick up in the release - the library simply will not connect.

Autonomy


Basic use case - the user has a watch and a phone. He regularly holds them together, and the watch can use the Internet from the phone.



With the new watch releases, it became possible to use your own Wi-Fi and your own clock SIM card (there is a built-in e-sim).

Our task is to synchronize favorites between two applications (on the phone and watch). Every time we start, we try to read the last truth, and every time we change the list, we try to immediately find the second device and tell him about it. It turns out, obviously, not always, therefore, if possible, we synchronize at startup. If there are two different favorites who are confronted with conflicts, we consider it more accurate what is on the phone and we resolve conflicts in his favor.

Otherwise, the application is not connected to the phone. The cache is inside, requests to the network are sent from the clock (if possible), if not, through the bridge via the phone. As a result, you can use it autonomously for as long as you want, but you just need to somehow install the application for the first time (our version does not support installing from the clock directly from the app). After that, you can forget about synchronization, if you want. Prior to watchOS 2, the usual application scheme was that everything was counted and done on the phone, and the clock only rendered the result.

Total


The basic application for iOS here . The watch application is paired with it. The application uses one version of the release, but on a specific device it adapts to it in terms of speed and adapts the interface to the screen size. The first authorization when installing via phone, in the same place, copying favorites and frequent routes. Further synchronization or autonomous existence. Geolocation from the phone (this is necessary to understand whether you are in Moscow or from Moscow, if you do not allow geo or not, it is time-oriented, inverting morning requests). There were about as many installs as expected (we took into account that there are not many hours in Russia, and their users are in electric trains). Judging by the reviews, the app users on the watch are glad to see their schedule on the escalator.

All Articles