Интеграция библиотеки на Swift в UE4

от автора

Недавно возникла задача интеграции кода, который был написан только на Swift в проект на UE4. При этом вариант "давайте быстренько перепишем все на Objective C" не рассматривался. Готового решения нигде не нашел, пришлось, как это часто бывает, копать и вширь и вглубь. Задача была успешно решена и чтобы помочь кому-то еще, решил написать туториал.

Обычно, если нужно интегрировать какой-либо специфичный для iOS/macOS платформы код, используется сборка библиотеки в виде Framework, который должен быть статический. Сам же код пишется на Objective C. Далее, зависимость прописывается в скрипте сборки проекта MyProject.Build.cs, например, так:

if (Target.Platform == UnrealTargetPlatform.IOS) {     string ExtraPath = Path.GetFullPath(Path.Combine(ModuleDirectory, "../ThirdParty"));     PublicAdditionalFrameworks.Add(         new Framework(             "MyFramework",             ExtraPath + "/" + "MyFramework.framework.zip"         )     ); }

Для Swift можно попробовать пойти этим же путем, но возникнет проблема — зависимость от некоторых библиотек языка. Предупреждение при линковке будет выглядеть так:

ld: warning: Could not find auto-linked library 'swiftFoundation' ld: warning: Could not find auto-linked library 'swiftDarwin' ld: warning: Could not find auto-linked library 'swiftCoreFoundation' ld: warning: Could not find auto-linked library 'swiftCore' ld: warning: Could not find auto-linked library 'swiftCoreGraphics' ld: warning: Could not find auto-linked library 'swiftObjectiveC' ld: warning: Could not find auto-linked library 'swiftDispatch' ld: warning: Could not find auto-linked library 'swiftSwiftOnoneSupport'

После чего последуют стандартные ошибки о том что не найдены некоторые символы в объектных файлах. Можно попробовать найти где находится каждая библиотека, упомянутая выше, и указать путь к ней через ключ L, но я решил перейти на динамический тип Framework, который сам подгрузит эти библиотеки.

Сборка проект пройдет нормально, однако при запуске приложение будет падать со знакомой iOS разработчикам ошибкой:

dyld: Library not loaded: @rpath/MyFramework.framework/MyFramework   Referenced from: /private/var/mobile/Containers/Bundle/Application/.../MyApp.app/MyApp   Reason: image not found

Это связано с тем, что UE4 не умеет работать с динамическим типом Framework, и не копирует его при создании пакета приложения. Это можно было бы решить путем изменения кода IOS Toolchain и сборкой своей версии движка, но хотелось всего этого избежать и на помощь пришел IOSPlugin.

Это расширение обычно используется для модификации plist файла приложения, например, добавления описания для прав доступа:

<iosPListUpdates>     <addElements tag="dict" once="true">         <key>NSCameraUsageDescription</key>         <string>The camera is used for live preview.</string>     </addElements> </iosPListUpdates>

Нам же пригодится элемент copyDir. Он позволяет задать путь к директории, которую мы хотим скопировать, и место назначения:

<init>     <log text="Copy MyFramwork..."/>     <copyDir src="/.../MyFramework.framework" dst="$S(BuildDir)/Frameworks/MyFramework.framework" /> </init>

Важно, чтобы команда копирования располагалась в блоке init. После этого приложение начинает работать.

Итоговый файл MyProject.Build.cs будет выглядеть так:

using UnrealBuildTool; using System.IO;  public class MyProject : ModuleRules {     public MyProject(ReadOnlyTargetRules Target) : base(Target)     {         PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;          PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });          if (Target.Platform == UnrealTargetPlatform.IOS)         {             string ExtraPath = Path.GetFullPath(Path.Combine(ModuleDirectory, "../ThirdParty"));             PublicAdditionalFrameworks.Add(                 new Framework(                     "MyFramework",                     ExtraPath + "/" + "MyFramework.framework.zip"                 )             );              string PluginPath = Utils.MakePathRelativeTo(ModuleDirectory, Target.RelativeEnginePath);             AdditionalPropertiesForReceipt.Add("IOSPlugin", Path.Combine(PluginPath, "MyProject_IOS_UPL.xml"));         }     } }

А файл MyProject_IOS_UPL.xml так:

<?xml version="1.0" encoding="utf-8"?> <root>     <init>         <log text="Copy MyFramework..."/>         <copyDir src="/.../MyFramework.framework" dst="$S(BuildDir)/Frameworks/MyFramework.framework" />     </init> </root>

Это решение в некотором роде обходной путь, пока не появится нормальной поддержки динамического Framework в UE4, но меня оно полностью устроило.

ссылка на оригинал статьи https://habr.com/ru/post/522746/