2

I am trying to create a custom Swift xcframework "FrameworkA" for a client based on my own code, which has a dependency to another 3rd party framework "FrameworkB" (which I integrate through SPM). I'm having trouble figuring out the best way to do this.

In my case, I already have multiple targets in my project, and I have added another one for "FrameworkA", and added the relevant files to the framework target.

I can get my new framework to build if I add "FrameworkB" to "Framework and Libraries" section for my framework target, and adding it in code.

import FrameworkB 

let frameworkKit = FrameworkKit()

Now, referencing Apple's documentation, I used xcodebuild archive to create archive files for both "iOS" and "iOS Simulator".

However, when I read about 'umbrella frameworks', the answer listed here and Apple's documentation suggests that I shouldn't include a framework inside another framework.

So what are the concrete ways to accomplish this? I can't exclude the dependency because without it, my framework won't build at all.

Joakim Danielson
  • 43,251
  • 5
  • 22
  • 52
Z S
  • 7,039
  • 12
  • 53
  • 105
  • Is that different than a xcframework? – Z S May 28 '23 at 20:47
  • It seems like Swift Packages are based off xcframeworks as well. So in my case, would I create an xcframework and include the FrameworkB in it (in Framework and Libraries) ... and then create a "Swift Package" around it? And my clients would just need to import my FrameworkA in their own project? If there was a step-by-step answer to this, with some details, I can mark it 'answered'. – Z S May 29 '23 at 17:35
  • In Xcode, when I try to add a package, it wants a Git URL for it. The other option is "Add Local" in which case I would need to send the client the whole directory (including the source code). Is it possible to send a 'package' as a binary file (without including the source) that the client can drag-and-drop into Xcode and then be able to build and run? – Z S May 29 '23 at 19:40
  • Ok, so I see that I can create a package with a 'binaryTarget' and point it to my xcframework. But how am I supposed to share this with my client? Should I zip up the whole 'package' directory into a zip file and share it with them? – Z S May 29 '23 at 21:18
  • I did, after I read about SPM, but they don't seem to fill in the gaps properly, especially for my specific scenario. Apparently it's not trivial to create a binary package with external dependancies. Even if I can build the package, I'm not sure how to distribute it to a client, without going through Github. – Z S May 29 '23 at 22:10
  • What should the Package.swift file look like in this case (with a binary target and dependancies)? – Z S May 29 '23 at 22:29
  • The package file shown here doesn't have any dependencies. In general, it seems that you can't specify dependencies with `.binaryTarget`. There is some way to do with dummy targets, but I haven't found a proper solution using that. – Z S May 29 '23 at 22:37
  • I've been looking at this [page](https://forums.swift.org/t/swiftpm-binary-target-with-sub-dependencies/40197/6) specifically, which is referenced my multiple answers, but still don't find the solution for my specific use-case. – Z S May 29 '23 at 22:52
  • I don't think that what you are trying here is allowed by Apple, and I would not consider it good coding practice anyway. You are effectively including a binary build of another 3rd party framework, which creates an undocumented dependency on a certain version of this framework. Resolving dependencies should be part of the building process, and the whole reason for SPM is to allow that. If you would like to shield „your secret sauce“ you will have to abstract that part away so that it builds without that framework dependency. – DatBlaueHus Jun 05 '23 at 05:58

1 Answers1

1

When creating a custom Swift xcframework that has a dependency on a third-party framework, there are a few approaches you can take to ensure the proper integration without violating best practices. Here are some concrete ways to accomplish this:

  1. Embed FrameworkB as a binary: Instead of including FrameworkB inside FrameworkA as a framework, you can embed FrameworkB as a binary in your FrameworkA xcframework. This way, FrameworkB is not treated as a framework within FrameworkA but rather as a binary dependency. To achieve this, follow these steps:

    • Build FrameworkB separately and obtain the binary file (e.g., FrameworkB.framework).
    • Add the binary to your FrameworkA project by dragging it into the project navigator.
    • In the project settings for FrameworkA, navigate to the target's settings, go to the "Build Phases" tab, and under "Embed Frameworks," add the FrameworkB binary.
    • In your code, you can continue to import and use FrameworkB.

    This approach avoids the potential issues associated with including one framework inside another.

  2. Specify FrameworkB as a transitive dependency: If you're using Swift Package Manager (SPM) to manage dependencies, you can specify FrameworkB as a dependency of FrameworkA in your Package.swift file. This way, when you build or use FrameworkA, SPM will automatically handle the dependency resolution and include FrameworkB for you. Here's an example of how you can specify the dependency in your Package.swift:

    let package = Package(
        name: "FrameworkA",
        products: [
            .library(
                name: "FrameworkA",
                targets: ["FrameworkA"]
            ),
        ],
        dependencies: [
            .package(url: "https://github.com/vendor/FrameworkB", .upToNextMajor(from: "1.0.0")),
        ],
        targets: [
            .target(
                name: "FrameworkA",
                dependencies: [
                    .product(name: "FrameworkB", package: "FrameworkB"),
                ]
            ),
        ]
    )
    

    With this setup, when you build FrameworkA, SPM will automatically fetch and include FrameworkB as a dependency.

  3. Document FrameworkB as a prerequisite: If FrameworkB is a separate framework that needs to be present alongside FrameworkA, you can document it as a prerequisite. In your documentation, provide clear instructions on how to obtain and include FrameworkB before integrating FrameworkA. This way, consumers of FrameworkA will be aware of the dependency and can handle its integration separately.

Choose the approach that best fits your requirements and project structure. Embedding the binary or using SPM for transitive dependency management are generally preferred to avoid issues related to including frameworks inside other frameworks.

hugo bosse
  • 17
  • 1