Skip to content

Compile Swift sources of extensions on macOS and iOS - #1135

Draft
ekharkunov wants to merge 5 commits into
devfrom
feature/swift-extension-sources
Draft

ekharkunov wants to merge 5 commits into
devfrom
feature/swift-extension-sources

Conversation

@ekharkunov

@ekharkunov ekharkunov commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Native extensions can now include Swift source files for macOS and iOS (devices and simulator). Put the .swift files into the src folder of the extension and call them from the Objective-C++ code through the generated <Module>-Swift.h header. The Swift code can import the Swift packages declared in SwiftPackages.json, and an optional <Module>-Bridging-Header.h in src gives it access to Objective-C and C declarations.

Compiler options are set with the new swiftFlags key of the extension manifest, and the extra header and module folders with additionalSwiftIncludePaths. Only flags that the SDK allows are accepted, and the paths must stay inside the extension folder. The new additionalFrameworkSearchPaths key adds framework folders and is not tied to Swift: it applies to the Swift compile, to the compile of the extension's C++ and Objective-C++ sources, and to the link.

The Swift runtime is part of iOS 12.2 and macOS 10.14.4 and later. Parts of it, such as Swift concurrency before iOS 15 and macOS 12, are not, and the application then needs extra dynamic libraries. The build server now finds them and adds them to the engine archive, as Xcode does, so nothing has to be configured for it. A build whose minimum OS version is older than the runtime of the OS fails with a clear error.

name: MyExtension
platforms:
    ios:
        context:
            swiftFlags: ["-strict-concurrency=complete"]
            additionalSwiftIncludePaths: ["vendor/headers"]
            additionalFrameworkSearchPaths: ["vendor/frameworks"]

Technical changes

  • All Swift files of an extension are compiled by one swiftc -wmo -parse-as-library -c call per extension, which emits the object file, the .swiftmodule and the -Swift.h header. It runs before the C/C++/Objective-C sources, which import the header. The call goes through the existing process sandbox launcher.
  • The module name is the extension name with every character outside [A-Za-z0-9_] replaced by _. The header and module are written to a per-extension folder in the build directory.
  • .swift files are collected only from src on Apple platforms. Swift files in commonsrc or pluginsrc fail the build, because the editor plugin build reads those folders. sourceRe is not changed.
  • PlatformConfig gets the platform keys swiftSourceRe, allowedSwiftFlags and compileSwiftModuleCmd. An SDK without them fails a build that has Swift sources with an explicit "not supported by this Defold SDK version" error, and ignores swiftFlags.
  • ExtensionManifestValidator validates swiftFlags against allowedSwiftFlags; everything else, including -I and -F, is rejected. additionalSwiftIncludePaths and additionalFrameworkSearchPaths are lists of paths that are checked and resolved with SandboxedPath, then passed to the compiler as -I and -F relative to the job directory. additionalSwiftIncludePaths is ignored on an SDK without Swift support. additionalFrameworkSearchPaths is checked on every SDK and is also passed as -F to the existing compileCmd and linkCmd templates through ext.frameworkPaths, so no SDK change is needed for it. It is not applied to the sources of pods.
  • defines from the manifest context are passed to Swift as bare -D names, as Swift does not accept values.
  • Include and framework paths of resolved Swift packages and pods are passed to swiftc, so Swift code can import a package module and the bridging header can import the Objective-C headers of a package. Importing a module requires the Xcode that builds the packages to match the toolchain of the platform SDK.
  • When an extension has Swift code and the platform defines listSwiftRuntimeLibsCmd, linkEngine runs it after the link: swift-stdlib-tool --print lists the Swift libraries that the engine references. The result is filtered by the table in SDKSettings.json of the platform SDK (SupportedTargets.<target>.SwiftCompatibilityLibraryNotNeededStartingIn): a library is added only if it is in the table and its threshold is above the minimum OS version. libswiftCore and the other libraries that the OS always provides are never added. The libraries are copied from the toolchain to the build result, next to the engine, and are collected with the other dynamic libraries.
  • SwiftRuntimeLibraries holds the table, the parsing of the tool output and the check of the minimum version against SwiftOSRuntimeMinimumDeploymentTarget. Below it the build fails with "Swift sources are not supported for this minimum OS version".
  • JobFiles.copyFile gets an overload with a trusted source root, as the libraries come from the platform SDK and not from the job directory. The destination is still checked against the job directory.
  • The platform context variables swiftToolchain, swiftSdkSettings and swiftSdkTarget select the toolchain, the settings file and the target. An SDK without them or without listSwiftRuntimeLibsCmd skips the step.
  • Related: Add Extender templates for compiling extension Swift sources defold#13647, Add manual for Swift sources in native extensions doc#697.

An extension can now keep .swift files in its src folder next to the
Objective-C++ files. All of them are compiled as one module with a single
swiftc call that emits the object file, the .swiftmodule and the
<Module>-Swift.h header, which the .mm sources import. The module is named
after the extension, and an optional <Module>-Bridging-Header.h in src is
imported into it.

The ext.manifest gets a swiftFlags context key. It is validated against the
allowedSwiftFlags patterns of the platform in build.yml, and the -I and -F
paths are resolved inside the extension folder. Defines reach the Swift code
as conditional compilation names. Swift files outside of src are refused,
and an SDK without the new build.yml keys answers with an explicit error.

The Swift code can use the Swift packages and the Objective-C headers that
SwiftPM resolved for the build. The server has to be deployed before the SDK
that carries the new build.yml keys, as older servers refuse to load them.
@github-actions

github-actions Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Summary - Extender code coverage report

Summary
Generated on: 11/04/2026 - 14:39:56
Coverage date: 11/04/2026 - 14:39:23
Parser: JaCoCo
Assemblies: 16
Classes: 125
Files: 125
Line coverage: 53.2% (5416 of 10176)
Covered lines: 5416
Uncovered lines: 4760
Coverable lines: 10176
Total lines: 21495
Branch coverage: 54.4% (2490 of 4572)
Covered branches: 2490
Total branches: 4572
Method coverage: Feature is only available for sponsors
Tag: 333_41848543197

Coverage

com/defold/extender - 45.8%
Name Line Branch
com/defold/extender 45.8% 52.5%
com/defold/extender/AppManifestConfiguration 100%
com/defold/extender/AppManifestPlatformConfig 100%
com/defold/extender/AsyncBuilder 11.1% 23.6%
com/defold/extender/BuilderConstants 80%
com/defold/extender/BuildProgressController 93.1% 91.6%
com/defold/extender/Configuration 100%
com/defold/extender/Extender 13.8% 13.1%
com/defold/extender/ExtenderApplication 0%
com/defold/extender/ExtenderBuildState 63.1% 57.1%
com/defold/extender/ExtenderConst 0%
com/defold/extender/ExtenderController 17.4% 19.7%
com/defold/extender/ExtenderException 100%
com/defold/extender/ExtenderUtil 74% 69.7%
com/defold/extender/ExtenderYamlSafeConstructor 100%
com/defold/extender/ExtensionManifestValidator 96.8% 91.9%
com/defold/extender/ManifestConfiguration 100%
com/defold/extender/ManifestPlatformConfig 100%
com/defold/extender/PlatformConfig 100%
com/defold/extender/PlatformNotSupportedException 0%
com/defold/extender/R8Builder 88% 77.7%
com/defold/extender/R8Configuration 93.5% 50%
com/defold/extender/R8ResourceArchive 98.3% 83.3%
com/defold/extender/R8RulePolicy 76.2% 70.4%
com/defold/extender/RequestErrorAdvice 89.6% 75%
com/defold/extender/SandboxedPath 97.7% 86.6%
com/defold/extender/SwiftRuntimeLibraries 100% 88.4%
com/defold/extender/TemplateExecutor 39.1% 50%
com/defold/extender/Timer 100%
com/defold/extender/TreePrinter 0% 0%
com/defold/extender/Version 0%
com/defold/extender/VersionNotSupportedException 0%
com/defold/extender/WebSecurityConfig 54.5% 36.8%
com/defold/extender/WhitelistConfig 100%
com/defold/extender/ZipUtils 85.2% 73%
com/defold/extender/builders - 60.8%
Name Line Branch
com/defold/extender/builders 60.8% 63.8%
com/defold/extender/builders/CSharpBuilder 60.8% 63.8%
com/defold/extender/cache - 32.8%
Name Line Branch
com/defold/extender/cache 32.8% 15.6%
com/defold/extender/cache/CacheEntry 64.7% 75%
com/defold/extender/cache/CacheKeyGenerator 81.8% 100%
com/defold/extender/cache/DataCache
com/defold/extender/cache/DataCacheFactory 5% 0%
com/defold/extender/cache/DummyDataCache 80%
com/defold/extender/cache/GCPDataCache 0% 0%
com/defold/extender/cache/LocalDiskDataCache 66.6% 0%
com/defold/extender/cache/info - 100%
Name Line Branch
com/defold/extender/cache/info 100% ****
com/defold/extender/cache/info/CacheInfoFileParser 100%
com/defold/extender/cache/info/CacheInfoFileWriter 100%
com/defold/extender/cache/info/CacheInfoWrapper 100%
com/defold/extender/jetty - 82.6%
Name Line Branch
com/defold/extender/jetty 82.6% 52.9%
com/defold/extender/jetty/ExtenderJettyErrorHandler 75.8% 36.3%
com/defold/extender/jetty/JettyRequestEventsHandler 86.6% 83.3%
com/defold/extender/jetty/JettyServerConfiguration 100%
com/defold/extender/log - 63.7%
Name Line Branch
com/defold/extender/log 63.7% 73.3%
com/defold/extender/log/ExtenderLogEnhancer 75% 62.5%
com/defold/extender/log/ExtenderLogEnhancerConfiguration 5.8% 16.6%
com/defold/extender/log/LogSanitizer 100% 100%
com/defold/extender/log/LogTruncator 100% 100%
com/defold/extender/log/Markers 80%
com/defold/extender/metrics - 37.5%
Name Line Branch
com/defold/extender/metrics 37.5% ****
com/defold/extender/metrics/MetricsWriter 37.5%
com/defold/extender/process - 88%
Name Line Branch
com/defold/extender/process 88% 81.1%
com/defold/extender/process/CommandLineTokenizer 97.7% 92.6%
com/defold/extender/process/CommandTimeoutException 100%
com/defold/extender/process/DarwinSandboxPaths 42.8%
com/defold/extender/process/JobFiles 88.3% 50%
com/defold/extender/process/ProcessExecutor 79.4% 70.6%
com/defold/extender/process/ProcessHardening 75% 50%
com/defold/extender/process/ProcessLaunchException 100%
com/defold/extender/process/ProcessSandbox 95.6% 83.7%
com/defold/extender/process/ProcessSandboxInitializer 0% 0%
com/defold/extender/process/ProcessUtils 55% 66.6%
com/defold/extender/process/SandboxConfiguration 99.1% 75%
com/defold/extender/process/SandboxLauncherProbe 86% 90%
com/defold/extender/process/SandboxPolicy 100% 66.6%
com/defold/extender/process/SeatbeltProfile 100% 94.8%
com/defold/extender/progress - 90.6%
Name Line Branch
com/defold/extender/progress 90.6% 81.6%
com/defold/extender/progress/BuildProgressEvent 100%
com/defold/extender/progress/BuildProgressService 90.2% 80.5%
com/defold/extender/progress/BuildStage 100% 100%
com/defold/extender/progress/ProgressReporter 40%
com/defold/extender/remote - 88.9%
Name Line Branch
com/defold/extender/remote 88.9% 67.2%
com/defold/extender/remote/RemoteBuildException 100%
com/defold/extender/remote/RemoteEngineBuilder 93.2% 71.8%
com/defold/extender/remote/RemoteHostConfiguration 100%
com/defold/extender/remote/RemoteInstanceConfig 100%
com/defold/extender/remote/RemoteProgressRelay 76.7% 60.8%
com/defold/extender/services - 54.5%
Name Line Branch
com/defold/extender/services 54.5% 45.4%
com/defold/extender/services/DataCacheService 79.8% 61.9%
com/defold/extender/services/DefoldSdkService 75.5% 61.7%
com/defold/extender/services/DefoldSdkServiceConfiguration 90.9% 75%
com/defold/extender/services/GCPInstanceService 1.1% 0%
com/defold/extender/services/GradleArtifact 100%
com/defold/extender/services/GradleService 0%
com/defold/extender/services/GradleServiceInterface
com/defold/extender/services/HealthReporterService 82.4% 55.2%
com/defold/extender/services/MockGradleService 0%
com/defold/extender/services/NuGetCacheService 27.1% 22.9%
com/defold/extender/services/RealGradleService 55% 40%
com/defold/extender/services/ResolvedNativeDeps 0%
com/defold/extender/services/UserUpdateService 0% 0%
com/defold/extender/services/cocoapods - 51.9%
Name Line Branch
com/defold/extender/services/cocoapods 51.9% 58.7%
com/defold/extender/services/cocoapods/CocoaPodsService 9.1% 19.8%
com/defold/extender/services/cocoapods/CocoaPodsServiceBuildState 25%
com/defold/extender/services/cocoapods/CreateBuildSpecArgs 60.7%
com/defold/extender/services/cocoapods/IConfigParser
com/defold/extender/services/cocoapods/LanguageSet 66.6% 50%
com/defold/extender/services/cocoapods/MainPodfile 20%
com/defold/extender/services/cocoapods/PlatformAndLanguageSet 0% 0%
com/defold/extender/services/cocoapods/PlatformSet 0% 0%
com/defold/extender/services/cocoapods/PlistBuddyWrapper 0% 0%
com/defold/extender/services/cocoapods/PodBuildSpec 81.4% 65.4%
com/defold/extender/services/cocoapods/PodfileParser 94.9% 82.2%
com/defold/extender/services/cocoapods/PodfileParsingException 100%
com/defold/extender/services/cocoapods/PodSpec 60.4% 40%
com/defold/extender/services/cocoapods/PodSpecParser 92.2% 82.2%
com/defold/extender/services/cocoapods/PodUtils 86.7% 79%
com/defold/extender/services/cocoapods/ResolvedPods 52.7% 45.8%
com/defold/extender/services/cocoapods/XCConfigParser 94.5% 85.2%
com/defold/extender/services/data - 80.7%
Name Line Branch
com/defold/extender/services/data 80.7% 75%
com/defold/extender/services/data/DefoldSdk 87.5% 75%
com/defold/extender/services/data/GCPInstanceState 0%
com/defold/extender/services/spm - 37.6%
Name Line Branch
com/defold/extender/services/spm 37.6% 40.8%
com/defold/extender/services/spm/ResolvedPackages 0% 0%
com/defold/extender/services/spm/SpmBuildOutputParser 91.3% 81.1%
com/defold/extender/services/spm/SpmLockFile 90.5% 72.2%
com/defold/extender/services/spm/SpmManifestParser 87.3% 72.1%
com/defold/extender/services/spm/SpmManifestParsingException 100%
com/defold/extender/services/spm/SpmServiceBuildState 44.2% 0%
com/defold/extender/services/spm/SpmServiceConfiguration 44.4%
com/defold/extender/services/spm/SpmWrapperArchive 0% 0%
com/defold/extender/services/spm/SwiftPackageManagerService 15.3% 17.8%
com/defold/extender/tracing - 21.2%
Name Line Branch
com/defold/extender/tracing 21.2% 25%
com/defold/extender/tracing/ExtenderExecutor 0% 0%
com/defold/extender/tracing/ExtenderTracerInterceptor 100% 100%
com/defold/extender/tracing/TraceIdInResponseServletFilter 0% 0%
com/defold/extender/utils - 51.1%
Name Line Branch
com/defold/extender/utils 51.1% 53.8%
com/defold/extender/utils/FileCloneUtil 90% 100%
com/defold/extender/utils/FrameworkUtil 0% 0%
com/defold/extender/utils/PodBuildUtil 45.6% 45%
com/defold/extender/utils/VersionUtil 76.9% 75%

The -I and -F options no longer go into swiftFlags, where they needed token
pairs to be parsed and rewritten. The extension manifest gets the context keys
additionalSwiftIncludePaths and additionalFrameworkSearchPaths instead. They
are lists of paths that are validated to stay inside the extension folder and
are passed to the Swift compiler as -I and -F. swiftFlags is now a plain
allow list of flags.
The key is not Swift specific, so it is checked on every SDK and passed as -F
to the existing compile and link templates through ext.frameworkPaths.
Pod sources keep their own framework paths.
…the engine archive

After linking an engine with Swift code, scan it with swift-stdlib-tool --print and add the libraries
that the SDK settings of the platform mark as back-deployed for the minimum OS version, as Xcode does.
Fail the build when the minimum OS version has no Swift runtime at all.

SwiftRuntimeLibraries support = SwiftRuntimeLibraries.fromSdkSettings(new File(sdkSettings), target);
if (support == null) {
LOGGER.warn("Not checking the Swift runtime libraries: {} has no Swift support information for {}", sdkSettings, target);

SwiftRuntimeLibraries support = SwiftRuntimeLibraries.fromSdkSettings(new File(sdkSettings), target);
if (support == null) {
LOGGER.warn("Not checking the Swift runtime libraries: {} has no Swift support information for {}", sdkSettings, target);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants