44

I have build a framework using the following tutorial.

And I also got this framework.

I am trying to implement the second Framework inside mine, from what I read in the Apple docs the resulting framework is called "Umbrella Framework".

I added the second framework in framework using drag and drop and the verify that it is in the "Link Binary With Libraries".

And after I tried to make the next import in one of the classes of the my framework:

#import <CrashReporter/CrashReporter.h>

I received an error as the imported framework is not visible.

Also I have seen the stackoverflow post: How to create an umbrella framework in iOS SDK?

Update

Have anyone tried to extract the PLCrashReporter classes for iOS and integrate the in a project?

You can find my attempt here.

Community
  • 1
  • 1
Laur Stefan
  • 1,519
  • 5
  • 23
  • 50
  • In your build settings, what does the headers search path look like? Sounds to me like it's not finding the headers for CrashReporter. – tmpz Jan 13 '16 at 11:17
  • They look like: #import – Laur Stefan Jan 13 '16 at 15:04
  • 1
    Here is Simple Answer [enter link description here](http://stackoverflow.com/questions/27868259/embedding-a-framework-within-a-framework-ios-8) – Rajesh Kumar Nov 17 '16 at 12:19
  • Thank you @RajeshKumar for your contribution, if you are kind enough you could make a small tutorial and add it as an answers, also please user Xcode 8. – Laur Stefan Nov 17 '16 at 13:43

4 Answers4

62

The temptation to distribute another framework is understandable, but highly discouraged. I'll try to explain why that is (with arguments), and also provide some great alternatives that will help in your case.

Umbrella framework are intended for use, only when you are the distributor of both frameworks, you have full control over them, and they will be distributed together.

There is a popular quote on the topic from Apple, where they say that they discourage umbrella frameworks.

Don't Create Umbrella Frameworks

While it is possible to create umbrella frameworks using Xcode, doing so is unnecessary for most developers and is not recommended. Apple uses umbrella frameworks to mask some of the interdependencies between libraries in the operating system. In nearly all cases, you should be able to include your code in a single, standard framework bundle. Alternatively, if your code was sufficiently modular, you could create multiple frameworks, but in that case, the dependencies between modules would be minimal or nonexistent and should not warrant the creation of an umbrella for them.

First, here is what most developers do in this situation, since there are many frameworks out there that rely on others.

Inform the user that your framework requires the use of another third party framework. This is completely standard and expected in most cases. Then link to it at the system level. It's as simple as that. Your framework will find the third party and function seemlesly, just like using a native framework. Take UIKit for example. To link to the third party, follow the steps in the next section. It can certainly be done the standard way, but using a tool like CocoaPods will make your project easier to maintain.

To completely answer your question, instead of adding the third party framework the usual way, since you could run into problems and complications, use CocoaPods to add it for you. This way, you eliminate possible issues and also get the added benefit of CocoaPods getting you the exact version of the third party you will need.

Here is the CocoaPods Podfile for an app called "CrashTest"

target 'CrashTest' do
pod 'PLCrashReporter', '~> 1.2.0'
end

Just to clarify, when you are developing the framework, it will still be added to your project and visible. The big difference here is that it will be distributed separately from your framework, and end users will have to add both to their projects in order to make things work.

Here are the reasons why this is done this way.

For example, you would like to include PLCrashReporter in your framework. Say another framework vendor wants to include it in theirs as well. The application using both frameworks will have PLCrashReporter included twice (as part of each umbrella framework). Possible even different versions of it. This could lead to serious issues inside of the user application. If both umbrella frameworks are linking to PLCrashReporter, as described in the previous section, this issue would be avoided completely.

Another point, which I touched on above is versioning. When distributing an umbrella framework, you need to be able to control the versions of all frameworks involved, and you have no control over the third party framework. Which would again lead to a similar problem as the one described above.

I know that this approach does not provide a direct answer to the question, but it's trying to discourage a bad practice instead, while pointing the industry standard way of doing things.

Vel Genov
  • 10,513
  • 2
  • 16
  • 19
  • @Laur Stefan, did you have a change to give the suggestions above a try. Did it solve your issue? Let me know if you need some more info on the topic. Thanks! – Vel Genov Jan 15 '16 at 16:19
  • Thank you @Vel Genov but I am interested in keeping the SDK that I am including hidden and also for me to only have to include one frameworks not the both – Laur Stefan Jan 21 '16 at 09:20
  • @VelGenov Can you elaborate on "Then link to it at the system level." How do you do that? – olynoise Sep 15 '16 at 14:21
  • @olynoise, what I mean there is, to add the framework to your project. Xcode will take care of the rest. Here is how to add a framework to your project. In Xcode, select your project, navigate to "Build Phases". Expand the "Link Binary With Libraries" menu. Hit the "+" sign. Select "Add Other...". Navigate to the framework you would like to add, and hit "Open". This will take care of it. Let me know if you run into any other snags, or I'm misunderstanding your question. – Vel Genov Sep 15 '16 at 16:36
  • @VelGenov I posted a new question as I think my scenario might be slightly different : http://stackoverflow.com/questions/39517318/interdependent-frameworks-xcode – olynoise Sep 15 '16 at 17:48
  • @olynoise, it does seem to be a different workflow. Took a stab at it, based on the info you have. Thanks for posting a link to it. Let me know if I can help with anything else. – Vel Genov Sep 15 '16 at 18:12
  • 9
    We shouldn't be using "Use Cocopods" as a valid answer. That doesn't explain how to do this – TheCodingArt Apr 17 '17 at 14:43
  • @TheCodingArt Cocopods is there to help you keep track of the external libraries. You can use other tools or do it manually. Let me know if this clears things up – Vel Genov Apr 18 '17 at 02:27
  • 6
    @VelGenov I'm very familiar of what Cocopods is. You're recommendation for managing frameworks is to introduce a dependency in an environment rather explain how to do it yourself. This is a poor answer and harmful for unknowing people. – TheCodingArt Apr 21 '17 at 12:26
  • 2
    Cocopods in general is harmful to the iOS community. Wait for the actual swift package manager don't poison your system with some ruby code that (poorly) injects a bunch of crap into your project file (which is unsupported). And Apple is just wrong here. There are totally valid reasons to build "umbrella frameworks" as evidenced by their existence in the first place. – dcow Oct 25 '17 at 21:30
  • @dcow, Is it possible now to do with Swift package manager? Could you please point to any helpful online tutorial for that? – adev Feb 15 '18 at 01:14
  • 1
    @VelGenov While developing a framework we add a third party dependancy framework to our project which is fine, but how can we build our framework to distribute with out third party dependancy framework. if remove third party framework while building ours we get error right?? can you pls answer – Madhu May 16 '19 at 13:00
30

Apple discourages you to create an umbrella framework but yet, still says it is possible with some description on its structure etc' so i'll focus on how to actually do it.

I must mention that if you control one or more frameworks, bundling them into one is not such a bad idea and can help the end-developer.

Creating an umbrella framework is really simple now on recent Xcodes (7 and up)

Here's how to do it.

First create a framework:

enter image description here

Drag CrashReporter.framework to the Umbrella framework project:

enter image description here

Example code to make sure both frameworks are functional:

Umbrella.h:

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>

@interface Umbrella : NSObject

+(void)sayHelloFromUmbrella;

@end

Umbrella.m:

#import "Umbrella.h"
#import <CrashReporter/CrashReporter.h>

@implementation Umbrella

+(void)sayHelloFromUmbrella
{
    NSLog(@"Hey from Umbrella");
    PLCrashReporter *crashObject = [[PLCrashReporter alloc]initWithConfiguration:nil];
    NSLog(@"crashObject: %@",crashObject);
}

@end

Build and you'll get the Umbrella.framework (that contains CrashReporter.framework) in your build folder.

Drag Umbrella.framework and place it inside "Embedded Binaries" in the project that is going to use it:

enter image description here

Then just import your finished framework.

ViewController.m from the project that we just dragged Umbrella.framework to:

#import "ViewController.h"
#import <Umbrella/Umbrella.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [Umbrella sayHelloFromUmbrella];
}

Which will output this:

Hey from Umbrella 
crashObject: <PLCrashReporter: 0x7fb441d5c650>

Which tells us that both frameworks are working.

Segev
  • 19,035
  • 12
  • 80
  • 152
  • While this is a great post, and it answers the OP directly, this practice is highly discouraged both by Apple and the dev community. I believe the OP is getting confused about generally linking to a framework (including another framework in a project) and creating an umbrella (bundling a multiple frameworks in one package). An umbrella framework is never the option, unless you have ownership of both frameworks. Another point I'd like to make is that PLCrashReporter is under a specific license. The OP needs to make sure they follow the licensing terms when distributing PLCrashReporter. – Vel Genov Jan 17 '16 at 15:08
  • 11
    I started my answer with the discourages by apple. Also, I wasn't discussing the ethics of the act. (licenses etc') This is simply answering the op direct question. I manage a few frameworks and combining two of them into one was, in our case, a good solution. I couldn't find a decent tutorial on how to do it with recent Xcodes and believe the above suffice. – Segev Jan 18 '16 at 08:32
  • 1
    @Segev. could you give me another resources ? i want to integrate same. – Avijit Nagare Apr 27 '16 at 06:22
  • 8
    The framework ("CrashReporter") is not actually embedded in the Umbrella framework. It is only linked. The CrashReporter framework would have to be copied into a directory within Umbrella and would have to be loaded from there at runtime. – quellish Jan 18 '17 at 01:24
  • @quellish Not at all. You can otool (or use another method to decompile) the end result. Umbrella is an independent framework that includes PLCrashReporter. – Segev Jan 19 '17 at 10:09
  • @Segev An umbrella framework is a framework *that contains other frameworks*. From the documentation: "Physically, umbrella frameworks have a similar structure to standard frameworks. One significant difference is the addition of a Frameworks directory to contain the subframeworks that make up the umbrella framework.". What is described in this answer (and your comment) is a framework that *links to* other frameworks, but does not *contain* them. This answer does not describe how "CrashReporter" would be embedded in "Umbrella". – quellish Jan 25 '17 at 20:04
  • 2
    @Segev This answer, does not describe the creation of an umbrella framework. Again, the framework produced by this answer *statically links* to "PLCrashReporter". The "PLCrashReporter" binary is *archived* within "Umbrella" . – quellish Jan 25 '17 at 21:01
  • 7
    This answer is not creating an umbrella framework just linking to frameworks – drasick Feb 09 '17 at 11:02
  • Have you actually tried submitting an app to the store? The first comment here shows someone who tried and it fails with an error about not allowing nested bundles. https://stackoverflow.com/a/39767803/259521 – malhal Oct 14 '17 at 14:15
  • @drasick, did you find an answer on how to do umbrelkla framework? As you mentioned, this answer is just linking frameworks. – adev Jan 23 '18 at 02:23
  • @adev sorry I work a year ago on it. Can't remember. – drasick Jan 23 '18 at 14:28
15

HERE IS AN DEMO PROJECT:

Umbrella Framework Demo

All answers under this line are wrong, cause they just do the thing that manually copy sub frameworks into "umbrella framework"

Embedding a framework within a framework (iOS 8+)

How to create an umbrella framework in iOS SDK?

How to add a framework inside another framework (Umbrella Framework)

Umbrella framework

First thing we should know that "umbrella framework" is a conception in Mac OS not in iOS, the official document is here

https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/CreationGuidelines.html#//apple_ref/doc/uid/20002254-BAJHGGGA

if you want to create an un-recommend "UmbrellaFramework", you must do these process step by step, and know details during the compile and link periods

  1. Change all sub frameworks Mach-O to Static Library, it means compile this target as Static Library(.a)
  2. Manually copy all sub-Framework into UmbrellaFramework during the build phase(Like other answers did)
  3. Add "FakeBundleShellScript" to Target "UmbrellaFramework", it makes all sub frameworks package itself resources as bundle to join "UmbrellaFramework"
  4. Change the framework load function, you must load the sub-framework resources via path or url, cause it became an bundle, this step means you should have the supreme control of all codes both sub-frameworks & umbrella

!!Here is an example of "FakeBundleShellScript" you can refer

APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}"
find "$APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORK
do
FRAMEWORK_EXECUTABLE_NAME=$(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable)
FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME"
BUNDLE_IN_ROOT="$APP_PATH/${FRAMEWORK_EXECUTABLE_NAME}.bundle"
if [[ -e "$FRAMEWORK_EXECUTABLE_PATH" ]]; then
  FRAMEWORK_MACH_O="$(otool -a "$FRAMEWORK_EXECUTABLE_PATH" | head -n 1)"
  FRAMEWORK_FAT_ARCH="$(lipo -info "$FRAMEWORK_EXECUTABLE_PATH")"
else
  FRAMEWORK_MACH_O="NO EXIST"
  FRAMEWORK_FAT_ARCH="NO EXIST"
fi
echo "FRAMEWORK_EXECUTABLE_NAME is $FRAMEWORK_EXECUTABLE_NAME"
echo "FRAMEWORK_EXECUTABLE_PATH is $FRAMEWORK_EXECUTABLE_PATH"
echo "FRAMEWORK_MACH_O is $FRAMEWORK_MACH_O"
echo "FRAMEWORK_FAT_ARCH is $FRAMEWORK_FAT_ARCH"
echo "BUNDLE_IN_ROOT is $BUNDLE_IN_ROOT"
if [[ "$FRAMEWORK_MACH_O" =~ "Archive :" ]]; then
  echo "Rmove Static-Mach-O is $FRAMEWORK_EXECUTABLE_PATH"
  rm "$FRAMEWORK_EXECUTABLE_PATH"
  defaults write "$FRAMEWORK/Info.plist" CFBundlePackageType "BNDL"
  defaults delete "$FRAMEWORK/Info.plist" CFBundleExecutable
  if [[ -d "$BUNDLE_IN_ROOT" ]]; then
    rm -rf "$BUNDLE_IN_ROOT"
  fi
  mv -f "$FRAMEWORK" "$BUNDLE_IN_ROOT"
elif [[ "$FRAMEWORK_FAT_ARCH" =~ "Architectures in the fat file" ]]; then
  #statements
  EXTRACTED_ARCHS=()
  for ARCH in $ARCHS
  do
    echo "Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME"
    lipo -extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" -o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH"
    EXTRACTED_ARCHS+=("$FRAMEWORK_EXECUTABLE_PATH-$ARCH")
  done
  echo "Merging extracted architectures: ${ARCHS}"
  lipo -o "$FRAMEWORK_EXECUTABLE_PATH-merged" -create "${EXTRACTED_ARCHS[@]}"
  rm "${EXTRACTED_ARCHS[@]}"
  echo "Replacing original executable with thinned version"
  rm "$FRAMEWORK_EXECUTABLE_PATH"
  mv "$FRAMEWORK_EXECUTABLE_PATH-merged" "$FRAMEWORK_EXECUTABLE_PATH"
fi
done

http://alanli7991.github.io/2017/07/17/%E6%A8%A1%E5%9D%97%E5%8C%9617Framework%E4%B8%8EStaticFramework%E4%BC%AA%E8%A3%85Bundle/

As all I said, the key point of to make an un-recommend "UmbrellaFramework" is !!!! [Compile the sub-frameworks as Static, process the resources via fake a bundle], REMEMBER!! Apple always said DONT'T CREATE AN UmbrellaFramework

if you can understand Chinese, more details to make an "UmbrellaFramework" can be obtained from my blog

Alan.li 2017年的文章

MinamiTouma
  • 409
  • 3
  • 10
  • Thanks a lot, but given the complexity of your approach, I may need sometime before I can clone your success. Seems all the other answers on stackoverflow are teaching how to copy the sub-framework inside the "Umbrella" framework's folder, and then ask the actual user of the "Umbrella" framework to search for that framework. I don't see any difference for sending sub-framework and the master framework separately for those approach. – vk.edward.li Feb 03 '18 at 15:16
  • If I want my client only import a single framework without hacking the searchpath stuff, the only possible way would be: compile all my sub-Dynamic.framework into sub-staticLib.a, and then add those .a into the single framework? Thanks! – vk.edward.li Feb 03 '18 at 15:54
  • Well I followed your blog, and found out that I should be compiling my sub-Dynamic.framework into sub-Static.framework (I don't even know there is Static.framework!!!) but not sub-staticLib.a, finally I can create a sample project which can import the master-Dynamic.framework directly, hiding my sub frameworks, without any additional user settings, thanks a lot!!!! – vk.edward.li Feb 03 '18 at 22:21
  • **TL;DR: This should be the only correct answer in stackoverflow.** – vk.edward.li Feb 03 '18 at 22:22
  • @vk.edward.li, did you manage this to make it work? I couldn't follow the blog in chinese. Is there anyway you could upload a sample setup somewhere and share since you got it to work? Thanks in advance. I followed the above steps and I still got the same error "missing required module 'FrameworkA'" I have sample framework A with a swift file which has mach-o type static library, and FrameworkB which uses it. Test app uses FrameworkB but if i copy test app to another machine, it cant find frameworkA. – adev Feb 11 '18 at 21:18
  • Yes I did, may be I could upload a test project later, here's my setup: **1.** In the project "SubFramework", setup a `Cocoa Touch Framework` target, change `Mach-O Type` to `Static Library`. **2.** Build or Archive, anyways just generate the actual file A.framework. **3.** Drag that file into another project, remember you need to link it inside project setting's `Linked Frameworks and Libraries` – vk.edward.li Feb 11 '18 at 21:56
  • However, if you don't do anything special (those lipo stuff or magic build script), the default generated build would contains 1 target platform only, either iOS (armv7/arm64) or Simulator (x86_64), you can test the framework setting using the same platform type only, so you will have 1 framework file for simulator and 1 framework file for iOS devices. Once you are confident with the project setup then you need to figure out how to create a "fat" Static Framework which includes armv7+arm64+x86_64 in 1 .framework file for final usage. – vk.edward.li Feb 11 '18 at 21:58
  • @vk.edward.li, I did all those and it works fine in my mac even if I dont change mac-o type to static(dynamic or static doesnt make any difference). Issue is when I give this umbrella framework to someone for use in their app, in their macbook, it asks for this internal framework embedded. I thought above answer is somehow merging internal framework to umbrella framework but apparently even this answer didn't figure it out yet. It is still just linking and not umbrella. – adev Feb 12 '18 at 01:57
  • the main difference for dynamic vs static is, for dynamic only linking happen will happen, so you will need to embed the framework, and if the framework file doesn't exist, it will prompt for error and telling you the framework is not found. But for static framework, the whole mach-o is included in your compiled binary, the actual user don't even know the sub framework name or its existence, if the user see the error looking for the sub framework, your setup is wrong unfortunately – vk.edward.li Feb 12 '18 at 04:11
  • @vk.edward.li, I was finally able to get this answer working. The script has an error and after fixing that I was able to generate umreblla.bundle. But do how do I load classes from that? Did you figure that out? this script now correctly creates a .bundle from framework. But I cant seem to load any classes from it. – adev Feb 12 '18 at 21:08
  • @adev this script has functionals both remove unsupported arches and detect which framework is static, may be it is too rough, I will update it later, hope you can tell me what errors you are occurred – MinamiTouma Feb 13 '18 at 08:43
  • @adev I will public a example project in my vacation few days later, it is not correct to make umbrella.bundle – MinamiTouma Feb 13 '18 at 13:29
  • @MinamiTouma, thanks, the error was the line `mv -f "$FRAMEWORK" "$BUNDLE_IN_ROOT"`. I changed it to `mv -f "$FRAMEWORK" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.bundle"` and it worked. – adev Feb 14 '18 at 03:57
  • @adev I released a demo project, may you use this script in a wrong way....I test that command, it is all right in my demo :) – MinamiTouma Feb 17 '18 at 03:25
  • @MinamiTouma, Thanks for putting the demo project. But my issue was different, I had swift files in my "SubAlpha.framework" and I need to access those in "AppDemo" project. But if I send "Umbrella.framework" to someone else, they are not able to use it since Xcode says "SubAlpha.framework" module required. Have you tried using Swift files in framework? – adev Feb 19 '18 at 19:34
  • @adev if you have this requirement, only I can say is it has a way to work it out, but really difficulty to explain exactly without face to face. I used swift classes in "SubAlpha.framework", but just accessed them from "Umbrella.framework". For "AppDemo", I tried use Obj-C classes in "SubAlpha", I think swift can use the same solution. Though those steps recorded in my blog, I strongly recommend not to do this, may you can modify your whole project structure – MinamiTouma Feb 22 '18 at 13:03
  • @adev you need to create "proxy" classes for your inner framework, because Xcode will try to link your inner framework to your "AppDemo" directly whenever you try to access inner framework, which will always fail since inner framework doesn't not exists anymore, but is included inside the binary of Umbrella.framework – vk.edward.li Mar 05 '18 at 19:12
  • 1
    @vk.edward.li, Actually I found the exact reason for this and it is an Apple bug https://bugs.swift.org/browse/SR-5694. Reason is swift-modules cache absolute path and once I distribute umbrella framework, it still has path as per my machine. Here is a related discussion https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170911/039773.html. so all the above answers are wrong for swift and we cant do anything until apple fixes it. – adev Mar 06 '18 at 22:00
5

I tried doing what @Segev suggested, but I kept receiving the error that the embedded framework files were missing.

However, doing some additional config I managed to make it work!

Here's what I did:

  1. Add the embedded framework header file inside the umbrella framework project.
  2. In the umbrella header add: import "EmbeddedFramework.h"
  3. Then import the umbrella framework into the desired project and you wont get more errors

You will see that the umbrella framework 'Headers' folder will include the 'EmbeddedFramework.h' file

You can see an example project here:

Hope this helps

Adriana Pineda
  • 9,219
  • 1
  • 12
  • 14
  • I am still getting errors.The sample app is not able to find EmbeddedFramework.h defined in the umbrella header . – IOSDevops Mar 13 '17 at 09:54
  • @Sree thanks, I made some changes and you should be able to run the projects without errors. Please pull the master branch for each project and feel free to reach out if you need more help. – Adriana Pineda Mar 26 '17 at 17:58
  • ,I am having some problems with GoLang at my end and cannot generate the Sample.Framework.Can you please add the Sample.Framework to your Umbrella framework and update please. – IOSDevops Apr 23 '17 at 14:52
  • Sorry I hadn't seen your question. Were you able to generate the framework? – Adriana Pineda May 28 '18 at 05:03