This guide will show you how to create a universal app in Pyinstaller, and how to test them properly before distributing the application.
What is universal application?
A universal application in macOS is designed to run natively on both Intel-based Macs (x86_64) and Apple silicon Macs (arm64). These apps contain executable code for both architectures, allowing them to perform efficiently without relying on translation tools like Rosetta. Developers create universal applications by building universal binaries, which include versions of the app optimized for each architecture.
To create a universal app, we need to use the option —target-architecture “universal2” in Pyinstaller. Also make sure you have installed the universal version of Python3.
Note: If you use console-based option, then you get a unix executable (no-icons). If you use window-based option, you get a .app file
In auto-py-to-exe, you can find this option:
Universal Wheels
During conversion, you may encounter errors with certain packages that are architecture-specific, meaning they are installed as either x86_64 or arm64 versions. As a result, creating a universal2 binary is not feasible for these packages.
To resolve this issue, you need to download the non-binary universal package. For example, you can check for the availability of universal versions in the case of the Pillow module here: Pillow module files. However, the Pillow module does not offer a universal wheel. Although it provides a non-binary option, it has compatibility issues.
You can create a universal binary for this module using the delocate package, which combines two wheels (x86_64 and arm64) into a single universal binary.
Since this process can be challenging for beginners, we've developed a simple tool to help create and install universal wheels: Universal2Maker.
You can use this tool to install the required universal wheel and then attempt to convert the application again using PyInstaller.
Testing in both Arch
After creating the universal app, you can test the universal file to ensure its compatibility. It will likely open on your native system, but how can you verify it works for other architectures? No need to use a separate machine—you can easily test it using the arch command.
Open the terminal.
Navigate to the program's directory using the cd command.
Test the app for x86_64 (Intel environments):
arch -x86_64 ./my_app
Test the app for arm64 (Apple Silicon/M-series chips):
arch -arm64 ./my_app
If both tests succeed and the app opens, congratulations—you’re ready to distribute your application to users.
However, if it fails, debugging will be necessary. In most cases, issues stem from non-Python dependencies. It’s recommended to search online for solutions to the specific errors encountered.e to debug the errors, most probably the issues are related to non-python packages, so it is reccmomended to search the error solutions on the internet.
Distribution
Before distributing your app, it’s advisable to test it on older macOS versions. Many users face difficulties opening apps on outdated systems, which can lead to frustration. Testing ensures compatibility and helps identify potential issues in advance.
Additionally, if you have an Apple Developer ID, you can sign the executable. This process allows the app to run on other macOS devices without triggering security warnings, enhancing user experience and trustworthiness.
Otherwise, just provide them with these steps:
Double click on the executable, it will show a permission error. (Don't delete the file)
Now go to your System settings > Privacy & Security section.
Scroll down, you will find a message saying "application was blocked...", simply Allow it by clicking "Open Anyway"
These files on mac don't generally gets flagged as viruses, so you are good to go!
You can also create a DMG/PKG installer for the application...