23

I'm trying to create a framework that keeps some common code I use around in my projects. I found online tutorials and managed to create a framework but I still have one problem related to resources (xibs, images, etc).

Let's say I have a MainViewController.xib which has a UIImageView using a test.png. All these are in my framework package.

When I use the framework in another project, I add it to the "Copy Bundle Resources" build phase. The problem is that the xib is only accessible using a path like dummy.framework/Resources/MainViewController.xib and the UIImageView inside can't load test.png.

It seems that the UIImageView will try to load the png from the root of the bundle, and not from the relative folder where the xib is also stored.

Has anybody managed to create a framework with code and resources and use it in another project?

Andrei Stanescu
  • 6,353
  • 4
  • 35
  • 64
  • 1
    Hi andrei, can you please tell me how you add the .bundle file into framework directory... i am not able to do that. there may be some mistake in my shell script. – Apple Apr 07 '15 at 06:50

7 Answers7

14

I know this is an old thread, but to keep new people from making the same mistakes here is a note:

As of Xcode 6 and iOS8, Apple has a fully supported first party Dynamic Framework solution built in. For more information look here: https://developer.apple.com/library/ios/documentation/DeveloperTools/Conceptual/WhatsNewXcode/Articles/xcode_6_0.html#//apple_ref/doc/uid/TP40014509-SW14

But the short of it is, create a new project and choose the Cocoa Touch Framework template.

Ryan Poolos
  • 18,421
  • 4
  • 65
  • 98
  • All good and well, but have you actually managed to include image resources in a Dynamic Framework created in Xcode 6. I've tried everything myself, but can't get it working! Seems to be very little documentation from Apple on it. – simonthumper Oct 27 '14 at 15:53
  • 10
    Yes. its been fairly simple but maybe not obvious. Simple include your images in your framework like normal, but to load them, you need to reference the bundle of your framework. So `[NSBundle bundleWithIdentifier:@"com.<#company#>.<#framework#>"];` – Ryan Poolos Oct 27 '14 at 18:38
  • You don't need a fool around with creating a bundle or anything. Xcode's framework handles that for you. But you do have to load them from that default bundle. I find it useful to create an NSBundle category with a shortcut for the frameworks category. – Ryan Poolos Oct 27 '14 at 18:38
  • Also, for future issues, you'll have a lot of success finding help and documentation if you search for Mac frameworks or Cocoa Touch frameworks. They're new to us iOS developers, but they're not new to the world. There was lots of a good Mac documentation out there that aligned and thats how I found out that they create their own bundle. – Ryan Poolos Oct 27 '14 at 18:39
  • 3
    Yeah got this working with `imageNamed:inBundle:compatibleWithTraitCollection`. Sending inBundle: `[NSBundle bundleWithClass[self class]]` or whatever is appropriate given where I'm using the image. – simonthumper Oct 27 '14 at 18:43
  • Yes, that method is AWESOME! Sadly its iOS8 only. But if you go with Apple's new Dynamic Framework option you're tied to iOS8 anyway. And I think its safe to make that decision for new developments. – Ryan Poolos Oct 28 '14 at 14:41
  • Crap. you're right. About the method anyway... I was of the opinion that frameworks were compatible with iOS7 however? That was certainly how Apple advertised them. Unfortunately working for clients who expect their products to still run on iOS7 my hands are tied as far as only supporting iOS8. But thanks for the help! – simonthumper Oct 28 '14 at 21:12
  • Apple's frameworks definitely do not work on iOS7. But you'll need the same bundle mindset to support assets with a static library. This might help https://github.com/jverkoey/iOS-Framework – Ryan Poolos Oct 28 '14 at 22:41
  • Do they not work, or do they just refuse to submit them? Because We've had projects with Apple's frameworks working on iOS 7 devices under our enterprise license... – simonthumper Oct 28 '14 at 22:51
  • Everytime I try to get it to build I get an error that says Dynamic Frameworks aren't supported in this version of iOS. – Ryan Poolos Oct 28 '14 at 23:34
  • If you can show me how to get a framework working with iOS7 you would make my year – Ryan Poolos Oct 28 '14 at 23:35
  • 1
    Yeah we hadn't actually tried submitting until now. Turns out you can get it running, and even distribute enterprise builds for iOS7 using the new frameworks. But when apples checking your build for upload to the store it rejects it. A couple of guys from work were at WWDC where they said it'd work on iOS7, looked back and they'd removed 45 minutes of video from the talk on it! – simonthumper Oct 29 '14 at 17:05
  • 1
    Hi Ryan. Any suggestions how to use the image from a framework within a storyboard or xib? When I try, I get an error: **Could not load the "" image referenced from a nib in the bundle with identifier ""** – TheNeil Apr 25 '19 at 01:53
  • Don't have any relevant experience with Storyboards. Try to keep those out of frameworks and rely on code only instead. Probably worth creating your own question on StackOverflow for that specific question. – Ryan Poolos Apr 25 '19 at 16:02
8

I created a framework target and to access images inside its asset catalog I did the following:

UIImage *img = [UIImage imageNamed:@"IMAGE_NAME_HERE" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:[UITraitCollection traitCollectionWithDisplayScale:[UIScreen mainScreen].scale]];
devinross
  • 2,816
  • 6
  • 34
  • 34
  • Thanks a lot you I've waste a lot of time trying to find a solution for it. and this one worked :) – Ismail Mar 03 '16 at 11:48
  • 5
    This one's for dynamic frameworks though, not static ones, since for static frameworks `bundleForClass:self.class` returns the main app bundle. – FreeNickname Mar 14 '18 at 09:58
5

I don't manually manage frameworks anymore but use and recommend CocoaPods instead.


Original answer:

  1. Use the fake framework @wattson12 mentioned. It will compile and save the resources as well.
  2. Inspired by this script, add this to your target to copy the resources to your app:

    SOURCE_PATH="${TARGET_BUILD_DIR}/MYFramework.framework/Resources/"
    TARGET_PATH="${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MYFrameworkResources.bundle"
    mkdir -p $TARGET_PATH
    cp -R $SOURCE_PATH $TARGET_PATH
    

You could also just drag the framework to your Copy Resources step, but then you'll be adding unnecessary headers and compiled code as well.

Edit

To use these resources from IB, for instance a png file, replace:

MyImage

by:

MYFrameworkResources.bundle/MyImage.png

It will preview a broken image icon but will work when running.

Load a Nib from code:

[NSBundle loadNibNamed:@"MYFrameworkResources.bundle/MyNib" ...

Finally you can add these methods in a NSBundle category to ease access to Nib resources that my be in your main bundle or in MYFrameworkResources.bundle:

@implementation NSBundle (MyCategory)

+ (NSString *)pathForResource:(NSString *)name
                       ofType:(NSString *)extension
{
    // First try with the main bundle
    NSBundle * mainBundle = [NSBundle mainBundle];
    NSString * path = [mainBundle pathForResource:name
                                           ofType:extension];
    if (path)
    {
        return path;
    }

    // Otherwise try with other bundles
    NSBundle * bundle;
    for (NSString * bundlePath in [mainBundle pathsForResourcesOfType:@"bundle"
                                                          inDirectory:nil])
    {
        bundle = [NSBundle bundleWithPath:bundlePath];
        path = [bundle pathForResource:name
                                ofType:extension];
        if (path)
        {
            return path;
        }
    }

    NSLog(@"No path found for: %@ (.%@)", name, extension);
    return nil;
}

+ (NSArray *)loadNibNamed:(NSString *)name
                    owner:(id)owner
                  options:(NSDictionary *)options
{
    // First try with the main bundle
    NSBundle * mainBundle = [NSBundle mainBundle];
    if ([mainBundle pathForResource:name
                             ofType:@"nib"])
    {
        NSLog(@"Loaded Nib named: '%@' from mainBundle", name);
        return [mainBundle loadNibNamed:name
                                  owner:owner
                                options:options];
    }

    // Otherwise try with other bundles
    NSBundle * bundle;
    for (NSString * bundlePath in [mainBundle pathsForResourcesOfType:@"bundle"
                                                          inDirectory:nil])
    {
        bundle = [NSBundle bundleWithPath:bundlePath];
        if ([bundle pathForResource:name
                             ofType:@"nib"])
        {
            NSLog(@"Loaded Nib named: '%@' from bundle: '%@' ", name, bundle.bundleIdentifier);
            return [bundle loadNibNamed:name
                                  owner:owner
                                options:options];
        }
    }

    NSLog(@"Couldn't load Nib named: %@", name);
    return nil;
}

@end

It will first look into your application bundle and then in MYFrameworkResources.bundle, etc.

Community
  • 1
  • 1
Rivera
  • 10,792
  • 3
  • 58
  • 102
  • Thanks, but interface builder still won't properly detect the resources – Andrei Stanescu Dec 19 '12 at 07:46
  • 1
    Are you having problems with the images? (prepending the path is enough) or with the Nibs? You can also check your build directory to see where the resources are getting copied inside your app. – Rivera Apr 01 '14 at 01:24
4

In order to load an image from a dynamic framework in Swift 3:

UIImage(named: "name", in: Bundle(for: type(of: self)), compatibleWith: nil)

Luca Torella
  • 7,974
  • 4
  • 38
  • 48
1

Basic frameworks do not include most kinds of resources, to include resources use this "fake" framework library

You end up with a .embeddedframework folder which contains the actual framework, but also includes any resources which you add to the Copy Bundle Resources build phase. Ive used it to include xibs, core data models, images, and plists

wattson12
  • 11,176
  • 2
  • 32
  • 34
  • Yes, but if you have (in the framework) a xib that uses a resource that is also in the framework (a image let's say). Will you be able to load the xib in the test application? – Andrei Stanescu Aug 29 '12 at 14:57
  • I dont have that project in front of me but I'm pretty sure that would be ok, give it a shot... – wattson12 Aug 29 '12 at 15:04
  • I've concluded that what I want to achieve is not really possible. Due to Interface Builder's inability to display images that are in subfolders (they work on runtime though) it's really not feasible to deliver any resources in a bundle form. Best way would be to simply add a zip along with the static library and the user can use that folder in its project. – Andrei Stanescu Aug 30 '12 at 09:17
  • You can reference resources nested inside subfolders from IB by adding the path: MyResources.bundle/image.png. It won't look nice inside IB but it will work – Rivera Dec 13 '12 at 07:43
-1

Short answer: you can't.

When you compile the app, the bundle is generated with the resources of the "main" project, ignoring the resources in linked frameworks. This includes images, xib's, plist's, etc. (anything that isn't a source file)

What you need to do is add those resources to your main project so that they are available for use inside your application, not the happiest way around it but it'll work.

Ismael
  • 3,927
  • 3
  • 15
  • 23
-1

As of Xcode 7 we can now use Asset Catalogs. In my experiments I've found that u can reference files in the same workspace bit different projects removing the need to micro manage assets in build phases or worrying about targets.

leerie simpson
  • 186
  • 1
  • 3