Binary Frameworks with SwiftPM

I have been searching an answer to this, and I assume there is a way to do this but haven't succeed in finding it. I have a Package.swift which looks something like this, simplified for illustration purposes.

  let package = Package(
      name: "Core",
      platforms: [
          .macOS(.v10_14), .iOS(.v12)
      ],
      products: [
          .library(
              name: "Core",
              targets: ["Core"]
          )
      ],
      dependencies: [
           // some package dependencies here. 
     ],
     targets: [
         .target(
             name: "Core",
             dependencies: [
                 "BinaryFramework",
                 "BinaryFramework2",
             ]
         ),
         .testTarget(
             name: "CoreTests",
             dependencies: ["Core"]
         )
     ]
 )

The folder structure is like this on disk.

.
├── Frameworks
│   └── Build
│       └── iOS
│           ├── BinaryFramework.framework
│           │   ├── BinaryFramework
│           │   ├── Info.plist
│           │   └── Modules
│           │       └── module.modulemap
│           └── BinaryFramework2.framework
│               ├── BinaryFramework2
│               ├── Headers
│               │   └── FrameworkHeader.h
│               ├── Info.plist
│               └── Modules
│                    └── module.modulemap
├── Package.resolved
├── Package.swift
├── README.md
├── Core.xcodeproj
│   ├── project.pbxproj
├── Sources
│   └── RideCore
│       ├── Private
│       │   ├── Connector.swift
│       │   ├── Decodable.swift
│       │   └── JSONRequest.swift
│       └── Public
│           └── Common
│                ├── AuthenticationService.swift
│                └── Exceptions.swift
├── Tests
│   └── CoreTests
│       ├── AuthenticateTests.swift
│       └── BasicNetworkTests.swift
└── ToDo.txt

The frameworks are built already and I don't have the source so I can't import it as a package. These are not C system libraries. I tried looking at linkedFrameworks and importing dependencies as shown in the Package.swift file above but it does not find the files. Any assistance would be appreciated. Thanks.

Unfortunately, right now this doesn't work. SwiftPM cannot distribute packages containing binary frameworks. With the right combination of directives SwiftPM can build the folder structure you have, but it is ineligible to be a SwiftPM dependency because it would require the use of unsafeFlags for the linker, and you cannot use those in a package that is supposed to be a dependency.

What you can do, however, is distribute the binary frameworks separately and simply require that they be present and findable in order to build your SwiftPM package. To do that, you'd have to make some changes to the target Core. This is what you have today:

.target(
    name: "Core",
    dependencies: [
        "BinaryFramework",
        "BinaryFramework2",
    ]
)

This is what you'd want:

.target(
    name: "Core",
    linkerSettings: [
        .linkedFramework("BinaryFramework"),
        .linkedFramework("BinaryFramework2"),
    ],
)

This instructs SwiftPM that you have dependencies on the binary frameworks and you expect them to be present. Note that they must be on the default linker path on the system that builds Core, so you'll need to distribute and install them appropriately.

5 Likes

Thank you, it seems that I can do this using the linkedFramework you show above:

swift build -Xswiftc -FFrameworks/Build/iOS   
swift test -Xswiftc -FFrameworks/Build/iOS 

And it finds my frameworks, linking into the main app is similar in that I can add the frameworks path in the Xcode target. Alternatively, in the manifest:

 swiftSettings: [
        .unsafeFlags(["-FFrameworks/build/iOS"]),
 ]

and the command:

 swift build

This will work for now, but hope the discussion "SPM Support for Binaries Distribution" leads to something longer term. UnsafeFlags seems like it won't work past development phase.

My Core library now can't be a dependency because of the unsafe flags. Back to the drawing-board. Not sure what will work now.

I think I covered the suggestion above: require that the leaf package know how to find the binary frameworks, rather than distributing them as part of your package.

Sorry, I must be missing something, how do I control default framework path in a package imported by Xcode? The Core package must be compiled in Xcode and there are no Xcode settings on the Core target to define the frameworks search path on packages in Xcode. When I try to compile Core without the unsafeFlags it complains it can't find the frameworks so that it can be successfully compiled for Core. I don't see how to get to the next step of including the frameworks with the final product. Thank you for your patience.

I think it should be sufficient to adjust the settings on the project level.

Terms of Service

Privacy Policy

Cookie Policy