all micro contact rss

SwiftUI and PopToRootController Workaround

I’m thoroughly enjoying using SwiftUI. But like any new API, there are limitations that can at times be maddening.

One issue I ran into while developing the UI for logging custom and favorite items in my RECaf watch app was the lack of any way in SwiftUI to pop back to the root of the navigation stack.

Here’s my scenario: You start with a list, showing items for Custom Entry and Favorites. Tap Favorites, and you are pushed to a list of your favorites. Tap one of the favorites, and you are presented with a confirmation screen, detailing what you are about to log. At the bottom are two buttons. One for completing the log, and the other for canceling.

Because SwiftUI has no function for getting back to the root, I had no way to pop the customer all the way back to the initial list after they canceled or logged. I was stuck hoping that Apple ends up adding this function sooner rather than later. I couldn’t ship this, knowing the customer would have to swipe right several times to get back to the home screen.

But then I remembered, my root SwiftUI view was inside a WKHostingController.

WKHostingController is just a WKInterfaceController that expects a SwiftUI body view. It still retains all the methods of WKInterfaceController, including awakeFromContext.

So what I did was set the hosting controller to listen for a notification indicating a reload was needed. Then, on my Cancel button and on my log confirmation button, I could post that same notification.

And sure enough, the navigation stack pops back to the root.

I’ve seen other workarounds that use more convoluted ways to get SwiftUI to go back one level in the hierarchy, or to dismiss a presented view. But for getting all the way down a long stack in one move, this is going to be my goto. At least until Apple gives us a proper way to pop to root.

Here’s the code for my hostingController:

import SwiftUI


final class NewEntryController: WKHostingController<NewEntry> {
    
    override func awake(withContext context: Any?) {
        NotificationCenter.default.addObserver(self, selector: #selector(reloadRoot(notification:)), name: Notification.Name(NotificationKeys.watchReloadNeeded), object: nil)
    }
    
    @objc func reloadRoot(notification: Notification) {
        self.popToRootController()
    }

    override var body: NewEntry {
        NewEntry()
    }

}

Watch Dependence

I have reached the unfortunate conclusion that RECaf’s watch app will not be able to go fully independent this fall with the release of watchOS 6. While you have always been able to log from your wrist using the app or Siri shortcuts, I was hoping folks who didn’t want to keep RECaf installed on their phones would be able to continue using RECaf on their wrist.

There are simply too many things that can’t be done on watchOS alone at this point, however. So for now, you’ll have to keep that phone app installed.

What’s missing?

  • HealthKit queries on watchOS are limited to about 7 or 8 days worth of data. If RECaf does a sample query for all your caffeine, it will only get the last 8 days or so. I need 30 days to calculate your average caffeine intake. I need 60 days to figure out your most frequent sources and amounts and offer those for quick logging. For now I will have to continue calculating this on the phone and sending the data to the watch.
  • You can’t subscribe to changes in HealthKit in the background. If you log some caffeine using your phone or with Siri, the watch will have no idea that log happened until the app is launched. That means I can’t update your complication without using direct messaging from the phone to the watch.
  • No in-app purchase. You can’t subscribe to RECaf without launching it on your phone. There is no mechanism for purchasing on the watch at this point. I had planned to work around this by having you launch RECaf on your phone just once after installing. That would have been a terrible user experience, but I was willing to accept the trade off before I discovered the other limitations above.

This doesn’t mean RECaf won’t get some great improvements in watchOS 6. I’m working hard on a major update that will make the watch component of RECaf more independent. (You will be able to log all sources, not just your frequent items, for instance.) It just won’t be completely untethered from your phone just yet.

I’m sure Apple is aware of these restrictions and is planning on giving Apple Watch even more power in the coming years. But in the meantime, I suspect fewer apps than you might be hoping will be going fully independent in the short term.

If the Shoe Doesn't Fit, Grab Your Shoehorn

The internal drama explains a lot about Apple’s dilemma. Its one major new product of the post-Jobs era, the Apple Watch, made its debut five years ago. Its iPhone business is faltering, and more recent releases like its wireless AirPods haven’t been enough to shore up falling sales. It hasn’t had a megahit new product since the iPad that started selling in 2010.

via The Wall Street Journal

Every line of that paragraph infuriates me.

Its one major new product of the post-Jobs era, the Apple Watch, made its debut five years ago.

And the iPad debuted 5 years before that. The iPhone was only two years prior, but the iPod was 7 years before that. And the Mac, well that was 17 years prior to that. See a pattern here? Neither do I. Major new products from Apple come out when they are ready. There has never been a period in Apple’s history with hit major new product after major new product.

Its iPhone business is faltering

A 5% decline on 46-million units per quarter now qualifies as “faltering.” Check.

and more recent releases like its wireless AirPods haven’t been enough to shore up falling sales.

AirPods are a legitimate cultural phenomenon. But that doesn’t fit the narrative here, so let’s point out that they’re not expensive enough to make up for “faltering” iPhone sales. Because that won’t directly contradict your point later in the article that iPhone X was too expensive.

It hasn’t had a megahit new product since the iPad that started selling in 2010.

iPad is the fastest-selling consumer electronics gadget of all time. Sure, Apple hasn’t had a hit of that magnitude since. It also never had one of that magnitude prior. And neither has anyone else. What’s your point?

Look, I get it. Jony Ive quits Apple, and you as a journalist see an excellent opportunity to cash in on the sentiment that “things just aren’t as great as they used to be.” So you gather a bunch of disgruntled former Apple employees who haven’t exactly “had a megahit” themselves since they left and cast as negative a picture as you can, so you can generate as many clicks as you can. But don’t expect me to read this drivel and actually believe any of it.

By the end of the piece, I’m supposed to think Apple is doomed because Ive would rather spend time with his ailing father than watch a Lady Gaga concert. (I shit you not.)

But of course, my Twitter timeline is full of Apple fans with malfunctioning keyboards telling me this is a “must read.” So, mission accomplished, I guess?

The Day the Duplicates Died

It’s almost hard to believe I wrote this post about duplicate track issues in iTunes and iCloud Music Library way back in December 2017, and yet, in the shipping version of iTunes today, that bug is still present.

Not a day goes by since I wrote that post (I have the tracking stats to prove it) that at least a few people don’t find my article via a Google search, which means people have been frustrated with this issue and have been looking for solutions for a very long time. Some have even reached out to me to thank me for reassuring them they aren’t crazy.

Imagine how my ears pricked up, then, when rumors started floating around this year that Apple was poised to retire iTunes in the next major version of macOS? While many were frightened Apple was taking away their precious music player of choice, I was elated at the chance of a brand new app which would presumably not elicit this same bug. After all, the rumor originally figured the new Music app would be ported over from its iOS counterpart on the iPad. And the iPad version doesn’t have this bug.

But then Apple announced Music for macOS (which will ship this fall with the Catalina update), and it quickly became apparent that rather than a complete rewrite, the app would simply be iTunes with a new name and a lot of its more bloated features removed.

Happy as I am to see the bloat removed, and as much as I believe strongly that you should not throw away perfectly functioning code for no good reason, the code in iTunes was far from perfectly functioning.

So, the big question: Would this bug still be present in Music, despite all the work Apple had put into rebranding and reworking it?

Luckily, I don’t have to wait until September to find out. I’m an Apple developer, after all, so I get early access to the beta versions of this software.

So I installed Catalina’s beta onto my Mac, fired up Music, turned on the iCloud Music Library feature, and…

Well, at first, the duplicates appeared again. I was despondent. I could not believe Apple didn’t bother addressing this issue after at least one-and-a-half years. (In all likelihood, the bug has been around quite a bit longer than that.)

But then, a few moments later, something wonderful happened. All the duplicate tracks disappeared in an instant. It was if Music caught itself making the same old mistake, and then corrected itself.

As of this writing, the duplicate tracks are still gone. iCloud Music Library is functioning on my Mac as intended. And any new tracks I download are being synced perfectly between my iPhone and Mac.

Fingers crossed that all is well, and we will finally be able to call this bug defeated.

I say will be, of course, because Catalina is not shipping yet. Those of you who are not developers will have to wait to get this fix. And there’s always the chance that the bug will return before shipping. But I highly doubt it. I think there are going to be a lot of happy Music users come fall.

On Intentional Subscriptions

There is a concept in user interface design called the Principle of Least Surprise, where you want to design systems in such a way that they surprise their users least. I think a similar concept applies to subscription pricing. The ideal (from a user friendliness perspective, not best business perspective) system for customer subscriptions should never surprise the customer with a charge. The customer should always be happy to see a charge appear on their credit card.

In other words, their subscription payments should always be Intentional.

via David Smith

I mostly agree with David in this piece on Intentional Subscriptions. I have a few thoughts, however.

First and foremost, I love the idea of a system-owned screen for confirmation of subscriptions. Apple has made the guidelines for the subscription presentation page so muddy that almost no developer I know is ever confident that the requirements are being met by their apps. Let Apple design this page exactly how they want it, and let every app get the exact same screen. More consistency for customers, leading to fewer surprises. No more fearing I’m going to get rejected. Apple saves money on App Review. It’s a win all around.

Let us upload Terms and Conditions text and a description of each subscription item directly into AppStoreConnect. Then, Apple can pull everything it needs right off its own servers. All a dev would need to do is ask to pop up the SubscriptionConfirmationViewController, or whatever they call it. Similar to asking for a review prompt. When dismissed, we get a completion handler with confirmation that they subscribed and an ID for the item to which they subscribed, or a notice that the customer cancelled or that the transaction failed. Devs could even update their text descriptions this way without having to upload a new app binary. They’d have to get the new metadata approved, of course. Which would cut down on fraud as well.

Please, please, Apple. Do this.

Next, notifications. I agree that a notification makes more sense than an email on renewal of subscriptions. However, I get hundreds of notifications every week. And I’m pretty careful about turning off notifications for many apps. Ideally, these renewal notifications would be on by default, but they should only fire at the end of trial and at the first renewal after. And I should be able to opt out of them on an app-by-app basis. I don’t need to be reminded every month for every one of my apps separately. (There’s a fine line between not surprising a customer and treating them like a child who can’t be responsible for their own finances.)

Curtis Herbert said it better on Twitter (thread):

As far as being able to cancel your subscription right from the notification itself is concerned, I think this sounds better on paper than it would be in practice. People don’t read their notifications carefully. I see a ton of people accidentally unsubscribing to apps, then getting super confused when their apps no longer work. This would lead to increased support load for everyone. Tapping on the notification should take you directly to the subscriptions page on the App Store, however. (See Curtis’ thread linked above.) Then you could unsubscribe, intentionally, with another tap or two.

As far as auto-renew happening a the end of the trial, I think a lot of developers don’t realize how common this is in most markets. Yes, it’s different from the shareware days of yesteryear, but auto-converting trials are a practice with which most people are very familiar. Magazines have been doing this for decades, for instance. And it makes sense. For every customer who we might be saving from an accidental payment by disabling auto-conversion, we’re annoying ten others who already indicated they want to keep using the app and don’t want to verify yet again.

As long as customers are getting notifications before the trial ends, and they get a 24-hour grace period to cancel (another good idea from Smith) I don’t see any need for Apple to remove the convenience of auto-renewal after the trial ends.

Especially for apps like my own RECaf that almost never get launched (you can interact with it primarily through Siri), having to manage subscription renewal in the background could get hairy quickly. I could see tons of customer support issues stemming from this.

Any developer who doesn’t want to have a trial auto-subscribe at the end has the freedom to do that right now. Just don’t use Apple’s trial system. Track how long the customer is using the app yourself, then just present the subscription without a trial at the appropriate time.

Lastly, and I’ve written about this before, I think Apple should require apps to provide a link to the subscription management page inside our apps. We have the ability to do that (I do it inside RECaf), but few developers actually do.

And that subscription management page also absolutely should be linked at the top level in Settings.app with the title “Manage Subscriptions.” I don’t know any non-developers who know where that page is buried inside the App Store and iTunes settings.