Why Am I Not Able To Embed My Custom Framework? - ios

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).

Related

Integrating XCFrameworks into the development and release workflows

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"

How to build dynamic Framework for iOS with no Embedded Binaries?

I try to generate framework named AfTestFramework for ios. I created through Xcode Cocoa Touch Framework with some basic code, ran it and got:
So far so good.
I created new Test Application and drag # dropped AfTestFramework.framework into the project, like crashlytics does (see below)
After building project and running I got Library not loaded ... Reason: image not found:
dyld: Library not loaded:
#rpath/AfTestFramework.framework/AfTestFramework Referenced from:
/var/containers/Bundle/Application/9B12D0AD-94AA-4119-A5DE-6BCAA806FA9F/TestAvoidEmbedded1.app/TestAvoidEmbedded1 Reason: image not found
I know the solution is to attach this framework to Embedded Binaries but I look for the way to avoid this approach.
Further, It demands to create 2 Frameworks: for Simulator and for Release otherwise I cannot release application with my framework, that looks a bit messy and weird.
I found that following frameworks work without Embedded Binaries:
crashlytics
firebase
facebook
Chartboost
ans so on.
So far I didn't find any description, I know also that its something new.
I started to play with xcodebuild like:
xcodebuild -sdk "iphoneos10.1" "ARCHS=arm64 armv7 armv7s" "VALID_ARCHS=arm64 armv7 armv7s" "ONLY_ACTIVE_ARCH=NO" -target AfTestFramework -configuration "Release" clean build
but no success.
Can somebody spread the light on this problem?
If you add a dynamic Framework to your iOS project, you must add it to the Embedded Binaries. In opposite to static libraries are dynamic libraries loaded at runtime. Thus the dynamic linker must access them on the device.
The only possible way to add the code inside of the framework without embedding the framework is to bind it statically to your app. For that you should create a static library from the framework. (Probably, libtool(1) may help you with that.) But anyway you may run into other problems, if the framework needs its bundle structure for loading resources etc.
I'm not quite sure what you are trying to achieve but it sounds like you are trying to create a framework which has a dependency on another framework.
So when used, it would be something like app -> your-framework -> some-other-framework.
I would strongly recommend looking at carthage as a solution to our problem. Carthage is an OS X native dependency manager that is designed to manage dependencies for you.
Firstly you need to check that the framework your framework uses is Carthage friendly. For now I'll assume so.
In your frameworks project you would add a Cartfile where you declare your dependency on the other framework, then use Carthage to download and build the other framework. You then add the built framework to your project as you would with any Apple supplied framework. You DO NOT need to embed the framework and generally speaking it's not recommended to do so. Carthage will handle this issue.
In the app project you would then also add a Cartfile, but you only need to specify your project in it. When Carthage builds the framework, it will automatically locate the other framework, download and build it as well. Then in the apps build phases you simple specify linking agains both frameworks and add a carthage-copy script phase to include them in the Frameworks directory of the finished app.
With regard to building for simulator and release. You don't need to build multiple targets. For a start, using Carthage means that the frameworks are only built when you need them, so this removes any requirement for you to supply binaries.
When telling Carthage to build the dependencies, by default it builds Release binaries. Building Debug frameworks for testing is just a matter of add --configuration Debug to Carthage's command line arguments.
Finally - to be fair, there are also two other package managers out there: Ruby based CocoaPods which is an older manager and for Swift, the Swift Package Manager. Personally I prefer Carthage for reasons I explain in a blog post here.
this is old but if its useful for future reference:
you can do
$lipo -create <path to simulator framework> <path to device framework> -output <path to the output framework>

Building a Swift Framework with Xcode 7 (Beta 3) to use as an Embedded Binary

Ever since Embedded Binaries were introduced in iOS 8, I have been wanting to port a lot of my common code into frameworks. I decided to wait one year before doing it and this year, with Xcode 7 Beta and iOS 9, I'm starting to do that just that.
I have started a Cocoa Touch framework project in Xcode 7 and I want to compile it into a usable framework. I can get it to compile my project into a .framework, but there's a few issues; namely, the framework doesn't appear to be importable into new projects (I will describe the steps I did for that shortly). Because of that, I'm not sure if my framework has any visible symbols.
This is what I have done to create the Framework:
Created my Framework as a Cocoa Touch Framework.
Went to my target's Build Phases, went to Headers, and added all my Swift files to the "Public" section, in hopes that will export all my simbols without having to mark them as public.
I tried to archive my project as a framework. Currently, it looks like Xcode 7 Beta 3 has a bug (going to report it later today) in which it generates corrupted archive files. For this reason I couldn't get my framework from the Organizer Window. To work around this, I changed the schema of the Run action in Xcode from Debug to Release, built it and grabbed it's generated .framework from my project's build/iphoneos-release directory. This was a quick test so I didn't need the frameworks generated for emulators.
And this is what I did to try to add the framework to a new project:
Created a "Frameworks" group (for organizational purposes) and dragged the framework there, selecting "yes" when it asked me if I want to copy the file to my project's directory.
Went to my target's settings, removed my framework from "Linked Libraries" (it was added there automatically), added it to Embedded Binaries instead. This added the framework to Linked Libraries again, so I had to remove it from there twice. Leaving the framework in Linked Libraries causes a linker error (can't find the framework - no idea why but I think it's irrelevant to my problem and something I should report to Apple as well), but once you remove it from there it seems to compile fine when you add it to Embedded Binaries.
Tried to import my framework in a file. Xcode complains there is "no such module".
Unfortunately, despite the fact that embedded frameworks have been around for around a year, I can't find much writing on the topic.
So my question is: Am I creating the framework correctly, making it possible that my framework/anything else is failing due to an Xcode 7 Beta bug? Or is there a different procedure to create a framework that I want to use as an Embedded Binary? I should probably mention that I want to make this library open source, and I think distributing a plain .framework file to the people who want to use it would be neat.
I Had the same issue on Xcode 7.
I solved it by editing the build settings of the project (the one which includes the framework).
By setting the Framework Search Paths field to $(PROJECT_DIR) or to the path to the directory that contains the .framework file it should help Xcode finding the module.

How to build iOS framework with XCode 6

I know of familiar tutorials on this, but introduction of framework XCode 6 template has changed the game.
I already watched WWDC 2014 video about building modern frameworks but it talks more about building extensions, framework & app all inside single project. It does not specify if the framework I make with it is truly reusable across any project.
I am building framework the XCode 6 way (File->New Project->Framework and Library->Cocoa Touch Framework), but when I import it inside my test app project (separate from framework project) - I keep getting various errors.
Example: Include of non-modular header inside framework, and so on.
I know this is not what it says, and there are quite some missing steps in whatever I am doing. The older tricks may have worked for everyone, but I simply don't find which way to follow after XCode 6.
For example, there is some folder structure that a framework needs, but XCode 6 doesn't comply to it while building it. Is it right? If not, how can I change the way the XCode builds framework folder hierarchy?
Do I go back to old school or am I screwing some tiny thing in XCode 6 that I am unable to create a reusable framework?
I am not sure if you are trying to build a framework with Objective-C or Swift as your question doesn't state it. I've encountered errors you are mentioning with Swift so I'll give you my method to build Swift frameworks.
I found the process for Objective-C to be very straightforward and well documented, so I'll skip this.
As for Swift, there are a few things to consider. First, Swift static libraries are not supported, so you must exclusively use a framework (aka dynamic library) when linking an app to a library.
Here are the steps:
Create the Framework using New > Project under IOS > Framework & Library, select Cocoa Touch Framework
To avoid the "ld: warning: directory not found for option..." goto Library Search Paths in Build Settings for your target and delete the paths.
You can't mix Objective-C with Swift so don't even consider adding the Swift-Header bridge file in your code.
There are some cases in swift where you need to import code from unexposed Frameworks. I've successfully used the module-map inside the framework to deal with these case.
I also select CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES in the Build Settings to solve 'include of non-modular header inside framework module'. That seems to work
I make sure that the header file that gets generated is marked as Public (not Project). Click on the file and you'll see the selection in the inspector under 'Target Membership'
You may run into some bizarre error messages when building. Xcode has a tendency to report linker errors when your code can't compile correctly resulting in missing files the linker needs to output its binaries. Sometimes XCode won't show the errors in the files you are compiling and you need to go manually on the build output and go back to the files. Some other time, you'll get a problem where you need to delete the cache. Those issues I call XCode blues and deal with it constantly. I found this type of problems happens more often when building libraries. The rest should work as expected.

Cannot add embedded binaries (other projects) to project dependencies in XCode

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

Resources