SwiftUI

I very much like the idea of a declarative definition of a user interface and thus I’m motivated to kick SwiftUI’s tires. Having worked halfway through the tutorials, I’m a little bit concerned about the scalability of SwiftUI. Sure, a DSL is always going to win the elevator pitch because it looks so nice and elegant.

At least the WWDC videos about SwiftUI that I have watched so far restrict themselves to more or less the bare minimum of complexity that you might want to add to the declarative definition of an app’s user interface. And already in the simple cases SwiftUI starts to get messy, e.g. with respect to formatting chained expressions1.

My (probably not very popular) point is that I personally believe that defining a scalable XML-based format will way more likely yield a good result than designing a DSL for the same purpose because scalability is already baked into XML itself. You can wrap tags around tags around tags real simple and the resulting complexity ist still kept under control.

In many cases, the problem is that the design of a DSL will start with the simple and elegant cases and stay with the simple and elegant cases for some time – until it needs to expand towards supporting higher-level complexity2. But: if the need for supporting higher-level complexity hasn’t been considered from the start than the DSL will fall apart pretty quickly.

To drive my point home, I have actually done some work in declarative UI definition in the Windows world, specifically the Windows Presentation Foundation (WPF)3. Microsoft uses a dialect of XML named XAML for the UI declaration.

I fully understand that XAML in particular has lots of problems and isn’t as much fun as you might want it to be. But still, given the choice between declaring a UI in an XML dialect or else by means of using a DSL (like SwiftUI) I would personally actually very likely prefer the XML.

  1. That does not even include the point where suddenly imperative paradigms are mixed into the declarative language.
  2. Which – let’s face it – it will inevitably have to.
  3. Yes, in a text editor. There is a graphical frontend for XAML. My experiences with the graphical frontend are staggering and I have yet to come across anyone seriously endorsing it.

iPadOS

It didn’t come as a total surprise to me, but it was very close. Only five minutes or so before the keynote started I saw a retweet from someone who mentioned the possibility that a dedicated “branch” of iOS existed (named iPadOS) specifically for the iPad.

And then it became a reality. The strange thing about the naming is that iOS originally has been conceived as iPhoneOS and was only later rebranded as iOS when the iPad entered the market.

Maybe it’s just me, but wouldn’t it be ironic to give all sorts of devices their own specific OS-branding and the iPhone, arguably the most important device in the lineup (and the one that started this whole family of OS-variations) stays with the generic iOS branding?

Maybe a further rebranding may happen when final versions of <modifier>OS 13 are released to the world. And maybe the term iOS finally becomes some sort of abstract base marketing term for the entire class of OS.

Feature-wise, I’m delighted about what’s in store for the next major OS release on my iPad. Although I might want to mention that font management and a download manager for Safari would also make excellent features for the iPhone, just sayin’. I keep hoping for a trickle-down of such useful details to the iPhone at some point in time.

Overall, it is a good sign that Apple gives iPadOS such a prominence. At the very least it means that no WWDC will happen from now on where the iPad is not going to get some new OS-features. Neglect has happened way to many times in the past.

As Steve Troughton-Smith observed, they can’t ignore it anymore. Apple put this burden on themselves for good.

CalZones

David Smith‘s latest app, a calendar app named CalZones that is putting the focus on facilitating the handling of events across multiple timezones, is making quite a splash these days.

The app is mentioned and/or reviewed on many tech blogs and seems to receive universal praise, as far as I can see. And the praise is not unjustified. CalZones comes with some really fresh design and animation ideas, and – as mentioned before – the ability to take the friction out of handling multiple time zones in calendar events is certainly a compelling selling proposition.

The MacStories review mentions some „strategic“ shortcomings Federico identified during the beta period. My personal list of features that I would like to see added to CalZones is more about the practical aspects of working with a calendar app:

  1. It does not seem as if there is a touch gesture for navigating to the current date. However, when using a hardware keyboard, the app supports the shortcut ⌘ T for this purpose.
  2. No search. This is big, a calendar app that does not support search is not going to make it on my devices.
  3. I haven‘t found any way to create a calendar entry boundary with a finer granularity than 15 Minutes. For example, you can start an event at 9:15, but 9:12 is apparently not foreseen. Such a limitation, if confirmed, is not exactly compatible with calendar entries for train or flight connections1.

Despite all the complaints, I still think that Smith delivered a 1.0 that should be a solid basis for further iterations.

  1. At least for flight connections, I personally see a relevance for the ability to work with different timezones.

Five Years of Monument Valley

Remember Monument Valley? The game, developed by the studio ustwo games, was originally released in early 2014. In the fall of that year, I got my first iPad and (if I remember correctly) Monument Valley was the first game I‘ve downloaded onto my new device.

Monument Valley is a masterful play with geometry and perspective, and the game keeps giving. This week, ustwo shipped the „fifth anniversary edition“ of Monument Valley, accompanied by the slightly cryptical release note:

Don‘t miss out – the party‘s over on May 1st.

Whatever that means, I am already done with the ten additional levels. It was fun.

2019

Back in 2015, I went to the local Apple Store because my MacBook Pro would no longer boot. It had been hit hard by the notorious graphic card glitch that plagued the MacBook pro class of 2011. I got a new logic board for free as part of the repair program for this well-known issue.

The year went 2016, and I made another appointment for the same computer that was even in a worse condition than the year before. According to the Genius, the computer did not show any signs of animation and it was not even possible to run a low-level diagnostics, and so the computer was pronounced dead.

But because the of the uncomfortably short lifetime of the replacement logic board the Genius offered a 10% discount if I bought a new computer in the store. I honestly had the intention to buy a low-to-mid-range iMac and walk out of the store in peace. But, turns out, all stock iMac models in the store were fitted with a spinning disk or a fusion drive.

I politely informed the Apple sales person that it was 2016 and that, in the price range that Apple products typically occupy, nobody should sell computers that have a spinning disk.

She agreed with similar politeness, but nevertheless replied to me that SSD-configurations are built-to-order, and the discount voucher would not be good for a BTO-configuration. So I left the store Mac-less, and that day marked the beginning of a longer period without owning a Mac as my primary computer1.

With a little luck, I still have that voucher in my records. It‘s 2019 now, and I still would not be able to use the voucher to buy a stock iMac in the Apple Store that comes with an SSD.

Three. Years. Later.


  1. That period, to some extent, still hasn’t ended. I might have mentioned this already. 

From Edison to Newton?

What can I say, I did not see this coming. Newton is back on the App Store. The news broke to me after reading a sceptical tweet written by Stephen Hacket that linked to an article in 9to5Mac.

Would I be willing to switch back to Newton? Instead of a straight answer, let me give a short recap of my client usage since Newton’s regrettable shut-down.

I’ve spent the Newton-less time period mostly using Edison Mail. Edison is a solid alternative, but no actual replacement. And it has it’s own problems. For example, the rendering of folder trees in my main account is seriously broken.

Over time, I also switched to using Outlook, Apple’s own Mail.app, and – in times of desperation – even Airmail and Spark.

Airmail didn’t last longer than a a day, it’s still as buggy as it ever was. Spark made a mess out of a message I’ve got for confirmation of a Genius Bar appointment.

Seriously, this specific message rendered in Spark does not even look similar to what it looks like in any other client. It was not even possible to distill a calendar entry from what Spark presented me with. Who believes that such a behavior is a good idea?

On the bright side, my impression of Mail.app was surprisingly positive. But it finally wasn’t able to muster enough gravity to keep me in orbit. Lack of sharing options, rendering of messages, no way to print a message1, just to name a few shortcomings.

One of the biggest upsides of Edison (and Newton, for that matter) is that it uses the space that it gets and renders e-mails such that they become actually readable.

I’ve got two exhibits as counter-examples: Mail.app and Outlook. Especially the latter is the worst in this regard. On iPad, Outlook inexplicably uses only a small fraction of the available space to render messages, and the result in many cases is way too small to be comfortably read.

All that said, will I be spending money for Newton? Well, probably. I’ve got a couple of weeks on my existing plan2 before I need to decide about renewing my subscription. If the subscription rate were half the price it would be a no-brainer for me. But the current price makes me think twice.

On the other hand, with respect to my personal set of requirements against an iOS e-mail client, Newton does a near perfect job3, and (again, in my personal impression) none of the competitors comes close.


  1. Yes, in very limited cases I actually print e-mails. This happens mostly to get compensation for business trips using public transport. 
  2. For which I got a partial refund after Newton was officially taken down. 
  3. The new version of Newton comes with nice additions to the already impressive feature set. 

Automate storing of images with Pythonista and DEVONthink

One of my habits is to take a screenshot of my iPhone and iPad home screen on the first day of each month and store the screenshots away for further reference.

Until very recently, I used to store the screenshots somewhere in the file system.

I used to have a Workflow to store the screenshots into their respective directories. But at some point, the workflow broke on the basis of no longer being able to access an arbitrary directory in response to stricter sandboxing rules.

As a remedy to this issue, I considered adding the screenshots to one of my DEVONthink (To Go) databases. In this case, I would no longer have a problem with sandboxing and can still add the screenshots to separate folders inside of the DEVONthink database.

However, to make this possible I need to automate the process of adding of an image to DEVONthink. And this can – to the best of my knowledge – only be done by using the URL scheme for DEVONthink.

To celebrate the news that Pythonista is back in active development, I spontaneously decided to try to implement the storing of screenshots to DEVONthink using Pythonista1.

I learned a lot during this quest and found surprisingly little documentation about DEVONthink‘s URL scheme. Therefore, I figured it might be worth sharing my experience for the potential benefit of fellow DEVONthink users with a similar goal.

I started with one of the example scripts that implements access to the screenshots in my Photos library and modified it accordingly.

album = photos.get_screenshots_album()
screenshots = album.assets
if not screenshots:
    print('You have no screenshots in your library.')
else:
    # Access latest screenshot
    newest = screenshots[-1]

I learned that Photos provides a „virtual album“ of all screenshots in your library and that this album is accessible (you guessed it) by calling the method photos.get_screenshots_album().

This call returns an AssetCollection, of which the last element (an Asset) is taken into further consideration in the form of the variable newest.

After – by way of accessing the element with the index -1 – achieving access to the latest screenshot, meta-data (specifically the creation date) are utilized to form the title of the screenshot in the DEVONthink database.

In order to not break the pattern of naming the existing screenshots, the title takes the form of a filename using a {yyyy}_{MM}.{imagetype} pattern2, even if it is stored in a DEVONthink database.

The year can be accessed by calling the creation_date.year property on newest.

    year = newest.creation_date.year
    month = '{0:0>2}'.format(newest.creation_date.month)
    title='{0}_{1}.png'.format(year,month)

Setting the month was a bit of a challenge because the pattern asks for a two-digit month and the property creation_date.month returns just a single digit for all single-digit months.

Of course, this issue could be solved “quick and dirty”. But I wanted to find a solution by using str.format() with a fitting format string3. The result is shown in the code snippet above. I’m not sure whether this is the most elegant solution, but at least it works.

As mentioned before, I did not find any official documentation of the URL Scheme for DEVONthink, especially for the creation of images. After a round of searching, I found some hints in an article Federico Viticci at Macstories. That got me started.

Also, Gabe Weatherhead over at Macdrifter has documented some of his automation (mostly for adding markdown text) of DEVONthink.

The usage of any URL Scheme to pass an image to the database requires the serialization4 of the image in a URL-friendly form:

    img = newest.get_image_data(original=False)
    b64encoded=base64.b64encode(img.getvalue())
    urlEncodedPic = urllib.parse.quote_plus(b64encoded)

In addition, it is also necessary to decide about the target folder to store the image. As mentioned before, screenshots for iPhone are stored in a different folder than screenshots for iPad.

In other words, it will be necessary to programatically find out whether the script runs on iPad or iPhone and select the target folder accordingly. To solve this issue, I posted a question to the Pythonista User Forum and quickly got a response: use platform.machine().

I’ve had an eye on this method before, but unfortunately was distracted by the documentation that mentioned ‘i386’ as a possible return value and that (at least in my personal understanding) suggests a hint about the architecture rather than the concrete hardware.

A deeper look at the chapter in the documentation might have revealed that there is also platform.architecture() and therefore platform.machine() very likely has a different purpose than to return machine’s architecture. Anyway …

    device = platform.machine()
    if (device.startswith('iPad')):
      destination = '58B06234-37B8-485F-AB7C-DC67CACFB9FB'
    elif(device.startswith('iPhone')):
      destination = '71E35B25-466E-4E2A-AE2D-C65C4BAFB254'

This finally gets the last ingredient for the creation of the URL and its subsequent execution.

    url = 'x-devonthink://createimage?source={0}&title={1}&destination={2}'.format(urlEncodedPic,title,destination)
    webbrowser.open(url)

The argument named source passes the serialized image. The call to webbrowser.open() finally launches DEVONthink and adds the screenshot to the respective folder.

I have to say that this really works quite well and I’m very pleased with the result. Of course, the amount of time spent working on the implementation of the discussed script would probably be good for manually storing the screenshots worth of several years.

But that’s not the point. I learned a lot in the process and I may now be in a better position to solve further automation tasks with less effort. For example, variations of the script to handle photos or text rather than screenshots could easily be created.


  1. A possible alternative would have been to do the implementation in Javascript using Scriptable as the development platform. However, I know almost nothing about Javascript. My Python is better, but still not on expert level. ↩︎
  2. Where {yyyy} represents the four-digit year and {MM} represents the two digit month, with January being represented by ’01’. The {imagetype} boils down to ‘png’ or ‘jpeg’. ↩︎
  3. I’m a big fan of string formatting. ↩︎
  4. This means that the image is transformed into a sequence of textual characters form which the receiving end can reconstruct the original image. ↩︎

Apollo 50

The Twitter account Apollo 50th is doing a great job by providing a detailed live-coverage of the events and communication of Apollo missions 50 years ago.

Currently on: Apollo 8.

Castr…oh!

From the Supertop blog:

We have some news to share. Tiny has purchased a majority stake in Castro. We are still shareholders and will continue working on the app full time.

I would have loved to see Castro and Supertop thrive as an independent gig. On paper, this is good news because it means that the app will be more sustainable thanks to the financial support offered by the new majority stakeholder.

Castro has reached a size where the demands of running the business have been pulling us in too many different directions. We haven’t been able to focus as much on the core work of designing and building a product. Selling to Tiny gets Castro access to more resources, contacts and expertise. By growing the team we can specialize our roles to be more focused individually and get more done collectively. We can get back to what we’re good at and what we love doing.

On the bright side, this might get us the long anticipated iPad version. Who knows, maybe Castro 4 (that is also discussed in the article) will be released as a universal app. I certainly wouldn't mind.