Bringing Adaptivity to Mac Catalyst
Introduction
Adaptivity is an app which helps developers and designers visualise how iOS’s Size Classes and margins for layout, readable content and the safe area look on real devices and how they change with respect to orientation, iPad Slide Over/Split View and Dynamic Type size changes.
In the full paid version of the app, Adaptivity (A), there are also screens to explore System Colors, System Images, System Materials and Table Views. On iPadOS 13, this version supports multiple windows. Adaptivity (B) is a free, feature-limited, version. Adaptivity (C) is an iPhone-only version of Adaptivity (B) which demonstrates how iPhone-only apps appear on iPad.
There is much more information and many screenshots on my website that describe all of Adaptivity’s features on iOS and Adaptivity for Mac. The iOS and Mac versions of Adaptivity (A) are a universal purchase. Buying one version allows the other to be downloaded at no extra cost using the same Apple ID.
This article summarises the work it took to bring Adaptivity to the Mac. It is not intended to be a tutorial or guide to porting an app to Mac Catalyst. If you are planning porting your own apps to Catalyst this should give you an idea of the kinds of changes that will be required. I do go in to more detail on some issues that I had.
Quite deliberately, I chose to make as few Mac-specific customisations as possible so that Adaptivity would better demonstrate the default behaviour when porting an iPad app to Catalyst — what you get “for free”. There was still a lot more work than just checking the box in Xcode.
Resources
See the following resources for more information on developing with Mac Catalyst.
Apple Documentation
- Bring Your iPad App to Mac
- Creating a Mac Version of Your iPad App
- Mac Catalyst section in the Human Interface Guidelines
- Mac Catalyst top level documentation has many links to other information
- Removing the Title Bar in Your Mac App Built with Mac Catalyst
- Adding Menus and Shortcuts to the Menu Bar and User Interface
WWDC 2019 Videos
- 205: Introducing iPad Apps for Mac
- 235: Taking iPad Apps for Mac to the Next Level
- 809: Designing iPad Apps for Mac
Tutorials
- Catalyst Tutorial: Running iPad apps on macOS from Ray Wenderlich
- Ultimate Catalyst Guide by AppVenture.me
Other Articles
- Beyond the Checkbox with Catalyst and AppKit by Steve Troughton-Smith discusses how to use AppKit APIs in your Catalyst app
Enabling Catalyst Support
Xcode 11.4 or later is required to build universal Mac Catalyst apps. Apple’s article Creating a Mac Version of Your iPad App explains how to enable support for Mac Catalyst:
To add support for Mac, open your Xcode project and select the iOS target that you want to configure. In the General tab, under Deployment Info, select the Mac checkbox.
Fixing the Deprecations
Building for Catalyst is equivalent to building for iOS with a minimum deployment version of iOS 13. Anything referred to in code that was deprecated in iOS 13 or earlier will generate a warning when building for Catalyst. Even when using availability checks to support different iOS versions, deprecated methods or properties in the else
clause will trigger warnings. Availability checks are a runtime feature, not a compile time feature. It does not matter that the else
clause will never actually be executed when run on Mac, the compiler still sees that code when building and will generate warnings about using deprecated APIs.
The iOS version of Adaptivity has a minimum deployment version of iOS 11. Demonstrating behaviour on different iOS versions is an important feature of the app, so I wanted to keep that support. Conditional compilation was required to avoid referring to the deprecated APIs when building for Catalyst.
UIViewControllerPreviewDelegate
for 3D Touch previews. Adaptivity already had support for iOS 13'sUIContextMenuInteractionDelegate
UISearchController
’sdimsBackgroundDuringPresentation
was replaced withobscuresBackgroundDuringPresentation
in iOS 12- Pre-iOS 13 scene workflow support for external displays. Trying to set the
screen
property for aUIWindow
is deprecated in iOS 13. All the code for handling screen added/removed notifications would never be used and could be conditionally compiled away on Catalyst - Adaptivity uses a library of helper classes that is shared with many of my other apps. That library still supported iOS 8 and included usage of some long-deprecated classes such as
UIAlertView
,UIActionSheet
,UIPopoverController
andUILocalNotification
. I took a detour to update that library to use a minimum iOS deployment version of iOS 11. Which required a further detour to update a few of my older apps which used the library to also drop support for earlier iOS versions.
Unavailable APIs
Some iOS APIs and features are simply not available on Mac Catalyst:
- Add/Edit Voice Shortcuts for Siri. I removed the Siri Shortcuts row and sub-screen from the app’s Settings screen.
- Other app targets: Today Widget, Notification Content Extension, iMessage Extension, Intent Extension, Intent UI Extension, Watch app. Xcode automatically excludes these targets from the Catalyst version.
- The Dynamic Type screen allows the font to be changed on iOS 13. It uses
UIFontPickerViewController
to allow the user to choose a custom font, including those from other font provider apps (i.e. thecom.apple.developer.user-fonts
entitlement is included). This class is not marked as unavailable on Mac, but nothing happened when trying to present the font picker. I simply removed that option when run on Mac. There was another problem related to this feature which is discussed later.
Customising Adaptivity Features
Some Adaptivity features aren’t really useful on Mac or needed wording changes:
- The Keyboard screen is pointless on Mac
- Without a Notification Content Extension there’s no point generating local notifications
- The
Status Bar
andAdapt Model Presentation
settings were removed. Mac doesn’t show a status bar and the main view is always a regular size class so no adaptions are ever needed when presenting other view controllers. - Some user-facing text in table view footers and alert messages needed customising. For example, tapping (instead of long pressing) the “Context Menu” button in the main screen shows an alert “Use a long press to show a context menu” on iOS 13. For Mac it reads “Use a control click or right click to show a context menu”.
- The iOS app’s More Actions options are shown in a table view which allows an auxiliary window to be opened by dragging a row to the edge of the screen. The Mac version does support the same auxiliary window types, but only choosing an item from the File→New menu or (as on iOS) tapping the ⊕ button in the top left of a view of the same type that has been modally presented on top of a Main Window.
- Auxiliary windows on iOS have a close button which destroys the scene. On Mac I hide that button because the red traffic bar light in the window title can be used instead.
Mac-Specific Changes
There were some changes I made to the app to make Adaptivity more Mac-like but I tried to keep them to a minimum.
Content Scaling Factor
The app’s screens which show dimensions have a Point/Pixels segmented control to select the units for the size labels. Mac Catalyst apps use a scaling factor of 1 / 1.3 (≈77%) to reduce the size of apps. When showing sizes in pixels, the app takes account of this content scaling. For example, 1300 points will be 1000 pixels on a non-retina screen and 2000 pixels on a retina screen.
New App Icon
Search Controllers
On iOS, tapping into the search bar in the System Colors and System Images screens dims the background. This looked a bit strange on Mac because the search bar doesn’t take over the navigation bar as on iOS:
Don’t Tint Table View Buttons
The Mac Catalyst section of the Human Interface Guidelines has many recommendations including:
Don’t tint buttons in table rows. In your iPad app, you use a tint to show that buttons in table rows are active, but in macOS, tinted buttons in table rows look out of place.
Menu Bar Changes
Several changes were made to the default menu items. I removed the Edit and Format menus. I replaced the File→New menu item with a sub-menu to allow new instances of the primary Main Window (which retained the ⌘-N keyboard shortcut) or auxiliary windows to be created. Adaptivity does not include a Help Book so I replaced the “Adaptivity Help” menu item with items to link to my web site and the App Store page for the app.
About Window
The standard Mac About window uses the CFBundleName
Info.plist
entry for the app’s name. This defaults to $(PRODUCT_NAME)
, exposing the internal name of the target (“AdaptivityA” with no space in my case). I had originally hoped to refer to the Mac version as simply “Adaptivity” (with no suffix), but the universal app mechanism forces the same name to be used for all supported platforms. I need the A suffix on iOS to distinguish it from the B and C variants. I chose to change CFBundleName
to “Adaptivity” to make the About window cleaner. iOS uses CFBundleDisplayName
when present, so is unaffected by this change.
I also added NSHumanReadableCopyright
to the Info.plist
to provide a copyright message.
Issues
There were a number of issues I came across when porting Adaptivity to Mac Catalyst and had to work around.
Multi-window Crashes
When running Catalyst apps from Xcode which support multiple windows, opening and closing windows often crashes. Somebody had already reported this in the developer forums. I found that running the app directly (not through Xcode) worked around the issue.
Navigation Bar Appearance
The default transparent navigation bars on iOS 13 sometimes caused strange display bugs. This seemed to be affected by switching focus between apps and whether the navigation controller was presented in a separate auxiliary window or presented modally.
I never really fully understood the problem so I forced an opaque background to be used at all times:
Sidebar Style for System Images Screen
The Human Interface Guidelines suggest using a new sidebar style in UISplitViewController
for a more Mac-like appearance. When I tried this, it occasionally worked but usually I would see some table view rows styled incorrectly. I stopped trying to use the sidebar background style.
System Materials Custom Background Image
The System Materials screen allows the user to choose a custom background image. This would fail if the App Sandbox capability for User Selected File was not set to Read/Write
(despite the app only ever reading files).
Info.plist LSApplicationCategoryType
When I tried to build a notarised version of the app for beta testing I received a warning saying that the Application Category was not configured in the Info.plist file. I added an entry with the value “Developer Tools”.
Font Entitlement Issue
As mentioned above, the Dynamic Type screen would not show anything when I tried to use UIFontPickerViewController
to allow the user to choose a custom font. The app had the entitlement to choose fonts installed from other apps and works fine on iOS 13.
After notarising the app I encountered a run time error when trying to execute it app.
As far as I can tell, it looks like Xcode is embedding an entitlements file with com.developer.user-fonts
included into both the iOS and Catalyst builds. But the provisioning profile it automatically creates for Catalyst does not include it, creating the run-time mismatch. The iOS provisioning profile does have it.
I created a post on the developer forums and submitted it as Feedback 7599197. Neither have had any response. I encountered this issue in a beta version of Xcode 11.4. I need to try it again to see if it now works.
Mac App Store Showing Prices to Users Who Paid on iOS
As I write this article, v6.0 of Adaptivity has been released today (Monday 30th March 2020). After a small propagation delay (despite scheduling an automatic release days in advance), the iOS App Store showed the new version reasonably quickly, in about an hour.
The Mac App Store, however, is still not showing the app in search results several hours later. Bizarrely, the app does appear in the “More by this developer” section at the bottom of the App Store entries for my other Mac Apps Pommie and XcLauncher. Tapping the app does show the app’s entry, but it is also showing a price instead of a download icon to users who have already purchased the iOS version. So much for universal purchases…
Despite this, some brave souls did click through and despite seeing this dialog, clicked “Buy”:
I am hoping this is a temporary issue, related to whatever propagation delay is preventing the app from appearing in search results on the Mac App Store.
EDIT: the app did appear in search on the Mac App Store after a few more hours. I’m hearing mixed reports of whether users see a price or not. Perhaps people who bought the iOS version a long time ago are seeing the price whereas more recent purchasers will see the other platform show a download icon after a relatively short period of time.
Observations
I noticed a few differences in behaviour between the iOS and Catalyst versions of the app that are worth highlighting:
- Base and elevated user interface levels in
UITraitCollection
do exist on Catalyst but have not affect on the resolved color for the dynamic colors - The resolved colors for some dynamic colors are different on Mac to iOS
- The
opaqueSeparator
dynamic color resolves to the same color asseparator
in dark appearance on Mac: R:255 G:255 B:255 (10%). In light appearance on both platforms and in dark appearance on iOS,opaqueSeparator
really is opaque. - Mac Catalyst never shows the preview view controller in context menus. That is, the
UIContextMenuContentPreviewProvider
passed to the initialiser forUIContextMenuConfiguration
is ignored. - The context menu is displayed without images and destructive items are not shown in red. Items which lead to sub-menus do show a ►image.
- Modally-presented view controllers appear in their final location without animation. They disappear with a fade out animation. There is no swipe-to-dismiss gesture since the presented view controller does not slide up or down when presented/dismissed.
Summary
On the whole I was very impressed how much functionality really did just work when I enabled Catalyst support. Even though I was deliberately avoiding making too many customisations to make this particular app more Mac-like, there was still quite a few changes required to make the app behave better and to work around some issues.
Other Articles That You Might Like
I have written comprehensive articles on How to Switch Your iOS App and Scene Delegates for Improved Testing and the View Controller Presentation Changes in iOS 13.
As an iOS developer you might be interested in my long-running series of articles which show how apps adapt to newer device sizes depending on the Xcode version they are built with:
- How iPad Apps Adapt to the New 10.2" iPad
- How iPad Apps Adapt to the New 11" and 12.9" iPads Pro
- How Apps Adapt to the Series 4 Apple Watch Screen Sizes
- How iOS Apps Adapt to the iPhone XS Max and iPhone XR Screen Sizes
- How iOS Apps Adapt to the iPhone X Screen Size
You may not have realised that there were iPad Navigation Bar and Toolbar Height Changes in iOS 12.
I have also written about External Display Support on iOS, Working with Multiple Versions of Xcode and how to Hide Sensitive Information in the iOS App Switcher Snapshot Image.
If you found any of these articles helpful then please take a look at my apps in the iOS App Store to see if there’s anything that you’d like to download (especially the paid ones 😀).
If you work with a lot of Xcode projects you might like my Mac Menu Bar utility XcLauncher. It’s like having browser bookmarks for your favorite Xcode projects, workspaces and playgrounds. There is more information on my website about XcLauncher’s features.