PEP 730: Adding iOS as a supported platform

PEP730 has now been submitted to the steering council for acceptance.

Thanks to everyone for the feedback and suggestions!


Hi everyone,
I’m sorry I’m late for the discussion, I only became aware of this PEP when it was submitted to the steering council.

First of all, thank you very much for this; you did a great job in building and submitting this proposal, and I support it completely. I am the author of a terminal-like app (a-Shell) and a local Jupyter notebook app (Carnets), so this will make my life a lot easier.

I have few comments, questions and answers to complete this discussion.

  • as said by @freakboy3742, it’s totally possible to build Python and Python modules using a dynamic framework build of libPython (that’s what I’m using). With that build, it becomes possible to use --undefined error instead of --undefined dynamic_lookup, so the issue about possible obsolescence of that flag goes away.
  • the AppStore rule about downloading code to an existing app has been clarified through the years. Downloading human-readable code (like Python) is allowed, so pip works for pure Python packages. That’s what Pythonista, Juno, Pyto, a-Shell and Carnets use, to the best of my knowledge. Downloading and loading Arm64 binaries is both forbidden and technically impossible (the app will only load binary code that has been signed with the developer certificate and placed in a specific directory that can only be accessed at install time, and never modified later).
  • iOS and OSX are very similar in their behaviour, except for the fact that fork() won’t work and that dynamic libraries won’t load. As a result, a lot of existing Python packages written for OSX work “as is” on iOS, by going through the platform == "darwin" branch. This proposal will break that, and packages will have to be rewritten to expand the test: (platform == "darwin") or (platform == "ios"). It’s not a major issue for me, just a point to mention.
  • another specificity of iOS is that the user cannot write or create directories in ~, only in ~/Documents. It’s not an issue for Python itself, but it is for many packages that store configuration files or caches in ~. We will need to edit the code for these packages, but that’s not a major issue (and definitely not an issue for Python).

Thanks for the feedback @holzschu. Great to have another “legacy” Python on iOS user voice in the mix.

I wasn’t aware of the -undefined error alternative. While it presents an interesting alternative, my inclination is to leave the PEP as it stands, recommending dynamic linking as the only supported target.

The macOS usage of -undefined is (as I understand it) entirely driven by the need to support a static linked libpython3.X.a. My impression is that nobody is happy the flag is needed - it allows for a successful compile and link of a binary module that references a symbol that won’t exist at runtime. However, on macOS, it’s needed because we can’t link against libpython because of the static libpython3.X.a use case, which needs to exist for historical reasons.

There’s no historical baggage in the iOS support case, and there’s no especially compelling use case for supporting a static libpython3.X on iOS (that I’m aware of, anyway). On that basis, we can do the “right” thing: require a dynamic libpython3.X.dylib, link binary modules against that dylib, and avoid the dangling link reference problem entirely. As noted in the PEP, this is what Windows does, so there’s precedent for this approach.

@nad @ronaldoussoren @misl6 Do you have any thoughts on this?

…pray they don’t clarify it any further :slight_smile:

It’s definitely good news that this is a supported mode of operation (at least for now); however, I don’t think it substantially impacts on this PEP as it stands. There might be an argument to add a --only-pure option to pip that only allows py3-none-any wheels (--no-binary exists, but will fall back to tarballs if they’re available), but that’s an issue for the packaging ecosystem once iOS support is possible.

This is, for me the single most important reason why sys.platform needs to return a non-“darwin” value. Yes, a lot of code will work “out of the box” by inheriting macOS behavior - but a lot of code will break by making the same assumption. There needs to be a way to differentiate iOS from macOS so that code can easily opt out of iOS-incompatible behavior - and sys.platform is how Python spells that.

Yes, this does mean there’s going to be a migration path for some code to run on iOS - but the fix is straightforward, and an analog of the same fix would be required for people migrating code that runs on Linux to also run on FreeBSD - two Unix operating systems that are substantially similar… until they aren’t.

Strictly speaking, every operating system has the same restriction, in theory. There’s no reason to believe an arbitrary Python program can write to any arbitrary file system location - even the location where the Python source itself is located.

1 Like

Hi, thanks for the answer. I realize that I should have been more explicit: none of what I wrote is in any way an objection to this PEP. Some of it is an attempt to give answers to some questions that appeared in the discussion, some of it is an attempt to point potential issues that will occur further down the line, but in packages, not in Python itself.

For the issues that will occur in packages, having this PEP will actually make our life easier: we will be able to submit proposals of changes to the package maintainers, that will make explicit the changes we had to do, and allow us to share our workload (I’m thinking about all the packages that try to write some cache or configuration file in ~/. There are really a lot of these (Jupyter, astropy, bokeh, seaborn,…)).

For the main issue, I am strongly in favour of having libpython as a dynamic library (because that’s what I’m using already…). Like you said, having a static library is only for historical reasons, and since we don’t have any historical baggage, let’s go with dylib at the start.
To clarify, --undefined error is used when you are creating another dynamic library (say, libpackage.dylib), linking against existing dylibs (say, libpython3.13.dylib). It stops the compilation if there are any symbols in libpackage.dylib that are not defined in the other dynamic libraries. It’s to make sure that your new dynamic library is not referencing some unknown symbol. I think it’s the default when linking for iOS.

I agree with your reasoning on sys.platform. I will have to rewrite some code, but that’s OK.


Hey @freakboy3742 and everyone — thank you so much for putting this together. I think it’s a great idea, which I wholeheartedly support. I’m the author of several Python-related apps for iOS, Tinkerstellar (educational app with coding tutorials) and Juno (Python and Jupyter IDE). The proposal and the discussion here sound very sensible, and I don’t have much to add at this point. I will certainly need to change a few things in my apps, but these are primarily changes that I’ve been planning anyway (e.g., switching from static to dynamic libraries).

I agree with @holzschu that the App Store rules seem to allow downloading pure Python packages (i.e., pip) — or at least they can be interpreted that way in their current form. I also like the idea of a --only-pure option in pip, as this would make my life much easier, specifically regarding the work around Juno’s package manager.

1 Like

The SC has accepted this PEP! Our full response from GitHub is included below:

Thanks for the very thorough and well-written PEP! The SC is happy to accept it.

We aren’t worried about the lack of x86_64 hardware (though perhaps this will be a push to get some old hardware donated) nor the deprecation warning for the --undefined dynamic_lookup flag – these are technical details that we can find solutions to in the future as needed.