7. استفاده از پایتون در iOS

نویسندگان:

Russell Keith-Magee (2024-03)

پایتون در iOS با پایتون در پلتفرم‌های دسکتاپ متفاوت است. در یک پلتفرم دسکتاپ، پایتون معمولاً به‌عنوان یک منبع سیستمی نصب می‌شود که هر کاربر آن رایانه می‌تواند از آن استفاده کند. سپس کاربران از طریق اجرای پرونده اجرایی python و وارد کردن دستورها در یک اعلان تعاملی، یا اجرای یک اسکریپت پایتون، با پایتون تعامل می‌کنند.

در iOS، مفهوم نصب به‌عنوان یک منبع سیستمی وجود ندارد. تنها واحد توزیع نرم‌افزار، «اپلیکیشن» است. همچنین هیچ کنسولی وجود ندارد که بتوانید در آن پرونده اجرایی python را اجرا کنید یا با یک REPL پایتون تعامل داشته باشید.

در نتیجه، تنها راهی که می‌توانید از پایتون روی iOS استفاده کنید، حالت تعبیه‌شده است؛ یعنی با نوشتن یک برنامه‌ی بومی iOS، تعبیه کردن یک مفسر پایتون با استفاده از libPython و فراخواندن کد پایتون با استفاده از API تعبیه پایتون. سپس مفسر کامل پایتون، کتابخانه‌ی استاندارد و تمام کد پایتون شما به‌صورت یک بسته‌ی مستقل بسته‌بندی می‌شود که می‌توان آن را از طریق iOS App Store توزیع کرد.

اگر می‌خواهید برای نخستین بار نوشتن یک اپلیکیشن iOS با پایتون را تجربه کنید، پروژه‌هایی مانند BeeWare و Kivy تجربه‌ی کاربری بسیار در دسترس‌تری فراهم می‌کنند. این پروژه‌ها پیچیدگی‌های مرتبط با راه‌اندازی یک پروژه iOS را مدیریت می‌کنند، بنابراین تنها لازم است با خودِ کد پایتون سروکار داشته باشید.

7.1. پایتون در زمان اجرا روی iOS

7.1.1. سازگاری با نسخه‌های iOS

حداقل نسخه‌ی پشتیبانی‌شده‌ی iOS در زمان کامپایل، با استفاده از گزینه‌ی --host برای configure مشخص می‌شود. به‌طور پیش‌فرض، هنگام کامپایل برای iOS، پایتون با حداقل نسخه‌ی پشتیبانی‌شده‌ی iOS برابر با 13.0 کامپایل می‌شود. برای استفاده از حداقل نسخه‌ی iOS متفاوت، شماره‌ی نسخه را به‌عنوان بخشی از آرگومان --host ارائه کنید — برای مثال، --host=arm64-apple-ios15.4-simulator یک ساخت‌ شبیه‌ساز ARM64 با هدف استقرار (deployment target) 15.4 کامپایل می‌کند.

7.1.2. شناسایی پلتفرم

هنگام اجرا روی iOS، sys.platform مقدار ios را گزارش می‌کند. این مقدار روی iPhone یا iPad برگردانده می‌شود، صرف‌نظر از اینکه برنامه روی شبیه‌ساز اجرا می‌شود یا روی یک دستگاه فیزیکی.

اطلاعات مربوط به محیط ران‌تایم خاص (از جمله نسخه‌ی iOS، مدل دستگاه و شبیه‌ساز بودن یا نبودن دستگاه) را می‌توان با استفاده از platform.ios_ver() به دست آورد. تابع platform.system() بسته به دستگاه، iOS یا iPadOS را گزارش خواهد کرد.

os.uname() جزئیات سطح هسته را گزارش می‌دهد؛ نام Darwin را گزارش خواهد داد.

7.1.3. دسترس‌پذیری کتابخانه استاندارد

کتابخانه استاندارد پایتون در iOS دارای برخی حذف‌ها و محدودیت‌های قابل توجه است. برای جزئیات، به راهنمای دسترس‌پذیری API برای iOS مراجعه کنید.

7.1.4. ماژول‌های توسعه‌ای دودویی

یک تفاوت قابل توجه درباره‌ی iOS به‌عنوان یک پلتفرم این است که توزیع از طریق App Store الزامات سخت‌گیرانه‌ای بر بسته‌بندی یک برنامه اعمال می‌کند. یکی از این الزامات نحوه‌ی توزیع ماژول‌های توسعه‌ای دودویی را تعیین می‌کند.

App Store iOS اقتضا می‌کند که تمامی ماژول‌های دودویی در یک برنامه‌ی iOS باید کتابخانه‌های پویا باشند، در چارچوبی با فراداده‌ی مناسب قرار بگیرند و در پوشه‌ی Frameworks برنامه‌ی بسته‌بندی‌شده ذخیره شوند. در هر چارچوب تنها می‌تواند یک پرونده‌ی دودویی وجود داشته باشد و خارج از پوشه‌ی Frameworks نمی‌تواند هیچ محتوای دودویی اجرایی وجود داشته باشد.

این با رویکرد معمول پایتون برای توزیع پرونده‌های دودویی در تضاد است؛ رویکردی که اجازه می‌دهد یک ماژول توسعه‌ای دودویی از هر مکانی روی sys.path بارگذاری شود. برای اطمینان از انطباق با سیاست‌های App Store، یک پروژه‌ی iOS باید هر بسته‌ی پایتون را پس‌پردازش کند و ماژول‌های دودویی .so را به چارچوب‌های مستقل و مجزا با فراداده و امضای مناسب تبدیل کند. برای جزئیات درباره‌ی نحوه‌ی انجام این پس‌پردازش، به راهنمای افزودن پایتون به پروژه‌ی خود مراجعه کنید.

برای اینکه پایتون بتواند پرونده‌های دودویی را در مکان جدیدشان کشف کند، پرونده اصلی .so روی sys.path با یک پرونده .fwork جایگزین می‌شود. این پرونده یک پرونده متنی است که مکان پرونده دودویی چارچوب را نسبت به بسته برنامه (app bundle) در بر دارد. برای اینکه چارچوب بتواند مکان اصلی خود را دوباره بیابد، چارچوب باید یک پرونده .origin داشته باشد که مکان پرونده .fwork را نسبت به بسته برنامه در بر دارد.

برای مثال، حالت ایمپورت from foo.bar import _whiz را در نظر بگیرید، جایی که _whiz با ماژول دودویی sources/foo/bar/_whiz.abi3.so پیاده‌سازی شده است و sources مکان ثبت‌شده روی sys.path است که نسبت به باندل برنامه قرار دارد. این ماژول باید به‌صورت Frameworks/foo.bar._whiz.framework/foo.bar._whiz توزیع شود (که نام چارچوب از روی مسیر ایمپورت کامل ماژول ساخته می‌شود)، همراه با یک پرونده Info.plist در پوشه .framework که پرونده دودویی را به‌عنوان یک چارچوب شناسایی می‌کند. ماژول foo.bar._whiz در مکان اصلی با یک پرونده نشانگر sources/foo/bar/_whiz.abi3.fwork نمایش داده می‌شود که حاوی مسیر Frameworks/foo.bar._whiz/foo.bar._whiz است. چارچوب همچنین شامل Frameworks/foo.bar._whiz.framework/foo.bar._whiz.origin خواهد بود که حاوی مسیر پرونده .fwork است.

هنگام اجرا روی iOS، مفسر پایتون یک AppleFrameworkLoader نصب می‌کند که قادر به خواندن و ایمپورت کردن پرونده‌های .fwork است. پس از ایمپورت شدن، ویژگی __file__ ماژول دودویی، مکان پرونده .fwork را گزارش می‌دهد. با این حال، ModuleSpec ماژول بارگذاری‌شده، origin را به‌عنوان مکان پرونده دودویی در پوشه چارچوب گزارش می‌دهد.

7.1.5. دودویی‌های stub کامپایلر

Xcode کامپایلرهای صریحی را برای iOS در اختیار قرار نمی‌دهد؛ در عوض، از یک اسکریپت xcrun استفاده می‌کند که به مسیر کامل کامپایلر حل می‌شود (مثلاً xcrun --sdk iphoneos clang برای به‌دست‌آوردن clang مربوط به یک دستگاه iPhone). با این حال، استفاده از این اسکریپت دو مشکل ایجاد می‌کند:

  • خروجی xcrun شامل مسیرهایی است که مخصوص ماشین هستند و در نتیجه، ماژول sysconfig‌ای ایجاد می‌شود که نمی‌توان آن را بین کاربران به اشتراک گذاشت؛ و

  • این کار منجر به تعریف‌های CC/CPP/LD/AR می‌شود که شامل فاصله هستند. ابزارهای زیادی در اکوسیستم C فرض می‌کنند که می‌توانید خط فرمان را در اولین فاصله تقسیم کنید تا مسیر پرونده اجرایی کامپایلر را به دست آورید؛ اما هنگام استفاده از xcrun چنین نیست.

برای پرهیز از این مشکلات، پایتون برای این ابزارها stub‌هایی فراهم کرد. این stub‌ها اسکریپت‌های پوسته‌ای هستند که ابزارهای xcrun زیرین را در بر می‌گیرند و در پوشه‌ی bin در کنار چارچوب کامپایل‌شده iOS توزیع می‌شوند. این اسکریپت‌ها قابل جابه‌جایی هستند و همیشه به مسیرهای مناسب سیستم محلی حل می‌شوند. با گنجاندن این اسکریپت‌ها در پوشه‌ی bin که همراه یک چارچوب ارائه می‌شود، محتوای ماژول sysconfig برای کاربران نهایی به‌منظور کامپایل ماژول‌های خودشان مفید می‌شود. هنگام کامپایل ماژول‌های پایتون شخص ثالث برای iOS، باید اطمینان حاصل کنید که این دودویی‌های stub در مسیر شما قرار دارند.

7.2. نصب پایتون روی iOS

7.2.1. ابزارهایی برای ساخت برنامه‌های iOS

ساخت برای iOS نیازمند استفاده از ابزارهای Xcode اپل است. به‌شدت توصیه می‌شود که از جدیدترین نسخه پایدار Xcode استفاده کنید. این امر مستلزم استفاده از جدیدترین (یا دومین نسخه‌ی اخیر) نسخه‌ی منتشرشده macOS خواهد بود، زیرا اپل Xcode را برای نسخه‌های قدیمی‌تر macOS نگهداری نمی‌کند. ابزارهای خط فرمان Xcode برای توسعه iOS کافی نیستند؛ به یک نصب کامل Xcode نیاز دارید.

اگر می‌خواهید کد خود را روی شبیه‌ساز iOS اجرا کنید، باید یک پلتفرم شبیه‌ساز iOS (iOS Simulator Platform) نیز نصب کنید. هنگامی که Xcode را برای نخستین بار اجرا می‌کنید، از شما خواسته می‌شود که یک پلتفرم شبیه‌ساز iOS انتخاب کنید. به‌عنوان جایگزین، می‌توانید با انتخاب از زبانه Platforms در پنل تنظیمات Xcode، یک پلتفرم شبیه‌ساز iOS اضافه کنید.

7.2.2. افزودن پایتون به یک پروژه iOS

پایتون را می‌توان با استفاده از Swift یا Objective C به هر پروژه iOS افزود. مثال‌های زیر از Objective C استفاده خواهند کرد؛ اگر از Swift استفاده می‌کنید، ممکن است کتابخانه‌ای مانند PythonKit را مفید بیابید.

برای افزودن پایتون به یک پروژه Xcode در iOS:

  1. یک XCFramework پایتون بسازید یا به دست آورید. برای جزئیات نحوه‌ی ساخت یک XCFramework پایتون، به دستورالعمل‌های موجود در Apple/iOS/README.md (در توزیع کد منبع سی‌پایتون) مراجعه کنید. دست‌کم به ساخت‌ی نیاز دارید که از arm64-apple-ios پشتیبانی کند، به‌علاوه‌ی یکی از دو مورد arm64-apple-ios-simulator یا x86_64-apple-ios-simulator.

  2. XCframework را به پروژه‌ی iOS خود بکشید. در دستورالعمل‌های زیر فرض می‌کنیم که XCframework را در ریشه‌ی پروژه‌تان رها کرده‌اید؛ با این حال، می‌توانید با تنظیم مسیرها در صورت نیاز، از هر مکان دیگری که بخواهید استفاده کنید.

  3. کد اپلیکیشن خود را به‌صورت یک پوشه به پروژه Xcode خود اضافه کنید. در دستورالعمل‌های زیر فرض می‌کنیم که کد کاربر شما در پوشه‌ای با نام app در ریشه پروژه شما قرار دارد؛ در صورت نیاز می‌توانید با تنظیم مسیرها از هر مکان دیگری استفاده کنید. مطمئن شوید که این پوشه با تارگت (target) اپلیکیشن شما مرتبط است.

  4. برای انتخاب تارگت (target) برنامه، گره ریشه پروژه Xcode خود را انتخاب کنید و سپس نام تارگت را در نوار کناری‌ای که ظاهر می‌شود انتخاب کنید.

  5. در تنظیمات «General»، در بخش «Frameworks, Libraries and Embedded Content»، Python.xcframework را با انتخاب گزینه‌ی «Embed & Sign» اضافه کنید.

  6. در زبانه‌ی «تنظیمات ساخت» (Build Settings)، موارد زیر را تغییر دهید:

    • گزینه‌های ساخت

      • سندباکس‌کردن اسکریپت کاربر: خیر

      • فعال‌سازی آزمون‌پذیری: بله

    • مسیرهای جستجو

      • مسیر‌های جستجوی چارچوب (Framework Search Paths): $(PROJECT_DIR)

      • مسیرهای جستجوی سرآیند: "$(BUILT_PRODUCTS_DIR)/Python.framework/Headers"

    • Apple Clang - هشدار‌ها - همه زبان‌ها

      • include نقل‌قولی در سرآیند چارچوب: خیر

  7. یک گام ساخت (build step) اضافه کنید که کتابخانه استاندارد پایتون و وابستگی‌های دودویی پایتون خودتان را پردازش کند. در زبانه‌ی «Build Phases»، یک گام ساخت جدید از نوع «Run Script» پیش از گام «Embed Frameworks»، اما پس از گام «Copy Bundle Resources» اضافه کنید. نام این گام را «Process Python libraries» بگذارید، چک‌باکس «Based on dependency analysis» را غیرفعال کنید و محتوای اسکریپت را به صورت زیر تنظیم کنید:

    set -e
    source $PROJECT_DIR/Python.xcframework/build/build_utils.sh
    install_python Python.xcframework app
    

    اگر XCframework را در جایی غیر از ریشه پروژه‌تان قرار داده‌اید، مسیر آرگومان اول را تغییر دهید.

  8. کد Objective C را برای مقداردهی اولیه و استفاده از یک مفسر پایتون در حالت تعبیه‌شده اضافه کنید. باید اطمینان حاصل کنید که:

    • حالت UTF-8 (PyPreConfig.utf8_mode) فعال است؛

    • ورودی/خروجی استاندارد بافرشده (PyConfig.buffered_stdio) غیرفعال است؛

    • نوشتن بایت‌کد (PyConfig.write_bytecode) غیرفعال است؛

    • هندلر‌های سیگنال (PyConfig.install_signal_handlers) فعال هستند؛

    • گزارش‌گیری سیستمی (PyConfig.use_system_logger) فعال است (اختیاری، اما به‌شدت توصیه می‌شود؛ این مورد به‌طور پیش‌فرض فعال است)؛

    • PYTHONHOME برای مفسر به‌گونه‌ای پیکربندی شده است که به زیرپوشه‌ی python در باندل (bundle) برنامه‌ی شما اشاره کند؛ و

    • PYTHONPATH برای مفسر شامل موارد زیر است:

      • زیرپوشه‌ی python/lib/python3.X در بسته‌ی برنامه‌ی شما،

      • زیرپوشه‌ی python/lib/python3.X/lib-dynload از باندل برنامه‌ی شما، و

      • زیرپوشه‌ی app از بسته‌ی اپلیکیشن شما

    مکان باندل برنامه‌تان را می‌توان با استفاده از [[NSBundle mainBundle] resourcePath] تعیین کرد.

گام‌های ۷ و ۸ این دستورالعمل‌ها فرض می‌کنند که شما یک پوشه‌ی واحد از کد برنامه‌ی پایتون خالص با نام app دارید. اگر ماژول‌های دودویی شخص ثالث در برنامه‌ی خود دارید، برخی گام‌های اضافی لازم خواهد بود:

  • باید اطمینان حاصل کنید که هر پوشه‌ای که حاوی پرونده‌های دودویی شخص ثالث است، یا با هدف اپلیکیشن (app target) مرتبط باشد، یا به‌طور صریح به‌عنوان بخشی از گام ۷ کپی شده باشد. گام ۷ همچنین باید هر پرونده دودویی‌ای را که برای پلتفرم هدف یک ساخت خاص مناسب نیست حذف کند (یعنی، اگر اپلیکیشنی می‌سازید که شبیه‌ساز را هدف گرفته است، پرونده‌های دودویی مربوط به دستگاه را حذف کنید).

  • اگر از یک پوشه‌ی جداگانه برای بسته‌های شخص ثالث استفاده می‌کنید، اطمینان حاصل کنید که این پوشه به انتهای فراخوانی install_python در گام ۷ و به‌عنوان بخشی از پیکربندی PYTHONPATH در گام ۸ اضافه شده باشد.

  • اگر هر یک از پوشه‌هایی که بسته‌های شخص ثالث را در خود دارند، قرار است پرونده‌های .pth را در بر بگیرد، باید آن پوشه را به‌عنوان یک پوشه‌ی سایت (site directory) اضافه کنید (با استفاده از site.addsitedir())، نه اینکه آن را مستقیماً به PYTHONPATH یا sys.path بیفزایید.

7.2.3. آزمون یک بسته پایتون

درخت منبع سی‌پایتون شامل یک پروژه‌ی بستر آزمون (testbed) است که برای اجرای بدنه‌ی آزمون‌های سی‌پایتون روی شبیه‌ساز iOS استفاده می‌شود. این بستر آزمون همچنین می‌تواند به‌عنوان یک پروژه‌ی بستر آزمون برای اجرای بدنه‌ی آزمون‌های کتابخانه‌ی پایتون شما روی iOS استفاده شود.

پس از ساخت یا دریافت یک XCFramework برای iOS (برای جزئیات به Apple/iOS/README.md مراجعه کنید)، یک رونوشت از پروژه‌ی بستر آزمون (testbed) iOS پایتون ایجاد کنید. اگر برای ساخت XCframework از اسکریپت ساخت Apple استفاده کرده‌اید، می‌توانید اجرا کنید:

$ python cross-build/iOS/testbed clone --app <path/to/module1> --app <path/to/module2> app-testbed

یا، اگر XCframework خودتان را تهیه کرده‌اید، با اجرای:

$ python Apple/testbed clone --platform iOS --framework <path/to/Python.xcframework> --app <path/to/module1> --app <path/to/module2> app-testbed

هر پوشه‌ای که با پرچم --app مشخص شده باشد، در پروژه‌ی رونوشت‌شده‌ی testbed کپی می‌شود. testbed حاصل در پوشه‌ی app-testbed ایجاد می‌شود. در این مثال، module1 و module2 در زمان اجرا ماژول‌هایی قابل ایمپورت خواهند بود. اگر پروژه‌ی شما وابستگی‌های اضافی دارد، می‌توان آن‌ها را در پوشه‌ی app-testbed/Testbed/app_packages نصب کرد (با استفاده از pip install --target app-testbed/Testbed/app_packages یا مشابه آن).

سپس می‌توانید از پوشه app-testbed برای اجرای بدنه‌ی آزمون‌های برنامه خود استفاده کنید؛ برای مثال، اگر module1.tests نقطه ورود بدنه‌ی آزمون‌های شما بود، می‌توانستید آن را اجرا کنید:

$ python app-testbed run -- module1.tests

این کار معادل اجرای python -m module1.tests روی یک نسخه‌ی دسکتاپی از پایتون است. هر آرگومانی که پس از -- بیاید، به بستر آزمون (testbed) پاس داده می‌شود؛ گویی که آرگومان‌هایی برای python -m روی یک رایانه‌ی دسکتاپ بوده‌اند.

همچنین می‌توانید پروژه‌ی testbed را با اجرای دستور زیر در Xcode باز کنید:

$ open app-testbed/iOSTestbed.xcodeproj

این به شما امکان می‌دهد از بدنه‌ی کامل ابزارهای Xcode برای اشکال‌زدایی استفاده کنید.

آرگومان‌هایی که برای اجرای بدنه‌ی آزمون استفاده می‌شوند، به‌عنوان بخشی از طرح آزمون تعریف می‌شوند. برای تغییر طرح آزمون، گره‌ی طرح آزمون را در درخت پروژه انتخاب کنید (که باید نخستین فرزند گره‌ی ریشه باشد) و زبانه‌ی «پیکربندی‌ها» (Configurations) را انتخاب کنید. برای تغییر آرگومان‌های آزمون، مقدار «آرگومان‌های پاس‌داده‌شده هنگام راه‌اندازی» (Arguments Passed On Launch) را تغییر دهید.

طرح آزمون همچنین اجرای موازی آزمون‌ها را غیرفعال می‌کند و استفاده از پرونده‌ی Testbed.lldbinit را برای فراهم کردن پیکربندی اشکال‌زدا مشخص می‌کند. پیکربندی پیش‌فرض اشکال‌زدا، نقاط توقف خودکار روی سیگنال‌های SIGINT، SIGUSR1، SIGUSR2 و SIGXFSZ را غیرفعال می‌کند.

7.3. انطباق با App Store

تنها سازوکار برای توزیع اپلیکیشن‌ها به دستگاه‌های iOS متعلق به اشخاص ثالث، ارسال اپلیکیشن به iOS App Store است؛ اپلیکیشن‌هایی که برای توزیع ارسال می‌شوند باید از فرایند بررسی اپلیکیشن اپل عبور کنند. این فرایند شامل مجموعه‌ای از قوانین اعتبارسنجی خودکار است که بسته اپلیکیشن ارسال‌شده را برای یافتن کدهای مشکل‌دار بررسی می‌کنند. برای اطمینان از اینکه اپلیکیشن شما بتواند از این مراحل اعتبارسنجی عبور کند، باید چند گام برداشته شود.

7.3.1. کد ناسازگار در کتابخانه استاندارد

کتابخانه استاندارد پایتون شامل کدهایی است که می‌دانیم این قواعد خودکار را نقض می‌کنند. اگرچه این نقض‌ها مثبت کاذب به نظر می‌رسند، نمی‌توان قواعد بازبینی اپل را به چالش کشید؛ بنابراین، برای اینکه یک برنامه از بازبینی App Store عبور کند، لازم است کتابخانه استاندارد پایتون را تغییر دهید.

درخت منبع پایتون شامل یک پرونده وصل است که تمام کدهایی را که مشخص است در فرایند بررسی App Store مشکل ایجاد می‌کنند، حذف می‌کند. این وصل هنگام ساخت برای iOS به‌طور خودکار اعمال می‌شود.

7.3.2. مانیفست‌های حریم خصوصی

در آوریل ۲۰۲۵، اپل الزامی را برای ارائه‌ی مانیفست حریم خصوصی (Privacy Manifest) توسط برخی کتابخانه‌های شخص ثالث معرفی کرد. در نتیجه، اگر ماژول دودویی‌ای دارید که از یکی از کتابخانه‌های متأثر استفاده می‌کند، باید یک پرونده‌ی .xcprivacy برای آن کتابخانه ارائه دهید. OpenSSL یکی از کتابخانه‌های متأثر از این الزام است، اما کتابخانه‌های دیگری نیز وجود دارند.

اگر یک ماژول دودویی با نام mymodule.so تولید کنید و از اسکریپت ساخت Xcode که در گام ۷ بالا توضیح داده شد استفاده کنید، می‌توانید یک پرونده mymodule.xcprivacy را در کنار mymodule.so قرار دهید و مانیفست حریم خصوصی هنگامی که ماژول دودویی به یک چارچوب تبدیل شود، در مکان مورد نیاز نصب خواهد شد.