Swift на Windows: практические советы, проблемы и инструменты
Усі статті, обговорення, новини про Mobile — в одному місці. Підписуйтеся на телеграм-канал!
Меня зовут Александр Смарусь, и я Product Engineering Lead в Readdle. Я работаю над приложением Spark — популярным почтовым клиентом с миллионами пользователей на iOS, macOS и Android. Сейчас наша команда занимается версией для Windows, чтобы Spark стал доступен и на ПК.
В этой небольшой статье я хочу описать наш опыт, обозначить места, с которых можно начать работу со Swift на Windows, рассказать о его возможностях, вероятных проблемах, а также поделиться некоторыми инструментами.
Экспериментировать со Swift на новой платформе мы начали больше года назад. У нас уже был успешный опыт со Spark для Android, в котором базовая часть кода общая с iOS/macOS благодаря Swift. Поэтому возможность расшириться на Windows выглядела крайне привлекательно.
Мы начали изучать текущее состояние проекта, и почти сразу стало понятно, что сообщество разработки Swift уже приложило немало усилий к портированию на Windows. Компилятор был вполне живой, в стандартных библиотеках реализованы необходимые слои. И хоть на тот момент там ещё очень многое работало не совсем корректно, разработка велась очень активно. Всё серьезно. Из того, что было необходимо именно нам, бОльшая часть либо функционировала, либо выглядела не очень сильно сломанной. «Починим», — подумали мы и взялись за работу. Забегая вперёд, скажу, что некоторые сторонние зависимости на C/C++ принесли нам больше головной боли, чем сам Swift. А после проработки основных концепций работа по переносу ядра Spark на Windows стала довольно простой и рутинной.
Итак, что у нас есть сейчас:
- Инструментарий для сборки тулчейна и SDK Swift
- 9 модулей на Swift (255 739 строк кода, 2 133 исходных файла)
- 3 сторонних модуля на Swift
- 1452 теста (на базе XCTest)
- CI на Windows для отслеживания состояния тестов
- Система сборки, отчасти основанная на собственных скриптах, отчасти на CMake
Тулчейн и SDK Swift
Начать работу со Swift на Windows достаточно легко. Просто следуйте инструкциям из проекта //swift/build. Но нам требовалось нечто большее. Поскольку поддержка Windows всё ещё в разработке, постоянно возникала необходимость исследовать разнообразные проблемы в стандартных библиотеках, а иногда и в самом компиляторе. Нужен был инструмент, позволяющий собирать собственную версию Swift. И пересобирать какую-нибудь отдельно взятую часть. И так много, много раз.
Мы начали с довольно простого сборочного скрипта, основанного на Azure Pipelines compnerd`а. Со временем он превратился в гибкий инструмент, который позволяет регулярно получать свежие сборки Swift, следить за их состоянием, применять патчи, проводить эксперименты и так далее.
Сторонние модули
Модули на чистом Swift, такие как CryptoSwift и OAuthSwift, заработали практически без усилий.
CryptoSwift — это сборник криптографических алгоритмов, реализованных на Swift. Популярная библиотека определённо не нуждается в представлении. Нам понадобилось сделать лишь несколько правок импортов системных библиотек, заменить вызовы arc4random_uniform
на стандартный random(in:)
и в одном месте заменить mlock
на его Windows-аналог, VirtualLock
. Насколько мне известно, чуть позже разработчики CryptoSwift убрали оттуда arc4random_uniform
, поэтому текущая версия стала более совместимой с Windows «из коробки».
OAuthSwift. Ещё одна известная библиотека с говорящим названием. Наряду с arc4random_uniform
и исправлением импортов пришлось написать замену недостающей функции CFStringConvertEncodingToIANACharSetName
. Эта функция из библиотеки CoreFoundation, и она недоступна (не является частью SDK) на не-Darwin платформах. OAuthSwift также содержит немного UI-кода, который привязан к фреймворкам macOS/iOS. Но ядро Spark не работает с UI, поэтому тот код мы просто исключили из сборки.
Можно выделить несколько советов, которые помогут подготовить ваш код к сборке под Windows:
- Замените
arc4random_uniform
на соответствующий вызовrandom(in:)
:
let rand = arc4random_uniform(length) // До let rand = UInt32.random(in: 0..<length) // После
Достаточно очевидная рекомендация, ведь эти функции появились ещё в Swift 4.2. Должна быть веская причина, чтобы всё ещё пользоваться старым способом.
- Если в коде используются какие-либо функции из стандартной библиотеки C, добавьте импорт
ucrt
:
#if canImport(ucrt) import ucrt #endif
- В библиотеке
swift-corelibs-foundation
разделили привычный Darwin-разработчикам Foundation на три части: Foundation, FoundationNetworking и FoundationXML. Поэтому, если ваш код использует, например, сетевой функционал, вам потребуется явным образом подключитьFoundationNetworking
:
#if canImport(FoundationNetworking) import FoundationNetworking #endif
- Убедитесь, что у вас не используется функционал из CoreFoundation. Мы привыкли воспринимать эту библиотеку как должное, но на платформах, отличных от Darwin, CoreFoundation не доступен и считается не более, чем внутренней деталью реализации Foundation.
Система сборки
Сейчас Swift Package Manager почти готов к полноценному использованию на Windows, но год назад это было не так. Даже CMake, который уже долгое время остаётся самым гибким решением для Swift-проектов на этой платформе, поддерживал Swift только на базовом уровне. Но мы же инженеры, в конце концов, и наше любимое занятие — писать велосипеды. Можно сделать что-то своё. Swift — это современный компилятор, основанный на llvm. Передать ему горстку исходных файлов, указать желаемый результат (библиотека или исполняемый файл), при необходимости включить оптимизацию, подсказать пути к библиотекам, и готово!
Очень советуем посмотреть эти примеры с CMake, но для самых простых экспериментов подойдет и командная строка:
set SWIFTC=%SystemDrive%\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr\bin\swiftc.exe set SDKROOT=%SystemDrive%\Library\Developer\Platforms\Windows.platform\Developer\SDKs\Windows.sdk set SWIFTFLAGS=-sdk %SDKROOT% -I %SDKROOT%\usr\lib\swift -L %SDKROOT%\usr\lib\swift\windows -O %SWIFTC% -emit-executable %SWIFTFLAGS% HelloWorld.swift -o HelloWorld.exe
Конечно, полноценный продукт содержит множество компонентов и зависимостей. Даже примитивная система сборки должна как-то разрешать пути, обрабатывать связи между модулями, управлять ресурсами, позволять настраивать весь процесс. В итоге у нас получилась связка PowerShell-скриптов, которая со всем этим неплохо справляется. Но мы обязательно перейдем на Swift Package Manager, как только он будет готов. Это позволит унифицировать сборку на всех платформах, которые поддерживаются в Spark.
Swift и Node.js
Ещё одним испытанием было принятие решения о реализации пользовательского интерфейса. Решали довольно долго, взвешивали все за и против, проверяли некоторые перспективные идеи на практике. После всестороннего обсуждения остановились на Electron в качестве фронтенда для будущего Spark на Windows. Это означало, что нам требуется не просто собрать ядро Spark на Windows, но и сделать его загружаемым модулем для Node.js.
Аддон для Node.js на чистом Swift? Звучит интригующе, ведь в разработке таких вещей царит C и C++. Но всё оказалось на удивление просто. Разработку начали на macOS. Всё-таки пользоваться Xcode для отладки и написания кода очень удобно, а node-gyp, инструмент для сборки аддонов Node.js, может сгенерировать полноценный проект под эту IDE. Swift, в свою очередь, отлично работает с
Единственную хитрость пришлось провернуть, чтобы определить точку входа. Дело в том, что аддон должен правильно себя зарегистрировать путём определения специальной функции. Хотя написать её на Swift вполне реально, это не очень удобно. N-API предоставляет простой в использовании однострочный @_cdecl
! Судя по вот этому посту от Joe Groff, с ним связаны некоторые риски и ограничения, но у нас всего один маленький вызов. Что может пойти не так?
Шалость удалась, мы сделали аддон для Node.js... на macOS. А как же Windows? Как уже упоминалось, мы использовали node-gyp и Xcode. Очевидно, что на Windows так не получится. Там со сборкой лучше всего справляется CMake. Но node-gyp не просто собирает исходники, он делает ещё кое-что полезное: выкачивает необходимые для интеграции с Node файлы, подсказывает компилятору, где они лежат, включает кое-какие важные флаги. Вот если бы был какой-то инструмент, объединяющий в себе CMake и node-gyp... И такой есть! Cmake-js делает в точности то же, что и node-gyp, только на основе CMake. С ним всё встало на свои места. Аддон успешно собрался на Windows и запустился внутри Node.js и Electron. Немного жаль, что cmake-js не попал в поле зрения сразу, ведь тогда можно было бы не тратить время на альтернативы.
И это всё?
К глубокому сожалению, эта статья не способна вместить в себя всё интересное о Swift на Windows. Мы сфокусировались на том, что было сделано, но за рамками осталось очень много подробностей о том, как это было сделано. Планируем рассказать вам обо всём в отдельной истории.
Итого
С момента начала нашего путешествия Swift на Windows проделал огромный путь. В библиотеках исправили множество багов, компилятор перестал падать от вида необычного кода, Swift Package Manager уже в шаге от выпуска. Вы наверняка слышали, что недавно вышел Swift 5.3. Это первый релиз с официальной поддержкой Windows.
Сообщество разработки Swift приложило огромное количество усилий, чтобы воплотить в жизнь поддержку новой платформы. Отдельно хочется отметить вклад Saleem Abdulrasool, который не только пишет огромное количество кода, но и активно помогает новичкам, на практике показывает потенциал Swift и вдохновляет других на первые шаги в этой области.
Если вы думаете о расширении поддержки существующего кода на платформы кроме macOS/iOS, то Swift почти наверняка позволит это сделать. Если вы поддерживаете небольшую Swift-библиотеку, добавить поддержку Windows не составит большого труда.
18 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів