I have a target that links with a framework (both of which I develop). If I build the target it builds the framework before hand for use in the target.
What I want during certain builds is to bring in an existing already built version of that framework and use it during the build of that target. Is there a way to force xcode to build using an already compiled version of a dependant framework
(The reason I'm doing this is because I have multiple targets that are built separately and want them all to be built using the same framework binary)
I finally solved this, this can be done by turning off the "Find Implicit Dependencies" under your build scheme.
Some good reading can be found here on the topic here: https://pewpewthespells.com/blog/managing_xcode.html#scheme-action
Related
I have an Xcode workspace with an iOS framework target and an iOS SampleApp target that depends on and embeds the framework. The framework is closed-source and thus gets delivered in binary form to customers, in a release package alongside a copy of the sources for the entire SampleApp project, so that they can test it out with our app and see how the integration is done.
With this setup, during the development phase, I can open the workspace in Xcode and build the SampleApp target, and because it has an explicit dependency on the framework, Xcode immediately triggers a build of the framework first and then the app is built and linked correctly.
Now I want to leverage the new XCFramework format to distribute this framework to customers instead of using lipo'ed fat frameworks. I update my shell script that produces the release package to create an xcframework bundle at the end with all the various platforms supported, and also update the SampleApp project to depend on this xcframework instead of on the individual frameworks as it did before.
But here comes the problem with the updated SampleApp: After removing the dependency on the frameworks, the workspace setup described above doesn't work anymore, i.e. Xcode will no longer automatically build the framework target when the SampleApp target is built, because the explicit dependency link between the two is now broken.
My first attempt at solving this involved making the xcframework part of my regular development workflow as well. For this I added a run-script phase to the framework build target that generates a current-platform-only xcframework bundle and copies it to the place where the SampleApp expects to find it, and then added the framework target to the targets of the SampleApp build scheme, in order for the framework to be built when the SampleApp is built. Unfortunately this approach does not work because Xcode 12 always seems to build both targets in parallel, no matter what the 'Parallelize Build' setting is set to, and because of that the app will fail to build simply because the xcframework is not yet ready when it's processed by the build system, which is very early in the build phases of the SampleApp. So unless there's a way to force Xcode to honor the order in which the build targets are defined in the scheme, this approach is a no-go.
If this approach cannot work then I'd have to ship the xcframework with the SampleApp unmodified, and instruct customers wanting to run the app to manually remove the references to the universal frameworks and then add/drag the xcframework to it. This would work but it's ugly and does not seem user friendly enough to me.
I'm also considering a different approach whereby the release script programmatically modifies the SampleApp.xcodeproj to depend on the xcframework instead of on the universal frameworks, but that seems like it could be a very fragile thing to do, so would rather avoid it if there's a better way.
This scenario does not seem unique to me so I'd guess someone else must have faced this problem too. If so, how did you solve it?
So I ended up solving this by programmatically modifying the SampleApp's project file at release time to link and embed the xcframework bundle instead of the universal frameworks.
Initially tried to use the tool pbxproj but it didn't quite work, the initial fat framework removal phase was not succeeding.
Then I figured I could try and diff the project.pbxproj file after having manually replaced the frameworks with an xcframework, and turns out it takes only a few small edits to get there, so they can be done programmatically with a simple shell script like the following:
#!/bin/bash
PROJECT_FILE=SampleApp/SampleApp.xcodeproj/project.pbxproj
FRAMEWORK_NAME=MyFramework.framework
XCFRAMEWORK_NAME=MyFramework.xcframework
sed -i '' "s|FileType = wrapper.framework; path = ${FRAMEWORK_NAME}; sourceTree = BUILT_PRODUCTS_DIR;|FileType = wrapper.xcframework; path = ${XCFRAMEWORK_NAME}; sourceTree = \"<group>\";|" ${PROJECT_FILE}
sed -i '' "s|${FRAMEWORK_NAME}|${XCFRAMEWORK_NAME}|g" ${PROJECT_FILE}
There might be small differences in other project setups but mostly it boils down to that. Just beware that if the xcframework is meant to be placed somewhere other than in the project root then you need both a name and a path properties in the first sed command, e.g.:
name = "MyFramework.xcframework"; path = "relative/path/to/MyFramework.xcframework"
I'm importing a newer version of a custom framework LCCommLibrary that creates the LCConnection class and I'm absolutely perplexed why this will build but not archive after trying multiple things.
Making sure the targets are added (4 apps, 1 Test)
Cleaning and Restarting the Project
This does builds and runs to my iOS devices, but none of the targets will Archive.
Archive usually uses the Release build configuration while building/running on the device uses the Debug build configuration, this is the hint that you have an issue there.
I would say in your case you can to go into the target's build settings, then look at Other Linker Flags, Runpath Search Paths and Library Search Paths, expand them to show the Debug and Release configurations and then ensure you have the same settings for both of them, you'll most probably find something missing in release in one of those.
Finally found the issue. The culprit was framework that was included was a debug version of the framework. This causes Archive Schemes to throw this error since the archive uses the Release as the default.
The resolution was to open the LCCommLibrary Project separately, change the Framework Target's Run Scheme to Release, Run and Build, Locate the target framework, import and embed that back into main project file, and archive as usual.
I have a very simple framework project, MyFramework, that builds successfully. The framework defines a single, global function, myFunction.
The framework also defines 2 global variables: MyFrameworkVersionNumber and MyFrameworkVersionString (These 2 variables were created for me by Xcode)
I have a very simple application project, MyApplication. I am adding the framework to the application project by dragging and dropping the framework package (i.e. the MyFramework.framework in DerivedData that was produced by building the framework) onto the Xcode Navigator and then selecting the framework in the Embedded Binaries section of the project's General tab.
If I add code to MyApplication that references the variable MyFrameworkVersionNumber then I am able to successfully build and run MyApplication.
If I add code to MyApplication that references the function myFunction then I am not able to build MyApplication.
First Update
I suddenly remembered that Carthage does exactly what I am trying to do. So, I used Carthage to build MyFramework and Voila! - I can drag/drop the Carthage build of the framework into the application project and successfully use it. So now my inquiry has become: What does Carthage know that I do not?
Oh for heaven's sake! It turned out to be so simple. MyFramework was being built for a Generic iOS Device (arm64 architecture). MyApplication was being built for a simulator (x86_64 architecture). As soon as I matched those two up all was well. The reason that the Carthage build worked so well is that it produces a universal binary (i.e. one that contains both architectures).
I still do not understand why the MyFrameworkVersionNumber global variable was able to be accessed regardless of the architecture. But I am okay with deferring that little mystery as some arcane bit of information that will be revealed in the goodness of time (perhaps something such as that it is in a header that is structured the same for both architectures).
I've built a Framework for distribution (not open source),everything is working despite Xcode not having a nice support for developing Frameworks.
The problem is that when archiving for AppStore you get the error:
The executable <EXECUTABLE_NAME_AND_PATH>.framework contains unsupported architectures[x86_64,i386]
The solution to the error above is to strip out the architectures mentioned, as already discussed in this question and in other sources as well.
The problem is that to use the binary on Xcode it is necessary to have all architectures, but to archive I cannot have all of them. How to build the framework in a way that it contains all the architectures (or in a way that simulators accept it) and still be able to archive and upload to AppStore without using the custom scripts to strip the exceeding architectures?
It looks like you need to build two fat libraries, one for development and another one for deployment.
This way you can create two targets into your project (you can duplicate the existing target). One target will be used for development and be linked with the full framework. The other target will be used for archiving and will be linked to the reduced framework.
Hoping this helps...
I have an XCode workspace created with XCode 6.0.1. It constains 2 (Swift) libraries and one iOS app (Swift) that depends on those 2 libraries. I had stable setup that allowed me to run the iOS app on both iPhone and simulators: The 2 library projects were added as Embedded Binaries (see picture) of the app.
Now, I have XCode 6.1. Recently, I deleted DerivedData folder in ~/Library/Developer/Xcode folder while XCode was running. After that my workspace did not work - the iOS app would fail to compile and I got linker error saying it cannot find the library projects.
I tried to solve it by removing my the 2 libraries from Embedded Binaries of the app project - and I cannot add them back. Clicking + button under Embedded Binaries in project settings displays workspace projects correctly but selecting and adding my library project does not add them to the list of Embedded Binaries. I have solved the linker error by creating new workspace. The app compiles but how it links the libraries is a mystery to me: They are not in listed Embedded Binaries or Linked Frameworks and Libraries not in the Frameworks search path. There appears to be no link between the app and the libraries it needs (and obviously have as it compiles) except that libraries projects are in the same workspace.
Why I cannot add library projects to Embedded Binaries? Is it normal in XCode 6.1 that dependency projects just compile and gets embedded into an app without being listed or linked anywhere?
This is a summary of my answer to the question Xcode won't add “Embedded binary” after deleting “DerivedData”, see the original question and answer for more context and information:
Remove all framework projects from the workspace
Perform a "clean build" and/or remove the "DerivedData"
Add project back into the workspace
Build the project (possibly optional)
In the General tab of the app target click the + under "Linked Frameworks and Libraries", select the framework.
Build and run in the Simulator (there should be no issues building or running)
Build and run for device (this might cause a crash due to the framework not being correctly linked, ignore this crash)
Click the + under "Embedded Binaries", select the framework. This should add it to the project (possible duplicate under "Linked Frameworks and Libraries")
Repeat for all required frameworks
Once building and running (on device) is confirmed you can remove any duplicate (and/or red) frameworks in the Project Navigator or target General tab
Ok, I ran into the same problem as you. After deleting the derived data, I could not re-link my binaries again. I think the reason is because the derived data is where the binaries are written to and linked against in your project.
What I did to solve was to select my Framework as my build target. After building it, the Framework target turned from red to black. I can see in your screenshot it is red, meaning it has not been compiled into a binary and written on disk.
Once I did this, I was able to re-link the Framework to my Project because there was a reference to it on disk. Hope this helps!
Clean your projects & build your framework first. Thereafter you can embed it.
Here is how I solved the problem:
Build the framwork.
Open the build folder and drag built framework into the app project (so it uses the path to DerivedData).
Add the framework to the list of embedded frameworks.
In the Finder, do a Show Contents on the app's xcodeproj file, then open project.pbxproj in your favorite text editor.
Find the line with the long DerivedData path. Change it so there is no name, the path is the framework name, and source tree is BUILT_PRODUCTS_DIR
Xcode should notice the change and the library in the app project will be black instead of red and will now build and run properly.
I have a very similar issue and fixed it just last night. Decided to come back to this thread and offer my workaround, as rjstelling's solution above did not help my case.
I have a workspace that contains two frameworks and one application. The App was making use of both frameworks happily for a while until I got hit by a mysterious compile eror where it decided that adding a property access to an instance variable called "cube" of a class type found in one of the framework, made access to "_cube" impossible (complaining it was not declared, while it had actually worked previously in a setter method).
Long story short, after a clean, somehow the workspace/app project lost track of the embedded framework of my iOS 8+ project. Removing the embedded framework was the last straw in that line of failure, causing my project to no longer allow be to select any frameworks for embedding.
Reverting the project & workspace to an earlier version did not get rid of the voodoo.
I ended up adding the to-be-embeded framework projects in the main app project (as files) and introduced target dependencies on the frameworks.
I was then able to re-embed the frameworks and link.
As for the _cube thing, I had to specify a getter for the property and #synthesize the property to a different name. I dont have an explanation for this one.
It's probably because your framework is a separate project and not a separate target. Try watching carefully Session 416: Building a Modern Framework at around minutes 34-36. It will show you how to set it up correctly.
This makes it a bit confusing if you wanted to share a framework across multiple projects by the way