“The process starts by checking a single box in Xcode” and then continues with compile errors. At least if your iOS app is using Carthage to manage its dependencies. Here my notes from working through those in my projects that use Carthage.

1.) Get Carthage to compile Catalyst frameworks

As of 21 October 2019, Carthage doesn’t yet support building specifically for macCatalyst nor does it support building xcframeworks (which would bundle a macCatalyst framework along with an iOS framework in the same meta framework). These features are coming up as part of Carthage’s Q4 2019 - Q1 2020 roadmap, but in the meantime you’ll have to use forks or PR that add those features.

A simple way to start is to use the fork from Glenn L. Austin which adds --platform macCatalyst option to Carthage:

  1. Clone or download the fork of the Carthage repository
  2. Compile and install locally: make installWarning: If you provide your admin password in the last step, you might overwrite your existing stable release of the carthage command. Instead you can call sudo cp -f .build/release/carthage /usr/local/bin/carthage-catalyst to add it as a new carthage-catalyst command.

Now you’re set and you can use carthage-catalyst update --platform macCatalyst to start building your dependencies for Catalyst. If that goes well, you can go straight to 3.) below, otherwise:

2.) Address Carthage issues

Firstly, if you have any Carthage compilation issues, update your depencies to the latest version as some projects have since added explicit support for Catalyst or might have since removed the use of deprecated APIs – many of which are unavailable in Catalyst.

As you’re working through these issues you’ll go through this:

  • While carthage-catalyst build --platform macCatalyst --no-use-binaries --cache-builds fails
    • If updated branch exists for that library that adds support for Catalyst
      • Update your Cartfile
      • Run carthage-catalyst update --platform macCatalyst --no-use-binaries --cache-builds
    • Else
      • Fix up the project manually in Carthage/Checkouts

Here’s the manual fixes that I had to apply to different projects:

Carthage raises Fatal error: No SDKs found for scheme X

If this happens, do the following:

  1. In the project configuration, check if “Base SDK” is set to “iOS”, if it’s not, set it to that.
  2. If it still fails, add “macosx” to “Supported Platforms”
  3. If it still fails, check if “Valid Architectures” overrides the default and if so, remove that override.

That should then make Carthage itself happy, though xcodebuild might not:

Compile issues

  • API not available: Your iOS library might be using deprecated classes that might still be available but have not been added to Catalyst. UIWebView in particular could be an offender here. Remove those uses.
  • Linked framework issue. If the library itself has dependencies that are managed using Carthage, you also need to update “Framework Search Paths”. In particular, you need to add new special cases for “Any macOS SDK” adding $(PROJECT_DIR)/Carthage/Build/macCatalyst and making sure it doesn’t use the Build/iOS directory.

Libraries that didn’t work out-of-the-box:

  • Kingfisher: Set “Base SDK” to “iOS”
  • KVNProgress: 1.) Add “macosx” to “Supported Platforms” + Delete overwrite of “Valid Architectures”; 2.) Delete as it depends on GLKit which Mac Catalyst doesn’t seem to have.
  • RxSwift: Set “Base SDK” to “iOS”
  • RxDataSources: Add “macosx” to “Supported Platforms” + fix up “Framework Search Paths”
  • UserVoice: Removed as it’s deprecated and relies heavily on UIWebView

3.) Use it in your app

To use the Catalyst frameworks in your iOS app, you’ll need to adjust your build settings as follows – the first step isn’t necessary but makes the other steps easier:

1) Add a “User-Defined Setting” and call it CARTHAGE_SUBFOLDER. It should default to “iOS”, and be overwritten as “macCatalyst” for “Any macOS SDK”:

Add a User-Defined Setting
Add a User-Defined Setting

2) Change your “Framework Search Paths” to use $(PROJECT_DIR)/Carthage/Build/$(CARTHAGE_SUBFOLDER) rather than hard-coding the “iOS” subfolder, so that your builds are using the correct frameworks.

3) Adjust your carthage copy-frameworks build phase, to use different input and output file lists depending on whether you build for iOS or Mac Catalyst. You can do this by using $(SRCROOT)/carthage_input_$(CARTHAGE_SUBFOLDER).xcfilelist for the Input File Lists and $(SRCROOT)/carthage_output_$(CARTHAGE_SUBFOLDER).xcfilelist for the Output File Lists. Your carthage_input_iOS.xcfilelist and ``carthage_output_iOS.xcfilelist should list all your Carthage frameworks and their target paths as normal, however your the carthage_input_macCatalyst.xcfilelist and carthage_output_macCatalyst.xcfilelist` should be empty. Instead, you’ll embed the Catalyst frameworks as follows:

4) Add a new “Copy Files Phase” step, and add the Carthage frameworks there, setting them to “macOS” and ticking the “Code Sign On Copy” option.

Now things should be working. If they don’t, it’s possibly Xcode’s fault. Try nuking your “Derived Data” folders might help. If you’re using fastlane a quick fastlane action clear_derived_data might do the trick.

4.) Archive and distribute

With the above steps doing an Archive build should already work.

If you get an error like MyFramework.framework: bundle format is ambiguous (could be app or framework), then you likely skipped step 3 above. I always got this when trying to use the carthage copy-frameworks build phase, even when pointing these at $(SRCROOT)/Carthage/Build/macCatalyst/MyFramework. Follow my step 3 and 4 above to address this, as the Carthage README states this step is not necessary for macOS. However, I haven’t yet confirm if this works for App Store builds as I’ve only distributed manually by signing using my Developer ID.

Next, if you try to distribute or notarize your achive, you might get an error if you don’t yet have a distribution certificate of type “Developer ID Application”. Create one on the Apple Developer portal.

In Xcode 12, notarization might fail with a not-very-descriptive Code signing "A" failed error. If this happens press the button to view the logs, hunt the logs which framework failed. For me Eureka and RxSwift made issues as they overwrite the Enable Bitcode setting. Modify their build settings, removing the overwrites, delete the cached Carthage builds, re-build them, re-archive your app, re-submit, and and repeat until it works.

Next steps

I hope all of this will get easier once Carthage supports XCFrameworks. Though, as of December 2020, that’s still not available and the support in PRs supports XCFrameworks but still not yet a --platform macCatalyst setting…

Anyway, now that your app is running on macOS, off to refine it and make it feel more like a Mac app than an iOS app in a Mac window:


Changelog:

  • 8 Dec ‘20: Added instructions for Xcode 12.
  • 4 March ‘20: RxSwift 5.1 is now Carthage compatible.
  • 10 November ‘19: Updated 3.) with specific instructions of a more robust case.
  • 9 November ‘19: Added section ‘4.) Archive and distribute’. Removed note on KeychainAccess and OAuthSwift as those are Catalyst compatible as of the latest release.