Archive for February 5, 2020

Wednesday, February 5, 2020 [Tweets] [Favorites]

Universal Purchase


Starting in March 2020, you’ll be able to distribute iOS, iPadOS, macOS, and tvOS versions of your app as a universal purchase, allowing customers to enjoy your app and in‑app purchases across platforms by purchasing only once. You can choose to create a new app for these platforms using a single app record in App Store Connect or add platforms to your existing app record.


Xcode 11.4 supports building and distributing macOS apps as a universal purchase. To distribute your macOS app as a universal purchase, specify the same bundle identifier as your iOS app in the Xcode template assistant when creating a new project. If you have an existing project, edit its bundle identifier in the Project Editor.

Universal purchase is enabled by default for new Mac Catalyst apps created in Xcode 11.4. When you create a new Mac Catalyst app, it will use the same bundle identifier as your iOS app.

It’s great to have the option for universal purchases, but tying it to the bundle identifier seems problematic. What if you’ve already shipped an app for multiple platforms? Apple doesn’t let you change the bundle identifier. Do you have to abandon the old app (losing its links and ratings and migrating its files and AppleScripts) or maintain two separate apps?

From the business side, it’s a great user experience for customers who want to pay once and get everything. But what about customers who only want the iPhone version and may not even own a Mac or Apple TV? They have to pay the same price? And, for developers, this is likely to further devalue software. Get all the versions for one low price, with Apple implying that it didn’t take much extra effort.

Steve Troughton-Smith:

if Apple was actually planning shared purchase this year then you’d think they would have launched it alongside Catalyst instead of making us go through all that now for dead-end bundle IDs and store records

Joe Cieplinski:

Universal purchases for Mac/iOS is yet another reason to go subscription.If you don’t offer universal, people will lob crap at you. If you go subscription, they will too. Might as well take the option that makes more money.


Update (2020-02-06): Craig Hockenberry:

Before you change a bundle ID for a macOS app, make sure you understand the implications. If you’re never heard of lsregister, you don’t understand the implications.

Then you need to think about receipts and app ID prefixes: @robotspacer asks some good questions - and without answers, I would not go anywhere near this feature.

Business-wise, I feel like it’s going to be as good for the Mac as Universal apps were for the iPad.

More platform-specific work for less overall revenue, and ultimately a bad move for all involved (especially customers).

Update (2020-02-07): Craig Hockenberry:

One hidden surprise with Universal macOS and iOS apps: you can no longer use the exact same name for different SKUs. Names have to be unique across all platforms.

Use this trick if needed: instead of U+002D for a dash, use something like U+2013, which is visually similar.

Update (2020-02-14): See also: Reddit.

Update (2020-04-17): Jeff Johnson:

It appears that the purpose of Universal Purchase was to encourage developers who didn’t yet have a Mac version of their app to produce one (also the purpose of the Catalyst technology in Catalina). But for developers and customers of apps that already had both Mac and iOS versions, Universal Purchase is an incredibly bad deal. It feels like a betrayal from Apple, because developers who have been “loyal” and did the “right thing” from the beginning, who made native AppKit and UIKit apps, are punished, while developers who never bothered to make a Mac app are rewarded.

Xcode 11.4 Beta


Build settings have a new evaluation operator, default, which you can use to specify the default value of a build setting if it evaluates to nil in the context of the evaluation.


View debugging supports showing layers using the Show Layers menu item in the Editor menu.


The exception reason now surfaces as an editor annotation. You can inspect the Exception object in Variables View and find the backtrace of the original uncaught exception, if any, in the Debug Navigator.


Selecting a SwiftUI preview in code now highlights the corresponding preview in the canvas, and vice versa.


You can call values of types that declare func callAsFunction methods like functions.


Subscripts can now declare default arguments.


XCTest now includes throwing variants of the setUp() and tearDown() instance methods, allowing tests to throw errors in Swift during set up or tear down. Override the setUpWithError() or tearDownWithError() methods instead of setUp() or tearDown(), respectively.


Errors thrown by Swift test methods now record the source location where the error was thrown.


XCTest now supports dynamically skipping tests based on runtime conditions, such as only executing some tests when running on certain device types or when a remote server is accessible.

Looks like some great improvements (including many for the simulator). Too bad it requires Catalina.


Update (2020-02-06): Paul Hudson (Hacker News):

The first beta of Swift 5.2 just landed with Xcode 11.4 beta, and it includes a handful of language changes alongside reductions in code size and memory usage, plus a new diagnostic architecture architecture that will help you diagnose errors faster.

Update (2020-02-07): Peter Steinberger:

We benchmarked Xcode 11.3.1 and Xcode 11.4b1 after seeing the reported Swift compiler performance improvements. For ObjC/C++ heavy code Clang became around 10% slower, not faster.

Peter Steinberger:

Xcode 11.4b1 is great overall. Similar stability, much nicer Simulator, everything still compiles, way faster UI tests, amazing view inspector upgrades. Gonna use this as main IDE now. Great work, Xcode team!

Update (2020-02-14): Shai Mishali:

Xcode 11.4b1 mentioned it offers Swift compiler improvements, and yet:

On a ~7 years-old project with mixed Objective-C/Swift (60%/40%)

3 clean (nuked Derived Data, etc.) build-time averages:

Xcode 11.3: ~185 seconds
Xcode 11.4b1: ~230 seconds

About 22% slower

Donny Wals:

I’m pretty sure that the ability to test push notifications in the simulator is my favorite new feature in Xcode 11.4

Update (2020-02-17): Donny Wals:

Swift isn’t the only language to allow its users to call instances of certain types as functions. A language that I have used a lot that allows this kind of behavior is Python. The ability to invoke instances as functions is very interesting in certain applications where you want to represent a stateful calculator, parser or computing object. This is very common in complex math operations and machine learning where certain objects might hold on to some state and only implement a single method.


In many cases, a simple closure wouldn’t do. The object that ends up handling the route would need to have a database connection, a concept of caching, authenticating and possibly a lot of other functionality. Capturing all of that in a closure just doesn’t seem like a great idea. Instead, you’d want some kind of complex object to handle this route. And that’s exactly the kind of freedom we get with callAsFunction.