@@ -25,9 +25,9 @@ Table of Contents
2525 - [ Overview] ( #overview )
2626 - [ Create the Static Library Target] ( #static_library_target )
2727 - [ Create the Framework Distribution Target] ( #framework_distribution_target )
28+ - [ Resources and Bundles] ( #resources )
2829- [ Adding the Framework to a Third-Party Application] ( #third_parties )
2930- [ Developing the Framework as a Dependent Project] ( #first_parties )
30- - [ TODO: Resources and Bundles] ( #resources )
3131- [ License] ( #license )
3232
3333<a name =" existing_solutions " />
@@ -171,14 +171,14 @@ as expected). <a href="#first_parties">Jump to the dependent project walkthrough
171171Create the Static Library Target
172172--------------------------------
173173
174- ### Step 1: Create a new "Cocoa Touch Static Library" project.
174+ ### Step 1: Create a New "Cocoa Touch Static Library" Project
175175
176176![ ] ( https://github.com/jverkoey/iOS-Framework/raw/master/gfx/newstaticlib.png )
177177
178178The product name will be the name of your framework. For example, ` Serenity ` will generate
179179` Serenity.framework ` once we've set up the project.
180180
181- ### Step 2: Create the primary framework header.
181+ ### Step 2: Create the Primary Framework Header
182182
183183Developers expect to be able to import your framework by importing the ` <Serenity/Serenity.h> `
184184header. Ensure that your project has such a header (if you created a new static library then there
@@ -210,7 +210,7 @@ copied to the correct location in the copy headers phase.
210210
211211![ ] ( https://github.com/jverkoey/iOS-Framework/raw/master/gfx/publicheaders.png )
212212
213- ### Step 3: Update the public headers location
213+ ### Step 3: Update the Public Headers Location
214214
215215By default the static library project will copy private and public headers to the same folder:
216216` /usr/local/include ` . To avoid mistakenly copying private headers to our framework we want to ensure
@@ -220,13 +220,13 @@ headers" and then set the "Public Headers Folder Path" to "Headers" for all conf
220220
221221![ ] ( https://github.com/jverkoey/iOS-Framework/raw/master/gfx/publicheadersconfig.png )
222222
223- ### Ongoing step : Adding new sources to the framework
223+ ### Ongoing Step : Adding New Sources to the Framework
224224
225225Whenever you add new source to the framework you must decide whether to expose the .h publicly or
226226not. To modify a header's scope you will follow the same process as Step 2. By default a header's
227227scope will be "Project", meaning it will not be copied to the framework's public headers.
228228
229- ### Step 4: Disable code stripping
229+ ### Step 4: Disable Code Stripping
230230
231231We do not want to strip any code from the library; we leave this up to the application that is
232232linking to the framework. To disable code stripping we must modify the following configuration
@@ -411,6 +411,133 @@ that you are grabbing the correct .framework when you are preparing to distribut
411411You can now drag the .framework elsewhere, zip it up, upload it, and distribute it to your
412412third-party developers.
413413
414+ <a name =" resources " />
415+ Resources and Bundles
416+ =====================
417+
418+ To distribute resources with a framework we are going to provide the developer with a separate
419+ .bundle that contains all of the strings and resources. This provides a number of advantages over
420+ including the resources in the .framework itself.
421+
422+ - Encapsulation of resources. We can scope resource loading to our framework's bundle.
423+ - Easy to add bundles to projects.
424+ - The developer doesn't have to copy the entire .framework into their application.
425+
426+ The hard part about bundles is creating the target. Xcode's bundle target doesn't actually create a
427+ loadable bundle object, so we have to do some post-build massaging of the bundle. It's important
428+ that we create a bundle target because we need to create the bundle using the Copy Bundle Resources
429+ phase that will correctly compile .xib files (a Copy Files phase does not accomplish this!).
430+
431+ ### Step 1: Create the Bundle Target
432+
433+ In the framework project, create a new bundle target. You will need to name the bundle something
434+ different from your framework name or Xcode will not let you create the target. I've named the
435+ target SerenityResources. We will rename the output of the target to Serenity.bundle in a following
436+ step.
437+
438+ ![ ] ( https://github.com/jverkoey/iOS-Framework/raw/master/gfx/newbundletarget.png )
439+
440+ Ensure that the Framework setting is set to "Core Foundation".
441+
442+ ![ ] ( https://github.com/jverkoey/iOS-Framework/raw/master/gfx/newbundletarget2.png )
443+
444+ ### Step 2: Clean up the Bundle Target Settings
445+
446+ By default the bundle will only show build settings for Mac OS X. It doesn't really matter what it
447+ builds for because the bundle isn't actually going to have any code in it, but I prefer to have
448+ things nice and consistent. Open the bundle target settings and delete the settings for
449+ Architectures, Base SDK, and Build Active Architecture Only.
450+
451+ ![ ] ( https://github.com/jverkoey/iOS-Framework/raw/master/gfx/bundlesettings.png )
452+
453+ This is also when you should change your bundle target's product name to the name of your framework
454+ rather than the target name. To do so, click your project in the Xcode file explorer, select the
455+ bundle target, and then click the Build Settings tab. Search for Product Name and then replace
456+ the value of Product Name with the name of your framework.
457+
458+ ![ ] ( https://github.com/jverkoey/iOS-Framework/raw/master/gfx/serenityproductname.png )
459+
460+ ### Ongoing Step: Add Resources to the Bundle Target Copy Files Phase
461+
462+ Whenever you add new resources that you want to include with your framework you need to add it to
463+ the bundle target that you created.
464+
465+ ![ ] ( https://github.com/jverkoey/iOS-Framework/raw/master/gfx/newbundleresource.png )
466+
467+ ### Step 3: Create the Bundle Folder Structure
468+
469+ We are going to create a "Loadable Bundle" as outlined in the following Apple document:
470+
471+ https://developer.apple.com/library/mac/#documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html
472+
473+ Our bundle is only going to be used for resource loading, so our bundle structure will need to
474+ resemble the following:
475+
476+ ```
477+ Serenity.bundle/
478+ Contents/
479+ Info.plist
480+ Resources/
481+ *.png
482+ *.nib
483+ *.lproj/
484+ <Localized files>
485+ ```
486+
487+ By default the bundle target will copy all of the files to the root directory, so we're going to
488+ move them around with the following script. Add a new Run Script phase and paste the following
489+ script into it.
490+
491+ ``` bash
492+ rm -rf " ${BUILT_PRODUCTS_DIR} /${PRODUCT_NAME} .${WRAPPER_EXTENSION} /Contents"
493+ mkdir -p " ${BUILT_PRODUCTS_DIR} /${PRODUCT_NAME} .${WRAPPER_EXTENSION} /Contents/Resources"
494+ mv " ${BUILT_PRODUCTS_DIR} /${PRODUCT_NAME} .${WRAPPER_EXTENSION} /Info.plist" " ${BUILT_PRODUCTS_DIR} /${PRODUCT_NAME} .${WRAPPER_EXTENSION} /Contents"
495+ mv ${BUILT_PRODUCTS_DIR} /${PRODUCT_NAME} .${WRAPPER_EXTENSION} /* .png ${BUILT_PRODUCTS_DIR} /${PRODUCT_NAME} .${WRAPPER_EXTENSION} /Contents/Resources/
496+ mv ${BUILT_PRODUCTS_DIR} /${PRODUCT_NAME} .${WRAPPER_EXTENSION} /* .nib ${BUILT_PRODUCTS_DIR} /${PRODUCT_NAME} .${WRAPPER_EXTENSION} /Contents/Resources/
497+ mv ${BUILT_PRODUCTS_DIR} /${PRODUCT_NAME} .${WRAPPER_EXTENSION} /* .lproj ${BUILT_PRODUCTS_DIR} /${PRODUCT_NAME} .${WRAPPER_EXTENSION} /Contents/Resources/
498+ ```
499+
500+ Modify the script accordingly as you work with more file types. There is likely a simpler way to
501+ write this script so that it will capture all files.
502+
503+ ### Step 4: Add the Bundle Target to your Aggregate Target
504+
505+ Whenever we build the framework for distribution we likely also want to build the bundle. Add the
506+ bundle target to your aggregate target's dependencies.
507+
508+ ![ ] ( https://github.com/jverkoey/iOS-Framework/raw/master/gfx/bundledependency.png )
509+
510+ ### Step 5: Loading Bundle Resources
511+
512+ In order to load bundle resources, we must first ask the third-party developer to add the .bundle to
513+ their application. To do so they will simply drag the .bundle that you distributed with the
514+ .framework to their project and ensure that it is copied in the copy files phase of their app
515+ target.
516+
517+ ![ ] ( https://github.com/jverkoey/iOS-Framework/raw/master/gfx/addbundle.png )
518+
519+ To load resources from the bundle we will use the following code:
520+
521+ ``` obj-c
522+ // Load the framework bundle.
523+ + (NSBundle *)frameworkBundle {
524+ static BOOL didAttemptLoad = NO;
525+ static NSBundle* frameworkBundle = nil;
526+ if (!didAttemptLoad) {
527+ didAttemptLoad = YES;
528+ NSString* mainBundlePath = [[ NSBundle mainBundle] resourcePath] ;
529+ NSString* frameworkBundlePath = [ mainBundlePath stringByAppendingPathComponent:@"Serenity.bundle"] ;
530+ frameworkBundle = [[ NSBundle bundleWithPath: frameworkBundlePath ] retain] ;
531+ }
532+ return frameworkBundle;
533+ }
534+
535+ [UIImage imageWithContentsOfFile: [[[ self class] frameworkBundle] pathForResource:@"image" ofType:@"png"]] ;
536+ ```
537+
538+ You can see an example of loading a resource from within the framework in the Widget object in the
539+ included Serenity framework.
540+
414541<a name="third_parties" />
415542Adding the Framework to a Third-Party Application
416543=================================================
@@ -425,6 +552,12 @@ This is the easy part (and what your third-party developers will have to do). Si
425552
426553Import your framework header and you're kickin' ass.
427554
555+ ### Resources
556+
557+ If you're distributing resources with your framework then you will also send the .bundle file to the
558+ developers. The developer will then drag the .bundle file into their application and ensure that
559+ it's added to the application target.
560+
428561```obj-c
429562#import <Serenity/Serenity.h>
430563```
@@ -475,6 +608,18 @@ can obviously choose whatever practice suits your needs.
475608#import < Serenity/Serenity.h>
476609```
477610
611+ ### Step 4-b: Adding Resources
612+
613+ If you are developing resources for your framework you can also add the bundle target as a
614+ dependency.
615+
616+ ![ ] ( https://github.com/jverkoey/iOS-Framework/raw/master/gfx/bundledependency2.png )
617+
618+ You must then add the bundle to the Copy Bundle Resources phase of your application by expanding
619+ the products folder of your framework product and dragging the .bundle into that section.
620+
621+ ![ ] ( https://github.com/jverkoey/iOS-Framework/raw/master/gfx/bundlecopy.png )
622+
478623### Step 5: Build and Test
479624
480625Build your application and verify a couple things:
@@ -483,13 +628,6 @@ Build your application and verify a couple things:
483628- Your framework should be linked into the application.
484629- You shouldn't get any compiler or linker errors.
485630
486- <a name =" resources " />
487- TODO: Resources and Bundles
488- ===========================
489-
490- Coming soon.
491-
492-
493631<a name =" license " />
494632License
495633=======
0 commit comments