Trying To Merge Static Frameworks Using Carthage - ios

carthage version: 0.25.0
xcodebuild -version: 8.3.3
Are you using --no-build? No
Are you using --no-use-binaries? No
Are you using --use-submodules? No
Are you using --cache-builds? No
Cartfile
github "rs/SDWebImage"
github "AgileBits/onepassword-extension"
github "hsousa/HCSStarRatingView"
github "sugoi-wada/facebook-ios-sdk" "780a72cd6086c57939d83a7143462ae13dfb9a74"
github "CSStickyHeaderFlowLayout/CSStickyHeaderFlowLayout"
github "card-io/card.io-iOS-source"
I'm able to build my dependencies using a build script as outlined in the documentation here. Now I'm trying to merge my static frameworks into a dynamic one and running into problems.
To start, I'm adding a single framework to the dylib target, I've disabled bitcode and added the following OTHER_LDFLAGS: -all_load, -ObjC. I'm starting small with the HCSStarRatingView. It has been added to they dylib target and that builds.
To test this new combined framework I've added an app target. I can import the framework target and the app target recognizes it. However when I try to declare a new view with let view = HCSStarRatingView(frame: .zero) I'm getting the following error: Use of unresolved identifier 'HCSStarRatingView'.
I'm assuming that I don't import HCSStarRatingView directly because it's now part of my framework target. I'm also assuming that I don't have to add anything to my dynamic framework target's umbrella header to expose the linked static frameworks inside. Are those assumptions correct? Any idea what I could be missing here?

i think your assumption might be wrong.
I have a similar setup and got it working by importing both: dynamic framework and the static library that it depends on. eg:
import framework-target
import HCSStarRatingView
furthermore i had to make sure that the app target included the carthage build path

Related

Embedding XCFramework in application with project framework dependencies

I have an Xcode workspace which features a project with an iOS Application target, and another project with a Framework target. The framework target is dependent on another framework, which is integrated in the form of an xcframework:
MyApp
MyFramework
OtherFramework
Using regular OtherFramework.framework would require it to be linked to MyFramework and then embedded in MyApp even though MyApp doesn't require the framework itself. However when integrating with xcframework, this project then fails to build with a No such module 'OtherFramework' error.
Project settings:
MyFramework Project
MyApp Project
Removing OtherFramework.xcframework from the MyApp target fixes the build issue, but then causes library not loaded errors as the framework is not present in the application.
Demo project here: https://github.com/msaps/XCFramework-Link-Issue
How are you meant to link an xcframework in an application and link in a dependent framework?
Why?
pyckamil just posted this article which explains the issue in detail: Everything wrong with XCFrameworks.
It turns out Xcode has an optimisation for the ProcessXCFrameworkLibrary step which extracts the correct .framework from an .xcframework for the active build architecture. This is only run once which causes issues for additional targets that try to link the same framework.
Update
This issue is resolved in Xcode 12.0
UPDATED - Resolved in Xcode 12.0
shinsuk came up with a reliable workaround that works by adding architecture-explicit framework search paths to ensure the correct .framework within an XCFramework is found.
Details can be found in the README.
Check build settings and defining the Framework Search Paths to a folder which contains the frameworks in question. If the frameworks are placed in your project directory, simply set the framework search path to $(SRCROOT) and set it to recursive.
check the response Getting error "No such module" using Xcode, but the framework is there
IMO, It seems not xcframework issue.
Check out this answer: https://stackoverflow.com/a/59435627/2661407
Umbrella frameworks are not supported on iOS, watchOS, or tvOS.
OtherFramework.xcframework should be signed and embedded in your host app.
and add "#executable_path/Frameworks" setting into your MyFramework.framework > Build settings > Runpath Search Paths.
I had this issue as well after using xcframework instead of framework. So I changed my project structure:
The MyFramework Peoject embed OtherFramework.xcframework,Then make it exported using #_exported import OtherFramework in MyFramework Project. And the MyApp just link MyFramework but can both import/use MyFramework and OtherFramework.
BTW, It seems to custom the #rpath and manual codesign the OtherFramework.
I had an issue like that as well.
First, make sure if you have MyFramework.framework file within the same directory as MyApp.
Second, when building MyFramework.framework, make sure that OtherFramework.xcframework is as well in MyFramework's project directory.
And one more thing, check target SDK versions. They should be somewhere on the same level.
I had the same issue as you, and after seeing your pbxproj I think it can be solved the same way.
Change your framework search path to recursive (either through UI or manually editing the pbxproj "$(SRCROOT)/../Frameworks" => "$(SRCROOT)/../Frameworks/**"), like so: https://github.com/msaps/XCFramework-Link-Issue/pull/1/files

iOS Xcode SPM failed to demangle superclass

My app is composed of many projects (frameworks), one for each main feature and a common framework with all sorts of things that I need to access in multiple of my features.
I'm using Xcode 11's Swift Package Manager to add dependencies.
The common framework contains a RxSwift dependency, which I use throughout the whole project.
I'm facing problems when I try to use RxTest in any of my feature frameworks.
If I add RxTest via SPM to the test target directly and run the tests, I get
failed to demangle superclass of 'class name' from mangled name 'other class name'
and many
Class 'class name' is implemented in both 'common framework path' and 'test target path'
where all these classes are Rx related. The 'failed to demangle' error crashes the test and only occurs when I try to initialize a RxTest class.
If I add RxTest to the common framework, the tests run fine, but when I run the app, I get
dyld: Library not loaded: #rpath/XCTest.framework/XCTest
Which makes sense, because I'm adding a test framework to a non-test framework, and it's not something good to do.
So basically, I wasn't able to get a configuration where both the tests and the app run fine. Either the app runs or the tests run.
How can I get this working? Is there a way to include RxTest on the common framework only when I build it on a test target? Or should RxTest only be included on the test targets and I'm missing some configuration?
Xcode with SPM dependencies cannot handle same SPM dependency in multiple targets that are dependent on each other right now. Each dependency needs to be only in single target at the moment. I dont know why as of now, but I'll try investigate more and file bug if it is not filed yet.
Your issue is likely that the library is using static linking instead of dynamic linking. In SwiftPM you can specify a library as being static or dynamic if you want or you can just let the build system decide which is what most packages do. Xcode seems to favor the static approach when it builds with SwiftPM which results in the build issues you are experiencing.
If you modify the Package.swift to have RxTest be a dynamic library instead it should work. You can easily test this by cloning RxSwift and modifying this line:
.library(name: "RxTest", targets: ["RxTest"]),
into:
.library(name: "RxTest", type: .dynamic, targets: ["RxTest"]),
and then dragging the local copy of RxSwift into your Xcode Project Navigator.
It will then use your local copy of the package instead of the one cloned by Xcode.
Once you do this you can link it against any targets you need and it should work. If that does actually fix the problem then your long term solutions are likely:
1) Have a fork that simply changes it to a dynamic library.
2) Convince the RxSwift community to change their products to dynamic or to vend dynamic versions in addition to the default.
3) Don't use RxTest or similar things in multiple places.
It is also worth noting, that Xcode 11.3 and earlier do not support archiving with dynamic Swift Packages. So if you go down the dynamic route you will have to wait for Xcode 11.4.
Workaround:
I had the same issues. My project configuration is:
Workspace
ProjectAppOne
AppTargetOne (Embed and sign FrameworkTarget)
ProjectAppTwo
AppTargetTwo (Embed and sign FrameworkTarget)
ProjectCoreFramework
FrameworkTarget
testsTareget
SPM Dependencies (including RxSwift)
My workaround was:
Duplicate the FrameworkTarget and create a FrameworkTargetT (make sure that are both building with no issues).
FrameworkTarget is still used to build the applications targets, so be sure that is the only one used in the imports of the application targets.
Add RxTest to FrameworkTargetT
Remove RxTest from FrameworkTarget
Set in your tests #testable import FrameworkTagetT
Adjust the FrameworkTarget to not run the testsTarget
Set the FrameworkTargetT to run the testsTarget
Adjust your CI fastlane/scripts, that involve testing target and call the FrameworkTarget directly
At the end the project structure was looking like:
Workspace
ProjectAppOne
AppTargetOne (Embed and sign FrameworkTarget)
ProjectAppTwo
AppTargetTwo (Embed and sign FrameworkTarget)
ProjectCoreFramework
FrameworkTarget
FrameworkTargetT
testsTareget
SPM Dependencies (including RxSwift)
I do not think is the ideal solution, it is easy to import the wrong framework in code or test, so double check that you are happy with that. But while we are waiting for the SPM team to sort the problem, it could be used and removed easily. This is also easy if you have already a framework separated code.
PS: you need to remember to add every new file to both of the targets FrameworkTarget and FrameworkTargetT.

cocoad pods - build static framework

Hi have this cocoapod framework, which I distribute in binary mode only (no sources). Basically, I am distributing the output of carthage build, which creates a dynamic framework.
I have been asked to release a static version of this framework as well. I have made a new target in XCode which builds the framework using a static library (using same sources as the dynamic framework).
Eventually, I file ...blabla.a and see it contains only arm architectures.
I have been looking into: https://github.com/Carthage/Carthage/blob/master/Documentation/StaticFrameworks.md - but this just fails for me (at linking state it tries to execute Ld which fails).
My code is only objc, and I am running Xcode 10.2.1.
Version v0.30 and above of Carthage can build static frameworks. Documentation does not say how :)
The link provided above is useless (does not work on modern systems). Instead I used this: https://github.com/Carthage/Carthage/issues/2534#issuecomment-407807111
and got a static framework.
As a bonus, I was able to make a new cocoapod (I added a new podfile, with same name and a static postfix), which links to my static build (in the same repo as the dynamic, just a different path), only diff between those two pods, is s.static_framework = true.
I am unsure how to make this a single pod which will honour use_framworks!, this this method

Xcode import my framework into a project

I've created an objC dynamic framework and I'd like to add it as a dependency into an objC project.
I've read those guides and some SO answers but none of them works:
https://developer.apple.com/library/content/technotes/tn2435/_index.html
http://netsplit.com/xcode-two-apps-with-a-shared-private-framework-in-a-workspace
https://medium.com/swiftworld/my-xcode-project-structure-for-open-source-project-1d363ff48534
https://www.raywenderlich.com/126365/ios-frameworks-tutorial
Basically what I do is open the host app and drag the framework project in it.
Then I drag the framework from the Products group of the framework project into the Embedded binaries section of the sample app.
What I see in the build phases is that I have:
In the target dependencies I can see the framework
In link binary with libraries I can see the framework
It has been added a new phase called Embed framework and my framework is there
If I try to import the framework module the compiler says
Module XXX not found
The only way to make it work seems to add in the framework search path of the build phases, the direct path to the product of the framework itself.
For what I understood it seems that somehow the path of the framework is not taken into account, but as far as I know in none of the guide is written to change it.
[NOTE]: no cocoapods or Carthage solution
Turns out that I had a wrong build settings in Per-configuration Build Products Path, instead of having it to point to build/debug-iphoneos it was pointing just to build .
Thus the process explained in the question works.
Hope this will help someone else.

Xcode: create Framework with pod dependecies

I create a framework that use AFNetworking (installed with pod).
I can import this framework on my project and I can use all classes/methods that I exposed, so project compile.
When I try to run project on simulator, I obtain this error:
dyld: Library not loaded: #rpath/AFNetworking.framework/AFNetworking
Referenced from:
/Users/.../Library/Developer/CoreSimulator/Devices/F56F98F0-2AE0-4C87-AC9A-6E3B449762D1/data/Containers/Bundle/Application/BFA5359F-8FCE-4402-8487-CD9C002CB673/MyProject.app/Frameworks/MyFramework.framework/MyFramework
Reason: image not found
I already included 'MyFramework' under:
Build phases -> Embed Frameworks
I suppose I missing something on building Frameworks, but I can find out what! I already seen this unanswered question.
Who can I use MyFramework without installing Pod on MyProject again?
It is really delayed, for this issue but I have got my project working. It might not be the best way but it works. I am putting it here for future reference.
I wanted to do it without using Pods in the main project. The reasoning is, we are bundling our SDK into a framework.
So essentially, the first step I took was to get the framework project bundling without using the workspace. I just dragged the Pods project into the framework project.
Then I added the frameworks the the pods project creates and add them to my framework. I set them to optional and code sign. You can see them in the picture of where I added them under the build phases.
Then add them to your main project. not the framework the normal way by adding the project and or the framework. Then add it to the Embedded binaries and Linked Frameworks.
If it helps anyone, the solution mentioned by #ArtyomDevyatov does work. The trick is that you have to add 's.dependency' in podspec file.

Resources