I figured something like this might be happening. If Pythonista etc are able to slide under the review radar, then that’s great. However, I wouldn’t want to bet any part of the Python-on-iOS ecosystem on that window being open for an arbitrary app. Essentially we should be assuming on-device pip isn’t possible, and if someone is able to build an app that gets through review that does use pip, then they win a prize
The primary reason for not using a universal wheel format is that the universal wheels experience on macOS has been a bit of a nightmare
A lot of the scientific community has given up on the universal2 format on macOS, and only produces architecture specific wheels. NumPy and Pandas, for instance, only produce x86_64 and arm64 wheels. Pillow is in the same category. This is because the underlying libraries that they’re integrating with don’t play nice with a single-pass, multi-architecture compilation. C header and source files for non-macOS platforms generally aren’t set up to deal with multi-architecture compilation. And if you publish a wheel that has development headers (as NumPy does), those headers need to be “universal” compatible, which the vast majority of non-macOS code isn’t set up to do.
On top of that, there’s also the question of what architectures would be included in a “universal” wheel format for iOS. If iOS support had existed 10 years ago, and we used a univeral iOS wheel format, we would have required a different universal wheel formats for:
- armv6 + armv7 on device, plus x86 on simulator
- armv6 + armv7 + armv7s on device, plus x86 on simulator
- armv6 + armv7 + armv7s + arm64 on device, plus x86 and x86_64 on simulator
- armv7 + armv7s + arm64 on device, plus x86 + x86_64 on simulator
- arm64 on device, plus x86_64 + arm64 on simulator
There might even be a couple of other combinations - I’d need to check the exact timelines on CPU release dates and device EOLs. (5) would be the current state for a “universal” iOS wheel; however, it’s reasonable to expect that in a year or two, we’ll only need arm64 on device and arm64 on simulator, so that’s another universal format; and if there emerges an actual need for an arm64e on device, that’s another format again.
My overall point - “universal” on iOS isn’t a stable concept, and it would require considerable ongoing maintenance from a standards perspective.
The alternative - single architecture wheels - allows the producer of the wheels to determine what platforms they are able to support, and the consumer of the wheels can decide which architectures they want to support in their apps. Consider the following use cases:
- I don’t have an x86_64 development machine. Therefore, I don’t have to download the x86_64 simulator wheel. If the wheel is large, this could be a major disk space saving.
- Apple announces a new CPU architecture for devices. A library maintainer can publish a single new wheel and make deployment onto this archictecture possible; all existing wheels continue to work.
- A library maintainer or app developer decides that they need to drop support for one architecture because of some techical detail of the platform, or availability of testing hardware. They can do so by publishing wheels for all platforms except for the one they have deprecated.
- A library maintainer or app developer decides that they can’t adopt a new architecture, for the same reasons as (3). Again, they don’t publish wheels for the new architecture.
It’s also entirely likely, based on how the CPython configure script works, that a new iOS CPU architecture wouldn’t require any changes to CPython, other than a modification to PEP11 to add it to the Tier3 list, and a modification to PyPI to accept the new architecture for uploads.
FWIW: BeeWare did use a “universal” wheel format originally; we decided to move away from that because of the experience of managing those wheels, plus conversations with people in the scientific Python community.
I agree that this makes the end user’s life marginally more complicated, because they need to do multiple pip
passes to get all the wheels they need for all the architectures they need to support; however, this is something that be easily managed with tooling. Briefcase does this presently; and the end user’s experience isn’t any more complicated than “add the library you want to a list of dependencies”. The code to manage multiple architectures is not especially complicated; It might even be something that can be upstreamed into pip (although there’s a bigger set of changes needed in pip to improve the experience of crossenv installation).
It’s also worth noting that if a library maintainer does want to publish a single platform wheel, they can - the wheel tagging format allows for multiple platform tags (e.g., how Pandas publishes manylinux wheels). So - if someone wants to publish universal/fat wheels, they can - there just won’t be a “universal2023” shorthand for encompassing multiple tags, and the tooling for creating universal wheels will be up to the person making the wheels… but that was going to be true regardless.
From the perspective of the PEP: whatever the final outcome, this topic definitely needs to be mentioned in the “rejected ideas” section, so I’ll add that to the list of v2 edits as soon as a consensus is reached on this topic.
In terms of the specific wheel structure - the iOS wheels we’ve been using (e.g., Files :: Anaconda.org) are structurally almost identical to what is distributed for macOS, Windows etc - that is, the wheel file contains dylib files “in-situ”, not Frameworks in a special location. We then use an Xcode build step to post-process the dylibs out of the site-packages
folder where the wheel is installed, and into the Frameworks folder. These libraries need to be code signed anyway, so a post-processing step is inevitable; and there’s the added benefit that we don’t need to patch every PEP518 build system tool to teach them how to format iOS-compatible wheels.