将Python解释器嵌入MACOS应用程序(和iOS应用程序)中,并成功发布到App Store。
#macos #ios #swift #pythonkit

斗争是真实的

当我开始研究这个主题时,我很快就意识到大多数指南,教程和stackoverflow答案只有部分信息或直接提供不良建议,这会阻止您在Apple的App Store上发布应用程序。

我不得不拼命所有这些,并最终能够将Python解释器嵌入MacOS应用程序,完全签名并具有发布到App Store的能力。

因此,这里是关于如何将Python解释器嵌入您的应用程序的快速简单指南。当您有明确的步骤时,这真的很简单。

我们为什么还需要这个?

作为本地Apple Eco System开发人员(如今我主要是iOS),我相信Swift是为iOS和MACOS开发的唯一方法。
但是,有时您的应用程序需要您不想重塑的大事,并且仅在Python中可用。那为什么不使用呢?

python有很多工具和开源代码,您可以直接使用框,可以通过惊人的功能赋予您的应用程序功能,并且您可以轻松地从Swift调用Python代码。

但要小心,通过将python解释器嵌入您的二进制文件中,您将其放大约100MB。因此,我不建议您使用Swift或找到维护良好的SPM的简单内容。

您可以在App Store上发布此类MacOS应用吗?

是。您可以嵌入Python并发布到MacOS App Store。

虽然MacOS已经随附Python,但您不想使用它。

  1. 您不能依靠系统上安装的python版本。
  2. 这将需要您删除MacOS应用程序的沙盒和Disable Library Validation。一旦这样做,您就无法将应用程序提交给App Store,甚至可能在App Store外面的公证化问题:https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution

另一方面,通过嵌入Python解释器,您也允许应用程序的Python部分签名。
这允许硬化的运行时间保持完整:

  • 无需删除沙箱 - 您需要它才能将MacOS应用程序提交到App Store。
  • 不需要Disable Library Validation

那iOS呢?

是。您可以嵌入Python并发布到iOS App Store。只要看pyto:https://pyto.app
iOS App Store对此没有限制。所有这些都签名为一个应用程序。
将Python嵌入iOS的过程与MACOS非常相似,但是它可能需要在以后的文章中涵盖的更多步骤。

逐步嵌入MacOS应用中的Python解释器

  1. 添加pythonkit spm:
    https://github.com/pvieito/PythonKit.

  2. 下载所需Python版本的框架(用于MacOS平台):
    https://github.com/beeware/Python-Apple-support

  3. tag.gz存档中提取python-stdlibPython.xcframework

  4. 将它们复制到MacOS应用程序的根,最好通过Xcode。

  5. Xcode General->框架:
    5.1。应该已经在那里:
    -Python.xcframework被设置为Do Not Embed
    -PythonKit
    5.2。添加其他必需框架:
    -SystemConfiguration.framework设置为Do Not Embed

  6. xcode Build Phases
    6.1。验证Copy Bundle Resources包含python-stdlib
    6.2。添加bash脚本以在python-stdlib/lib-dynload/中签名.so二进制文件:
    重要说明:.so二进制文件必须与您的TeamID签名,如果您需要使用Sign and Run Locally,它将被签名为Ad-Hoc,并且您需要kude0。

set -e
echo "Signing as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)"
find "$CODESIGNING_FOLDER_PATH/Contents/Resources/python-stdlib/lib-dynload" -name "*.so" -exec /usr/bin/codesign --force --sign "$EXPANDED_CODE_SIGN_IDENTITY" -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der {} \;
  1. 创建一个名为module.modulemap的文件,并具有以下代码:
module Python {
    umbrella header "Python.h"
    export *
    link "Python"
}
  1. module.modulemap文件放在Python.xcframework/macos-arm64_x86_64/Headers/中。
    这将使我们可以做import Python

  2. 在运行时至INIT PYTHON,尽早:

import Python

guard let stdLibPath = Bundle.main.path(forResource: "python-stdlib", ofType: nil) else { return }
guard let libDynloadPath = Bundle.main.path(forResource: "python-stdlib/lib-dynload", ofType: nil) else { return }
setenv("PYTHONHOME", stdLibPath, 1)
setenv("PYTHONPATH", "\(stdLibPath):\(libDynloadPath)", 1)
Py_Initialize()
// we now have a Python interpreter ready to be used
  1. 运行测试代码:
import PythonKit

let sys = Python.import("sys")
print("Python Version: \(sys.version_info.major).\(sys.version_info.minor)")
print("Python Encoding: \(sys.getdefaultencoding().upper())")
print("Python Path: \(sys.path)")

_ = Python.import("math") // verifies `lib-dynload` is found and signed successfully
  1. 我们从事业务。现在,我们可以添加我们想要的任何Python代码。 要集成第三方Python代码和依赖项,您将需要确保PYTHONPATH包含其路径;然后您可以做Python.import(" <SOME LIB> ")。 有时,Python代码可能太复杂了,无法与Swift打电话给Pythonkit,因此我的建议是编写一个小的Python脚本,然后从Swift中调用该脚本的方法。

祝你好运。

^(;,;)^

更新:
我已经提交了上述步骤,以成为Beeware Python-Apple-Support的官方用法指南。如果您在上述步骤上遇到麻烦,则使用指南可能包含其他信息:https://github.com/beeware/Python-Apple-support/blob/main/USAGE.md