Missing Required Module (of linked Child Library)

Setup,
I've got a Xcode project with a dynamic library target called Parent.
Parent has a SPM static dependency called Child. I'm using library evolution and AccessLevelOnImport on both.

Goal,
My goal is for the symbols of Child to be available to consumers of Parent.framework. Either by consumers directly calling import Child or by adding a @_exported import Child within Parent. Both of these exhibit the issue so thats a bit of a tangent for now.

Issue,
When I build my final Parent.framework and embed/link that to a Consumer I get the "Missing Require Module Child" error.

Diagnosis,
From the build logs it appears only Parent.swiftmodule is copied into Parent.framework/Modules/ but while Child.swiftmodule is produced in derived data but is not copied (or merged?) into the Parent.framework.

Within the Consumer I can add an explicit SWIFT_INCLUDE_PATHS the Child.swiftmodule and this works. This however, does not seem right nor is it scalable for consumers.

Is there some proper way to combine these .swiftmodule directories, am I fundamentally misunderstanding something or perhaps this is a bug?

Steps To Reproduce,

  1. Build SPM-Framework/Parent/Parent.xcodeproj
  2. Copy the produced Parent.framework into Consumer/. I've pre-included a copy of Consumer/Parent.framework if you want to skip building Parent yourself
  3. Delete Derived Data. Don't skip this step.
  4. Build Consumer and see the issue.

Some Other Observations,

  • An awkward solution that works. In the PrebuiltChild-Framework directory I've pre-built a Child.framework. Parent statically links to this pre-built Child.framework. Consumer is happy with the final Parent.framework from this approach. EDIT: This does not work either. Xcode search paths got me :cry: .
  • A non-SPM solution that does not work. In the XcodeProject-Framework directory I'm defining Child as a sub-xcodeproj within Parent.xcodeproj.

Screenshot 2024-08-20 at 21.14.11
when you build a Parent framework in products you can see the child is lying outside its needs to be part of the Parent.framework

@manish12jain - Child is statically linked to Parent.

How are you building Parent such that it even produced a Child.framework?

This is what I see,
image

@popwarfour for me, the consumer app works fine even when I delete derived data

@manish12jain Can you share the steps you took and what Xcode version are you on?

You shouldn't be seeing a Child.framework in derived data if you are building from SPM-Framework/Parent/Parent.xcodeproject. Even if you are, delete that and retry Consumer and you should see error.

I chatted with @manish12jain. He was accidentially building the PrebuiltChild-Framework working example.

If you'd like to reproduce the issue you should be running the SPM-Framework or XcodeProject-Framework to build the Parent.framework. Copy that into Consumer to see the error.

1 Like

If there's a public import of Child, the Child module will need to be available. There is no way around that.

@NeoNacho - The details are still a bit unclear but I was curious if there is a way to define a module map the supports this? The re-exporting seems plausible on the surface.

This might be a poor option even if it works. That said, is there a proper way to combine these module map directories? If we could have a run script that merged these we might be ok?

@popwarfour if you find any solution please post here it will help others. Thanks!!!

@popwarfour did you find any way to do this?I also need to implement something like this so it would be helpful if you could share your findings on same

I've found a few work arounds but none of them feel great.

  • Write the code you want to propagate up from child in some c-flavor language (probably obj-c) and manually include the headers in the outer framework's umbrella header. I wasn't successful writing the types in swift and moving the Child-Swift.h into the framework header but that'd be ideal if I could get that working.
  • Manually copy/ship the child frameworks(s) .modulemap and .swiftmodule and have consumers manually set an INPORT_PATH.
  • Make everything a separate dynamic library.
  • Write a bunch of ugly bridging code to manually re-expose symbol copies.

I briefly thought mergable libraries were going to be my savior but they are not.