After seven years, the Apple Watch experience still is a mess

Seven years ago, in 2015, the Apple Watch was released and quickly switched focus from a personal communication device with some fitness support to a personal fitness device with anciliary functionality.

Every year since then, Apple released a new version of its watchOS operating system, adding some new features, but most of the time, what was added felt like how Software and Hardware development was done up until the early 2000s where features were made to fill bullets lists, not to actually be used.

To this day, the Apple Watch is a device that nearly gets there but even the basic functionality is hampered by bugs, inconsistencies and features which exist on paper but just plain don’t work in reality.

I am a heavy user of the Apple Watch, and daily I stumble over some 100% reproducible issue that I don’t expect to stumble over in an Apple product, much less one with such a pinpoint focus on a specific use case.

My “user story” matches exactly what the Watch was designed for: I’m wearing the watch all-day to know the time, to get silent notifications and the silent alarm clock. And once a day, I’m going on a running workout while listening to podcasts without taking my phone with me.

I’m a nerd, so I tend to be hung up in a case of XKCD 1172, but none of this user-story feels off what the watch was designed for.

But now, let me guide you through my day of 100% reproducible annoyances which have been present since the respective feature was added to the Watch (multiple years ago) and which to this day have not been addressed and which start to drive me up the wall to the point that I’m now sitting down and writing a long-form article.

First, let me give you context about the Apps and Hardware involved in my setup:

  • I’m using Overcast as my Podcast app. It has a hard-time syncing podcasts due to 3rd party API restrictions, but it’s still better than the built-in Podcasts app because that one cannot even sync the playback order of playlists (native podcasts was a feature added to the watch in 2018) and most of the time, syncing episodes did not work, worked only partially (some episodes downloaded, some not), or appeared to work (progress bar shown in the watch app on the phone but not moving). Streaming over LTE works (at the understandable huge cost of battery life), but even then, I have to manually select the next episode to play because it does not sync playback order of playlists (called “Station” in Apple Podcasts terms)
  • I’m using AirPods Pro as my headphones.
  • I’m tracking my runs using the built-in Workouts app (because that one is more equal than others with regards to what features it can have compared to third-parties) not because it’s better in general.

That’s all.

The trouble starts before I start my workout: Sometimes I want to add a specific podcast to the playlist I’m listening to. Because Overcast (see above for the reasoning why Overcast) only allows to sync one playlist, this means it will have to download that episode.

So I open Overcast and watch it start downloading.

Which is very slow because watchOS decides to use my phone as a proxy over a Bluetooth connection (this was the case since 2015).

I have WiFi enabled, but the Watch doesn’t auto-join my UniFi based Wifi (it works at home, but not at the COVID-related “home-office” location). All other Apple devices join fine. Wifi is a Watch feature since 2015.

But even if I manually join the WiFi (which works fine), watchOS will not stop using Bluetooth, so that won’t improve download speeds (this too was the case since 2015).

Also, because I switched to the settings app, Overcast was force-quit by the OS due to resource constraints, so when I go back to it, the download will be marked as “Failed” and I have to start it again.

So my daily routine before a run when I want to listen to a specific episode of a podcast that has not yet been downloaded for whatever reason, is as follows:

  • go into settings
  • disable bluetooth
  • manually join wifi
  • open Overcast and start the download.
  • Continously tap the screen in order to make sure the app is not terminated while the download is ongoing

You could list this as Overcast’s fault, but other Podcast players on the platform, most notable Apple’s native one also have similar to identical problems (with Apple’s offering being spectacularly bad in that it doesn’t work right even aside of the connectivity problems).

OK. Now I’m ready to run. So I select my episode on Overcast and hit play. But all I get is a prompt to chose an output device. Of course I forgot to re-enable Bluetooth which I fix and after a very long wait time, finally, one the AirPod in my right ear connects to the watch and starts playing the podcast. The left one remains silent until I take it out of my ear and put it back in (this doesn’t happen every time, but it does happen regularly enough for me to write it down here).

As I sit down again at my office chair to put on my running shoes, I accidentally bump the table which causes the mouse to move and the computer to wake up again. Thanks to automatic device switching (a feature added in 2020), the mac immediately grabs the AirPods back from my Watch to play the silence it’s currently playing (this one happens every. single. time).

So I go back to the watch and press play. Another 20s of waiting while the Watch negotiates the handover with my mac and I’m back to my podcast.

Finally it’s time to leave.

I’m in the very privileged position to work right next to where I’m running, so as I start to run, I’m initially going in and out of WiFi range. Every time the watch connects and disconnects of the WiFi, the audio stutters and sometimes breaks off completely (bug present since 2015).

So I stop and disable WiFi.

But now I’m running. Finally.

The workout itself is fine (with the exception of the display issue that if the workout is auto-paused due to me stopping to tie my shoelaces and then resumed, the screen will say “paused” in the top left, but the running animation and timer will still be running – this is a regression in watchOS 8, released in 2021) until the point that I’m getting a notification from a message I want to reply to.

I bought a Series 7 Watch based on their presentation of the new QWERTY keyboard feature for right this purpose.

Unfortunately, the message I got is in German (I live in the german speaking parts of Switzerland after all) and I want to reply in German. The new signature feature of the Series 7 Watch is not available in any language but English though which nobody told me beforehand, so it’s back to either scribble and only being sporadically able to type umlauts or dictation where I can watch the device in real-time bungling my speech into a ridiculous word salad it wants me to send off. The watch is much worse at dictation than the phone.

There’s no reason for the QWERTY keyboard to be Series 7 exclusive but to make more money for Apple which is also why they touted it as a signature feature of this new hardware generation.

They could at least have bothered to make it useable for the majority of the people on this planet (which speak a language other than English).

Anyways – back to the run. It starts to rain and after having had a half-marathon cancelled unbeknownst to me by a wet shirt hitting the touch screen in the past (why not warn me over my headphones if you detect I’m still moving? Ah right. There’s no weather in California, so this problem doesn’t happen), I enable the key lock feature.

After I reach the destination of my run, I want to stop the workout, so I turn to crown to disable the key lock. As that feature was invented for swimming, the loudspeaker of the watch starts playing a sound to eject water that entered the speaker.

All well and good, but also, while playing that sound over the built-in speaker, Bluetooth audio stops. Why? I don’t know, but this misfeature has been present since key lock was introduced in 2016. Sometimes, the audio starts again, sometimes it doesn’t.

But that doesn’t matter anyways, because the moment I’m back in WiFi or Blueooth range with my phone, clearly what needs to happen is that audio needs to stop and Bluetooh needs to be transferred back to my phone which is currently playing… nothing. Also, while transferring audio from phone to the watch takes multiple tens of seconds, the way back is instant.

This always happended here and then before the automatic switching was added in 2020, but since then, it happens every time.

So here you have it. Bug after bug after annoyance every single day. Many of the features I was talking about were added after the initial release of the Watch and were used to coax me into spending money to upgrade to new hardware.

But none of these features work correctly. Some of them just don’t work at all, some of them only work sometimes.

Over the last seven years, the underlying hardware has gotten better and better. The CPU is multiple times faster than it was in 2015. There’s mutliple times more memory available. The battery is larger, there’s more storage available. Marketing has graduated the watch from being a companion of the phone to being a mostly self-sufficient internet-connected device.

Why are apps still being killed after mere milliseconds in the background? Why are apps only awoken rarely to do actions in the background? Apps I have installed manually and I’m using all the time. Why are data transfers from the watch to the phone still basically a crapshoot and if they do work, slow as molasses? Why is Bluetooth audio still hit and miss 6 years after the last iPhone with an audio jack has been released? Why did Series 7 launch with a signature feature only available for a small portion of the planet when there’s no regulatory needs to do so?

The product is supposed to delight and all it does is frustrate me with reproducible and 100% avoidable issues every single day.

This isn’t about wishing for 3rd party apps to have more capabilities. This isn’t about wishing the hardware to do things it’s not advertised to be doing. This isn’t about the frustrating development experience for 3rd parties. This isn’t about sometimes having to reset the watch completely because a feature stopped working suddenly – that happens too, but rarely enough for me to not mind.

This is about first-party features advertized for nearly a decade working only partially or not working at all when all I’m doing is using the product exactly as the marketing copy is telling me I should be using it.

Apple, please allocate the resources the watchOS platform so desperately needs and finally make it so your excellent hardware product can live up to its promise.

Fun* with SwiftUI Beta 5 and 6

After a lot of momentum in Beta 4 where I finally got my rogue smide.ch client for the watch to work, Beta 5 and 6 were a bit of a letdown with regards to real-world usability.

Already with Beta 4, Apple has deprecated @ObjectBinding and BindableObject, both of which total staples of SwiftUI and totally required for you to do any kind of meaningful application because they provide the glue by which you hook your UI up to you actual application.

With this, even the last pieces of sample code shown at WWDC sessions about SwiftUI were now invalidated. What a breakneck speed of development.

On the other hand, this was also a case of parallel evolution inside Apple because all the old pair provided was also provided by the Combine framework with latter having the advantage of actually be usable not just in SwiftUI but anywhere in your applications.

So I can completely understand the reasoning behind the deprecation. If you are willing to clean this up, then the beta period is the time to do it.

However in Beta 4 on watchOS, while the old method was deprecated, the new way only worked partially: If you changed your class that was previously inheriting from BindableObject to now inherit from the correct Combine.ObservableObject, none of your publishes would actually be picked up by SwiftUI and the UI would remain static.

So in Beta 4, even though it was deprecated, I kept using BindableObject because that’s what was working. @ObjectBinding on the other hand, I could replace with @ObservedObject

But then, Beta 5 happened and the Beta 4 app crashed on startup.

Trying to compile it lead to a linker error because BindableObject now was gone for good. Note that the compiler was still just complaining about it being deprecated, but at link time, the symbol was missing and linking failed. This would also explain the crash at startup of the old Beta 4 app.

I’ve quickly replaced BindableObject with Combine.ObservableObject which made the app build again and run fine – on the simulator

On the real hardware, it would continue crashing on launch.

Even after installing the logging profile on the watch in order to get some information via the Console, all I got was a single log line entry from Carousel complaining about the launched process shutting down.

As this is just a fun project after all, this is where I stopped again, waiting what further betas will bring.

After a while, Beta 6 came and went. It brought no change.

Then, Beta 7 happened, but I didn’t even bother trying to recompile without an updated Xcode which finally happened today and, spoiler alert, my App is back in a running state. No further changes were required.

So it all wasn’t my fault after all.

Next time I’ll talk about the changes I’ve done since Beta 7 and Xcode Beta 6

Fun with SwiftUI – Beta 4

After hitting yet another wall with Beta 3, the moment Beta 4 was released I’ve updated all my devices, recompiled the application and looked at it on the watch.

This time, without any change on my part, things just fell into place:

Location permission worked correctly. I had my list of Bikes sorted by location

What I didn’t like as much though were all the deprecation warnings I was already accumulating. During this years Beta Period, none of the APIs presented at WWDC were finalized. SwiftUI and Combine are still very much in flux and the APIs change on a biweekly basis.

At the time of this writing, Beta 5 has already removed all older deprecations and has added even more deprecations. We’re now at a point where most of the videos from WWDC sessions that are explaining SwiftUI and combine are not applicable to the real world any more, but that’s for another post.

Some lipstick on the pig

With things mostly working right, there was one thing that was bugging me in the list of bikes you’re seeing in the screenshot above: The distances to my current location were manually formatted in meters, but I knew that Apples platforms come with very good locale dependent unit formatters, so I wanted to fix that.

MeasurementFormatter has a .naturalScale unit option that’s supposed to pick scale and unit automatically dependent on the user’s locale. In case of distances, here in Switzerland, I would expect a distance to be shown in meters up until 1 km at which point I would expect a distance to be shown in km with one fractional digit of accuracy.

But that’s now what MeasurementFormatter does: It insisted on using km and it insisted on three fractional digits of accuracy. That’s why I’ve decided to format on my own. But I knew there must be a proper solution.

It tuns out, there is – but it’s part of MapKit, not of Foundation: There’s MKDistanceFormatter to use for this and while MeasurementFormatter has a unitOptions property, the MKDistanceFormatter has a unitStyle property which you can set to .abbreviated to get to the proper effect.

So I have added that and also used battery icons based on SFSymbols to display the bikes battery levels like so:

we’ll never know why there’s no battery.50 image. Only .100, .25 and .0

Reactive warts in SwiftUI

Remember when I said that the whole UI hierarchy was going to be built from one parent SwiftUI view that would decide on a state object? That’s the design I totally went with:

My main ContentView is just a big switch statement, completely exchanging its subview hierarchy dependent on what the global state handler object thinks should be currently active.

As you can see in above code, there’s only .ListingBikes – there’s no state to list a single bike. That’s fine, because that’s up to that BikeList view to decide to instead of listing a list of bikes it wants to show a single bike.

I did this using a NavigationLink nee NavigationButton setting its destination to the detail view:

What’s nice is that you get a free sliding animation and a free back button out of this. However, what’s not as nice is that if you do this, the detail view gets pushed as another native view on top of the existing view.

Which means that even when the big switch statement from the screenshot above causes a different sub-view to be rendered (and it does get rendered), then the additional view pushed by the NavigationLink remains shown on top and does not get closed.

In the end, here on Beta 4, I went with a NavigationDestinationLink so I could first close the detail view and then tell the state handler I wanted to create a booking.

At the time of this writing (Beta 5 has just been released), NavigationDestinationLink is already deprecated again and the state whether the destination is showing or not can be passed as a $binding, however, also at the time of this writing, this currently messes with the free back button

Another thing that falls in the same bucket of re-painting the whole hierarchy does not in fact repaint the whole hierarchy is a SwiftUI View’s navigationBarTitle modifier: if you set that once on any subview, it will persist even through you removing that subview and replacing it by another which doesn’t use the navigationBarTitle modifier.

Meaning that setting a property on a subview as an effect on global state.

This feels wrong to me.

First booking

Anyways – enough complaining. With all of this in place, I did my very first commute with a smide bike using nothing but my watch. Here you see me at the end of the trip, ready to end the booking:

That felt great.

What doesn’t feel great is the mess Beta 5 made out of my nicely layouted UI. But that’s for another day.

Fun with SwiftUI – Beta 3

After being abruptly stopped last time by a compilation step that would not complete in a finite amount of time, I let the project rest until Beta 3 was released.

I could probably have found my mistake, but I was also willing to give it another two weeks and then see whether the compiler would just tell me what I was doing wrong.

Which, when Beta 3 was released, it actually did. Trying to recompile the project would immediately be stopped at a clear error message (I don’t actually remember the details any more), but the fix was very easy after all.

Motivated to finally move on, I finished hooking up my state handler to the UI itself and I was finally at a point where I would run this skeleton in the simulator.

Simulator says: nope.

First the good news: Apple was right when they proudly said that they have improved the watch simulator workflow: Launching the simulator in stand-alone mode finally is a sub-second endeavor and so is actually launching your app in the debugger.

Working this way is actual honest-to-god fun.

Yes, things should just work like this out of the box, but until now, they never did: Running the watch simulator meant also running the phone simulator and proxying all debugger operations through the phone simulator, including breaking connections and horrible, horrible lag.

But none of this still happens in WatchOS 6: The simulator can run on its own and it launches instantly. No connection issues.

At least not to talk to Xcode…

My initial excitement about things working so well was abruptly dampened by the fact that all network access I was trying to do in the simulator ended up failing with a generic error and in the log output some backend component would complain about losing connection to the background transfer service.

Of course I first assumed to be the source of the problem and I spent two hours trying to find out what I was doing wrong.

I shouldn’t have, because my last resort was to check the Apple Beta forums and there, I didn’t even have to bother posting a question: Others had the issue too and the solution is to just use a real device.

Onwards to the real device

Updating a watch to a Beta OS is a tricky proposition: There is no (official) way to ever downgrade and stuff is known to be shaky.

Also, my watch is the one single computer I use that produces data for which I have no backup and re-creating the data is a (literally) painful experience.

I’m talking about workout data.

For two years now I’m running 10ish km every day and while I know rationally that the actual act of running is what counts, unfortunately, my subconscious only accepts a run as having happened when it’s also tracked in the Activity app and when the rings are closed.

So would I dare updating to a beta version knowing that I can’t downgrade and that the watch is producing irreplaceable data that I heavily rely on?

Of course I would. 🤓

But only after checking our local electronics retailer to make sure that they had a replacement watch on stock if worse comes to worst. Yes. I know that you can ask Apple to downgrade a bricked/unsuable watch, but that would mean days without the watch and days without my runs being tracked.

Inacceptable.

Anyways. Updating to watchOS 6 went fine and a small test walk around my house has shown that tracking workouts was still generally working fine. So I was all set to try it on the real device.

Moving forward on the device

The good news: While not as fast as the simulator, deploying and debugging on the watch still is considerably quicker and more reliable than it ever was on any previous combination of Xcode, iOS and watchOS.

Debugging still involves proxying though the phone, but now it’s reliable. Over the course of 4 weeks of doing this (spoiler alert), I only had one or two instances where Xcode wouldn’t talk to the watch and more and I had to restart my computer, my phone and my watch to get connectivity restored.

Judging by other people’s prior experiences, this is a huge step forward.

The other good news: Network requests to indeed work on the real device. My client could fetch a JWT token from the smide.ch service and it could get a list of currently available bikes.

Impressive rendering speed

I have chosen the most naïve implementation possible and just fed the whole list of ~200 bikes directly into the UI framework. No dealing with cell reuse, no limiting the size of the list, nothing. Just “hey SwiftUI, please render this list of 200 bikes”.

And render it does: It’s quick and scrolling through the whole list is buttery smooth without doing any kind of optimization work. And once the next roadblock is fixed (see below) and the list gets dynamically re-sorted as my location changes, that too is buttery smooth.

I’m getting away with telling the framework that the list has changed and needs repainting and I just pass it a new updated list of bikes. The change is instantaneous. Even though it’s a new list of 200 items.

This is so much fun. I shouldn’t need to care about minimizing updates. I shouldn’t need to care about cell reuse. I shouldn’t need to deal with this. And with SwiftUI, I don’t have to.

Location roadblocks

Excited, I moved forward to asking for location access and using location to sort the list of bikes.

And this is where things ground to a halt.

Whenever my independent watch app extension would be launched, I would be calling CLLocationManager.authorizationStatus() which would tell me that my status was .notDetermined, so I would ask for permission.

My delegate callback would be called with .authorizedWhenInUse, but CLLocationManager.authorizationStatus() would still return .notDetermined and all attempts at calling location specific API would be ignored.

As this was my first strides into CoreLocation, I assumed this to be my fault and spent a lot of time debugging this, moving code around and trying out things, but not matter what I did, the effects didn’t change.

Then I tried Apple’s Sample Code from 2016 which of course worked fine even after I changed the integrated watch app to be usable independently.

After a few more hours of trial and error, I finally was able to pin it down though: In Beta 3 (and presumably earlier Betas too), the CoreLocation permission management is broken if your watch app is a completely independent watch app.

If it has a companion iOS app, then requesting location permission is fine, but when you have a watch app without any iOS app which has a plist that looks like this:

<key>WKWatchOnly</key>
<true/>

Then requesting location permission would trigger a race condition where your permission is simultaneously granted and not granted.

I could have caved and made an empty iOS companion app at this point, but I decided to report this issue using Feedback Assistant and call it another two weeks.

The relief I felt when I’ve seen Apple’s official code sample to fail the same way as my sample code did the moment I set that WKWatchOnly flag was one hell of a feeling.

I wasn’t doing it wrong. I wasn’t losing my mind.

Next time, things will finally fall into place, but only after dealing with deprecations.

Fun with SwiftUI – Beta 2

After spending the first two weeks of the beta period to get a foundation going, I was eager to start working on the actual watch app.

This was right about the time when Beta 2 hit, so first I’ve upgraded to that and then started with the Watch project.

Building the UI

Eager to play around with SwiftUI, the first thing I have done was to actually just create a skeleton UI:

What immediately sprang to my mind as I was working on this was the fact that the built-in preview feature of SwiftUI forces you to keep your views self-contained and to keep the dependencies small and to keep your data easily mockable.

Otherwise you will suddenly be in the position where your Xcode preview requires working network connections and a lot of application state.

I’ve also learned that Beta 2 was still on very shaky grounds before running the actual code even once: My attempts to display a map view caused Xcode to crash completely the moment it tried to paint the UI preview, so I’ve stubbed that out to just be a rectangle

But overall, designing (if you can call it that) the skeleton UI went very quickly (a matter of a few hours) and I was eager to hook everything up.

A hard stop

After working on the UI, the next step was to produce a backend that orchestrates the actual application state. This single class is the only thing that keeps track of state in the application and based on which the UI decides what to paint and how and where the UI will call into in order to change the overall state (for example when the user logs in or when they start a booking)

This is what (at the time) you would use @ObjectBinding and BindableObject for.

My next step, thus, was to create what I called the ApplicationStateHandler which I had implement the BindableObject protocol.

That handler itself would expose a state property which could have one of various values of an ApplicationState enum. The main SwiftUI view would basically be a huge select statement over that state property and then decide what actual view to render based on the state.

This was my plan, but no matter what I did, the moment I had ApplicationStateHandler implement the BindableObject protocol, I would put Xcode 11 Beta 2 in a state where it was using 100% of each of my 8 CPU cores while trying to compile my code.

So in the end, I wasn’t stopped by incomprehensible error messages (I got my share of those too), but by a compilation run that did not seem to want to complete in finite time.

Instead of solving the halting problem, I decided to wait another two weeks because I already had other non-project related things on my plate.

Stay tuned for next time to see what stopped me hard in Beta 3

Fun with SwiftUI – Beta 1

As explained before, I’ve decided to scratch my own itch and write an independent Apple Watch client for the smide.ch bike sharing service.

The first step to getting from the idea to the final watch app wasn’t actually involving the Watch at all: Before I could get started, I needed to know how the existing smide clients actually work and how to talk to their server.

Then I wanted to have a unit-tested library that I could use from the Watch Frontend.

On top of that library, I wanted to have a command-line client for easier debugging of the library itself.

And only then would I start working on the frontend on the watch.

Preliminaries

So as the Developer Beta 1 for XCode 11, WatchOS 6 and Catalina rolled out, the first few days of development I spent reverse-engineering the official Smide Client.

As always, the easiest solution was to just de-compile their Android Client and lo and behold, they are making use of retrofit to talk to their server which lead to a very nice and readable interface documentation right in my decompiler

Armed with this information, a bit of grepping through the rest of the decompiled code and my trusty curl client, I was able to document the subset of the API that I knew I was going to need for the minimal feature-set I wanted to implement.

In order to have a reference for the future, I have documented the API for myself in the OpenAPI format

This is useful documentation for myself and if I should ever decide to make the source code of this project available, then it’ll be useful for anybody else wanting to write a Smide client.

Moving to XCode: SmideKit

Now that I had the API documentation I needed, the next step was to start getting my SmideKit library going.

Even though there are tools out there that generate REST clients automatically based on an OpenAPI spec, all the tools I looked at produce code that relies on third-party libraries, often Alamofire. As XCode 11 was in a very rough shape already on its own, I wanted to minimize the dependencies on third-party libraries, so in the end, I’ve opted to write my own thin wrapper on top of URLSession

The SmideKit library

SmideKit is a cross-platform (by Apple’s definition) library in that the code itself works across all of Apple’s OSes, but there are individual targets for the individual OSes

But by manually setting the Bundle Name to $(PRODUCT_NAME) in the individual Info.plist files, I can make sure that all projects can just import SmideKit without any suffixes.

As this library is the most crucial part of the overall project, I have written unit testes for all methods to make sure we correctly deal with expiring tokens, unresponsive servers and so on.

The command line client

The first user of SmideKit would be a macOs command-line frontend called smidecli. It would offer various subcommands for listing bikes, booking them and ending bookings.

Here’s a screenshot of me booking a bike

Going from nowhere to the working command-line client has taken me the whole period of Beta 1. Two weeks is a long time but between my actual day job and my newly put upon me parenting duties, my time was a bit limited.

Still. It felt good to go from nowhere to writing a library, writing a command-line frontend and then actually using it to book a bike. On the other hand: None of the code written at this point had anything to do with the announcements of WWDC. All work done could just as well have been done on the old SDKs. But still: Having a good foundation to stand on, I was sure was going to pay off.

Next time: Adventures in Beta 2

Fun project in SwiftUI

This year’s WWDC really shook the Apple Ecosystem with probably the most announcements ever happening at a single conference.

Three of the announcements when put together finally pushed me over the line to scratch a personal itch of mine that I was having for a bit more than a year: I’m a very happy customer of the smide.ch bike sharing service here in Zürich, Switzerland: Their electric bikes are well maintained, extremely fun to use and readily available to the point that they have become my main means for transport for my daily commute.

On the other hand, as the Apple Watch has become more and more capable over time between updates to the OS and to the hardware itself to the point where I can now theoretically leave my phone at home and just rely on the watch.

The last remaining hard stopping block was the smide client which so far is only available on the phone, but not on the watch.

So there you see my itch that needed scratching.

In this context, this year’s WWDC was the perfect storm for me: independent Watch apps, SwiftUI and especially SwiftUI on the watch as a real native UI framework unshackled from the constraints of the purely Storyboard based hacks in WatchKit.

The moment I dug through the announcements, I knew: I need to make myself a Smide client and after 3 Beta releases from Apple, that’s what I have done:

After launching the App and assuming you’re logged in, it lists the bikes around you, sorted by distance to your current location (they have both black (smaller) and white (bigger) bikes – hence the coloring):

Tap any bike and you get a detail view including a map

Start the booking and you get some booking information

This feature-set is very limited compared to the official client:

  • Logging in only works for accounts that were created with an email address and a password. There is no way for my third-party client to possibly work with any oAuth provider
  • There is no way to report an issue with a bike
  • The client doesn’t currently take into account the free 10 minute reservation
  • The official client does some additional user interface activity reporting which my rogue client doesn’t do.
  • There is zero payment related functionality: As I personally have a 3 year subscription, I don’t need it and besides, this is a rogue client and I can’t and don’t want to deal with their payment system.

Still. This was a fun experience to develop and to keep up-to-date between the various Beta releases, all of which deprecated some essential functionality that was also shown off during conference sessions.

Over the next few days I’m going to write down a development diary like I have done for tempalias back in the days.

To put a bit of a damper on your expectations: As this work is not sanctioned by Smide themselves and as it’s based on reverse-engineering their existing client and because this year’s API for SwiftUI and Combine is still very much in flux, I’m reluctant to release the source code of this.

Technology driven life changes

Last year, when I talked about finally seeing the Apple Watch becoming mildly useful, I had no idea what kind of a ride I was going to be on.

Generally, I’m not really concerned about my health nor fitness, but last September, when my wonderful girlfriend left for a year of study in England, I decided that I finally had enough and I wanted to lose weight.

Having a year of near-zero social obligations would totally allow me to adjust my life-style in a way that’s conducive to weight loss, so here’s what I started doing:

  • During weekdays, I greatly reduced my calorie intake to basically just a salad and a piece of bread every day (you can pry my bread from my cold dead hands – it’s the one food I think I like the most).
  • Every day, no matter the weather, no matter the workload, no matter what, I was going to walk home after my work-day in the office, or on weekends, I would just take an equivalent walk.
  • Every day, I wanted to fill the “Activity” and the “Exercise” rings on my apple watch.

Now walking home sounds like nothing special, but I’m privileged to live in Zürich Switzerland, which means that I have very easy access to forests to walk in.

So commuting home by foot meant that I could walk at least 8 kilometres (4.9 miles), climbing 330m (1082 feet), most of it through the forest.

Every day, no matter whether it was way too hot, way too cold, whether it was raining, hailing or snowing, I would walk home. And every day I would be using my Apple Watch to track what I would generously call a “Workout” (even though it was just walking – but if you go from zero sports to that, I guess it’s ok to call it that).

From September to December I started gradually increasing the distance I walk.

This is the other great thing about Zürich: Once you reach the forest (which you do by walking 20 minutes in practically any direction), you can stay in the forest for hours and hours.

First I extended the 8km walk to 10km, then 12, then 14 and finally 19 (11 miles).

During that time, I kept tracking all the vital signs I could track between the Apple Watch and a Withings scale I bought 1-2 months into this.

My walks got faster and my heart rate at rest got lower and lower, from 80 to now 60.

Every evening after the walk, I would look at the achievements handed out by my Watch which is also why I’ve never updated my movement goals in the Activity app because getting all these badges, honestly, was a lot of fun and very motivational. Every evening I would get notified of increasing my movement streak, of doubling or even tripling my movement goal and of tripling or quadrupling my exercise goal.

screenshot of the activity app

Every morning I would weigh myself and bask in the glory of the ever falling graph painted by the (back then very good) iOS app that came with the scale. I would manage to lose a very consistent 2kg (4.4lbs) per week.

Every walk I would have the chance to experience some of natures beauty.

Crazy sunsets

sunset

Beautiful sunrises

sunrise

Enchanted forests

wintery forest

And frozen creeks

frozen creek

And in spring I could watch trees grow.

When I got home after up to three hours of walk, I was dead tired at around 10pm, meaning that for the first time in ages I would get more than enough sleep and I would still be able to get up between 6 and 7.

By mid of March, after 6 months of a very strict diet and walking home every day, I was done. I had lost 40kg (88.1 lbs).

Now the challenge shifted from losing weight to not gaining weight. I decided to make the diet less strict but also continue with my walks, though I would not do the regular 19km ones any more as they would just take too long (3 hours).

But by June, I really started to notice a change: I wouldn’t feel these walks at all any more. No sweat, no reasonable change in heart rate while on them, no tiredness. The walks really felt like a waste of time.

So I started running.

I never liked running. I was always bad at it. All the way through school where I was the slowest and always felt really bad afterward, through my life until now where I just never did it. Running felt bad and I hated it.

But now things were different.

The first time I changed from walking to running, I did so after reaching the peak altitude, so it was mostly straight and a little bit downhill. But still: I ran 4km (2.48 miles) and when I got home I didn’t feel much more tired.

I was very surprised because running 4km through all of my life would have been completely unthinkable to me, but there I was. I just did.

So next day, I decided to run most of the way, just skipping the steepest parts. Suddenly, there I was, running 8km (4.9 miles), still not feeling particularly tired afterwards.

So I started tracking these runs (using both Runkeeper and Strava for technical reasons – but that’s another post), seeing improvement in my time all the way through July.

And then, on August 1st, I ran half a Marathon climbing 612m (2007 feet)

Screen Shot 2017-08-04 at 11.40.28.png

Considering that this was my first, it’s not even in too bad a time and what’s even more fun to me: I didn’t even feel too tired afterwards and I totally felt like I could run even farther.

So I guess after taking it very slowly and moving from walking a bit to walking more to walking a lot to running a bit to running some more, even I, the most unathletic person possible can push myself into shape.

But what’s the most interesting aspect in all of this is that without technology, the Apple Watch in particular, without the cheesy achievements, none of this would ever have been possible. I hated sports and I’m honestly still not really interested. But the prospect of getting awarded some stupid batches every day is what finally pushed me.

And now, in only a single month, my girlfriend will finally return to Switzerland and I guess she’ll find me in better shape than she’s ever seen me before in our lives. I hope that the prospect of collecting some more batches from my watch will keep me going even when the social pressure might want to tempt me into skipping a workout.

Apple Watch starting to be useful

Even after the Time for Coffee app has been updated for WatchOS 2.0 support last year and my Apple Watch has become significantly more useful, the fact that the complication didn’t get a chance to update very often and the fact that launching the app took an eternity kind of detracted from the experience.

Which lead to me not really using the watch most of the time. I’m not a watch person. Never was. And while the temptation of playing with a new gadget lead to me wearing it on and off, I was still waiting for the killer feature to come around.

This summer, this has changed a lot.

I’m in the developer program, so I’m running this summer’s beta versions and Apple has also launched Apple Pay here in Switzerland.

So suddenly, by wearing the watch, I get access to a lot of very nice features that present themselves as huge user experience improvements:

  • While «Time for Coffee»’s complication currently is flaky at best, I can easily attribute this to WatchOSes current Beta state. But that doesn’t matter anyways, because the Watch now keeps apps running, so whenever I need public transport departure information and when the complication is flaky, I can just launch the app which now comes up instantly and loads the information within less than a second.
  • Speaking of leaving apps running: The watch can now be configured to revert to the clock face only after more than 8 minutes have passed since the last use. This is perfect for the Bring shopping list app which now suddenly is useful. No more taking the phone out while shopping.
  • Auto-Unlocking the Mac by the presence of an unlocked and worn watch has gone from not working at all, to working rarely, to working most of the time as the beta releases have progressed (and since Beta 4 we also got the explanation that WiFi needs to be enabled on the to-be-unlocked mac, so now it works on all machines). This is very convenient.
  • While most of the banks here in Switzerland boycott Apple Pay (a topic for another blog entry – both the banks and Apple are in the wrong), I did get a Cornèrcard which does work with Apple Pay. Being able to pay contactless with the watch even for amounts larger than CHF 50 (which is the limit for passive cards) is amazing.

Between all these features, I think there’s finally enough justification for me to actually wear the watch. It still happens that I forget to put it on here and then, but overall, this has totally put new life into this gadget, to the point where I’m inclined to say that it’s a totally new and actually very good experience now.

If you were on the fence before, give it a try come next autumn. It’s really great now.

The new AppleTV

When the 2nd generation of the AppleTV came out and offered AirPlay support, I bought one more or less for curiosity value, but it worked so well in conjunction with AirVideo that it has completely replaced my previous attempts at an in-home media center system.

It was silent, never really required OS or application updates, never crashed and never overheated. And thanks to AirVideo it was able to play everthing I could throw at it (at the cost of a server running in the closet of course).

The only inconvenience was the fact that I needed too many devices. Playing a video involved my TV, the AppleTV and my iOS device. Plus remotes for TV and AppleTV. Personally, I didn’t really mind much, but while I would have loved to give my parents access to my media library (1 Gbit/s upstream FTW), the requirement to use three devices and to correctly switch on AirPlay has made this a complete impossibility due to the complexity.

So I patiently awaited the day when the AppleTV would finally be able to run apps itself. There was no technical reason to prevent that – the AppleTV totally was powerful enough for this and it was already running iOS.

You can imagine how happy I was when finally I got what I wanted and the new 4th generation AppleTV was announced. Finally a solution my parents can use. Finally something to help me to ditch the majority of the devices involved.

So of course I bought the new device the moment it became available.

I even had to go through additional trouble due to the lack of the optical digital port (the old AppleTV was connected to a Sonos playbar), but I found an audio extractor that works well enough.

So now after a few weeks of use, the one thing that actually pushed me to write this post here is the fact that the new AppleTV is probably the most unfinished and unpolished product that I have ever bought from Apple. Does it work? Yes. But the list of small oversights and missing pieces is as big as I have never seen in an Apple product. Ever.

Let me give you a list – quite like what I’ve done 12 years ago for a very different device

  • While the AppleTV provides you with an option to touch it with an iOS device for the configuration of the Wifi and the appleid settings, I still had to type in my appleid password twice: Once for the AppStore and once for the game center. Mind you, my appleid password is 30 characters long, containing uppercase, lowercase digits and symbols. Have fun doing this on the on-screen keyboard
  • The UI is laggy. The reason for having to type in the game center password was because the UI was still loading the system appleid as I was pressing the “Press here to login button”. First nothing happened, then the button turned into a “Press here to sign out button” and then the device reacted to my button press. Thank you
  • The old AppleTV supported either the Remote app on an iPhone or even a bluetooth keyboard for character entry. The new one doesn’t support any of this, so there’s really no way around the crappy on-screen keyboard.
  • While the device allows you to turn off automatic app updates, there is no list of apps with pending updates. There’s only “Recently updated”, but that’s a) limited to 20 apps, b) lists all recently updated apps, c) gives no indication what app is updated yet and what isn’t and finally d) isn’t even sorted by date of the last update. This UI is barely acceptable for enabled automatic updates, but completely unusable if you want them disabled to the point that I decided to just bite the bullet and enable them.
  • The sound settings offer “Automatic”, “Stereo” and “Dolby Surround”. Now, “Dolby Surround” is a technology from the mid-90-ies that encodes one additional back-channel in a stereo signal and is definitely not what you want (which would be “Dolby Digital”). Of course I’ve assumed that there’s some “helpfulnes” at work here, detecting the fact that my TV doesn’t support Dolby Digital (but the playbar does, so it’s perfectly fine to send out AC/3 sinal). Only after quite a bit of debugging I found out that what Apple calls “Dolby Surround” is actually “Dolby Digital”. WHY??
  • The remote is way too sensitive. If you so much as lift it up, you’ll start seeking your video (which works way better than anything I’ve seen before, but still…)
  • Until the first update (provided without changelog or anything the like), the youtube app would start to constantly interrupt playback and reload the stream once you paused a video once.
  • Of course in Switzerland, Siri doesn’t work, even though I would totally be able to use it in english (or german – it’s available in Germany after all) Not that it matters because the Swiss store is devoid of media I’d actually be interested in anyways and there’s no way for third-parties to integrate into the consolidated system-wide interface for media browsing.
  • Home Sharing doesn’t work for me. At. All. Even after typing in my Apple ID password a third time (which, yes, it asked me to).
  • It still doesn’t wake up on network access, nor appear in the list of Airplay-able devices of my phone when it’s in sleep mode. This only happens in one segment of my network, so it might be an issue with a switch though – wouldn’t be the first time :/

I’m sure as time goes on we’ll see updates to fix this mess, but I cannot for the life of me understand why Apple thought that the time was ready to release this.

Again: It works fine and I will be brining one to my mother next Friday because I know she’ll be able to use it just fine (especially using the Plex app). But this kind of lack of polish we’re used to on Android and Windows. How can Apple produce something like this?