23

I've recently converted a bunch of my frameworks to use Swift Package Manager. My Package.swift looks something like this:

// swift-tools-version:5.1
import PackageDescription

let package = Package(
    name: "MDFToolbox",
    platforms: [
        .macOS(.v10_13), .iOS(.v12), .tvOS(.v12), .watchOS(.v3)
    ],
    products: [
        .library(name: "MDFToolbox", targets: ["MDFToolbox"])
    ],
    dependencies: [
        .package(url: "git@github.com:Swinject/Swinject.git", from: "2.7.0"),
    ],
    targets: [
        .target(name: "MDFToolbox", dependencies: ["Swinject"]),
    ]
)

Since the library used to be a framework, I'd like to link it in my app as a dynamic library (.dylib). According to the library product definition in the Package documentation, I can specify the type of my library to be .dynamic if I want:

The optional type of the library that is used to determine how to link to the library. Leave this parameter unspecified to let to let the Swift Package Manager choose between static or dynamic linking (recommended). If you do not support both linkage types, use .static or .dynamic for this parameter.

If I leave it as nil, Xcode defaults to building a static library when I link this package in my app project, which is not what I want.

If I set the type to .dynamic in my library's Package.swift, Xcode builds a .dylib, but it doesn't get embedded in the application, leading to a linker error:

dyld: Library not loaded: @rpath/libMDFToolbox.dylib
  Referenced from: /Users/mpdifran/Library/Developer/Xcode/DerivedData/Remind-eewbkbjpfrqbdwchjrbmrtxzsjew/Build/Products/Debug-maccatalyst/Remind.app/Contents/MacOS/Remind
  Reason: no suitable image found.  Did find:
    /Users/mpdifran/Library/Developer/Xcode/DerivedData/Remind-eewbkbjpfrqbdwchjrbmrtxzsjew/Build/Products/Debug-maccatalyst/libMDFToolbox.dylib: code signature in (/Users/mpdifran/Library/Developer/Xcode/DerivedData/Remind-eewbkbjpfrqbdwchjrbmrtxzsjew/Build/Products/Debug-maccatalyst/libMDFToolbox.dylib) not valid for use in process using Library Validation: mapped file has no cdhash, completely unsigned? Code has to be at least ad-hoc signed.

I also see no easy way of adding the .dylib to a Copy Files Build Phase...

So what's the recommended way of asking SPM to build and link a dynamic library through Xcode? Is this something that is not yet supported?

Mark
  • 7,167
  • 4
  • 44
  • 68

1 Answers1

23

I figured it out!

If you want to create a framework library, you need to force it to be one in the Package.swift like so:

.library(name: "MDFToolbox", type: .dynamic, targets: ["MDFToolbox"])

Once you've done that, you'll see an embed option in the Xcode project settings of the project linking to the library. When selecting the target, scroll down to the Frameworks, Libraries, and Embedded Content section. You should see an option to embed your dynamic library dependencies:

enter image description here

Mark
  • 7,167
  • 4
  • 44
  • 68
  • 9
    Just a note on this, Xcode doesn't yet support linking to a SPM dynamic library without embedding it, so if you have multiple targets that use the same library, you'll get duplicate symbols. You'll either need to use a different dependency manager, remove the duplicated embedded libraries from the archive, or use an umbrella framework to contain all SPM dependencies. – Mark Sep 08 '20 at 16:36
  • 1
    What do you mean, "Xcode doesn't yet support linking to a SPM dynamic library without embedding it"? I have noticed, if you edit the .pbxproj file directly, you can remove out the embed steps, and now it will say "Do Not Embed" and now everything seems to work as expected unless you have SwiftUI previews, in which case they won't work unless you embedded everything. This is really problematic for my app because what's the point of using SPM if we're just going to have to make a wrapper framework or embed things multiple times? Seems like a huge oversight on the part of Apple! Why?? – scaly Dec 29 '20 at 21:01
  • Yes, you could edit the project file directly but that's not really a scalable solution, and evidently there's some edge cases. Hopefully Apple fixes this soon! – Mark Dec 31 '20 at 20:17
  • 2
    Well, I would argue that project files themselves are not a scalable solution, and if you have so many Swift packages that scale is a factor you have too many packages. That being said if you just click that X button on "Embed Frameworks" in Build Phases, deleting that whole build phase, then the Swift packages that were being embedded & linked will now just be linked—no editing of the project file directly is required :D – scaly Jan 15 '21 at 08:15
  • This just saved my day. Is this documented anywhere, or did you figure out by yourself? Or is there an issue documented anywhere public where we can follow this issue? – choli Mar 10 '21 at 12:02
  • I’ve logged a bug with Apple, which is unfortunately not public. I’d be surprised if we didn’t see updates here in June with the new version of Xcode. – Mark Mar 11 '21 at 15:51
  • 3
    If you don't specify ".dynamic" then Xcode will embed the framework everywhere that depends on it, leading to the same problem you described about having to remove the duplicated embedded libraries. If you do specify ".dynamic" then (as of Xcode 12.5) you can manually remove from the embed steps. However I STRONGLY recommend against using Swift Packages instead of Xcode project files for local dependencies because it's a nightmare of bugs and limitations like this. Be warned! – scaly Jun 30 '21 at 06:18
  • Thanks for the comment! Glad to see Apple's making progress on properly supporting Swift Packages. I do agree you should probably hold off until linking across targets just works without needing to specify dynamic! – Mark Jun 30 '21 at 17:45
  • @scaly Regarding your cautionary note, I don't think the issue is so much SPM as much as Apple's integration of SPM into Xcode and the accompanying support for precompiled binaries. SPM had been well for open sourced libraries. In my experience a lot of SDK authors misconfigure their packages – slythfox Sep 22 '21 at 22:16
  • @scaly All the package managing solutions have issue. Cocoapods has a slew of Apple Silicon issues and xcframework support problems as of now. At least this way, we're incrementally stepping towards what should be a universal solution rather back stepping. – TheCodingArt Dec 14 '21 at 23:01