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

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.


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.


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.


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年的文章