Xcode 6 iOS Creating a Cocoa Touch Framework - Architectures issues

The way I did it is similar to vladof, but hopefully a little simpler. I made the framework a subproject of the app project.

Framework project

  • Create a new iOS Cocoa Touch Framework. Call it MyLib. This will create a single MyLib.h
  • Add a simple Cocoa Touch Obj-C class, MyClass (.h & .m) and in the implementation of the .m, create a method that returns a string, - (NSString *)greetings;
  • In MyLib.h, add this near the bottom, #import "MyClass.h"
  • In the target's Build Phases/Headers section, Move MyClass.h from the Project section to the Public section.
  • Do a Build (cmd-B)
  • Close the the project

App project

  • Create a new Single View application project, either Swift or Obj-C. Call it MyApp.
  • From the Finder, drag your MyLib project file to the left hand organizer section of you MyApp window and make sure the insertion line is just below MyApp. This makes MyLib a subproject of MyApp. (It can still be used the same way in other projects)
  • Click on MyApp in the organizer and then select the MyApp target and select Build Phases.
  • In Target Dependancies, click the + sign and add MyLib.
  • In Link with Libraries, click the + sign and add MyLib.framework.

For an Obj-C app

  • In ViewController.m, add #import
  • In viewDidLoad, add the following lines:
  • MyLib *x = [[MyLib alloc] init];
  • NSLog(@"%@", x.greetings);
  • Run the project and you should see the message in the debug window. -

For a Swift app

  • In ViewController.swift, add import MyLib
  • in viewDidLoad, add the following lines:
  • var x: MyLib = MyLib()
  • println("(x.greetings())") -

By doing it this way, the app is dependent on the framework so if you make a change in the framework classes, you don't have to change targets to build the framework separately, it will just compile the framework first, then the app.


Try the following steps to create a workspace that contains a framework project and an app project.

Workspace:

  • Create a Workspace.

Framework project:

  • Create an iOS Cocoa touch Framework project inside Workspace.
  • Add a simple Objective C class MyClass (header .h and implementation file .m), and create a method - (void)greetings.
  • Go project Build Phases > Headers > Move MyClass.h from Project to Public.
  • Change scheme to framework project and choose iOS simulator, then build. (Choose iOS Device if the app that integrates this framework runs on device. I will continue to use simulator in this example)
  • You should have no issue building, the framework build is found in your Derived Data directory which you can find in Organizer.

App project:

  • Create a Swift Single View application inside Workspace.
  • Drag above iOS simulator framework build (Found in Debug-iphonesimulator or Release-iphonesimulator) to your project.
  • Create a bridging header to expose Objective C class methods to Swift.
  • Import MyClass.h in bridging header file.
  • Note that if MyClass definition is not found, then add framework Headers path of to Build Settings Header Search Paths.
  • Create instance of MyClass in viewDidLoad of ViewController.swift, then call greetings.
  • Add framework to Target > Embedded Binaries
  • Change scheme to app project and choose iOS simulator, then build.
  • You should be able to see greeting messages.

Note that above example demonstrates how to build an app that runs in simulator. If you need to create universal static library that runs on both simulator and devices, then general steps are:

  • Build library for simulator
  • Build library for device
  • Combine them using lipo

There are good references on the web about it, here for example.

Create universal binary for framework: navigate to framework Derived Data directory then /Build/Products, following command should help you create a universal binary in Products directory:

lipo -create -output "framework-test-01-universal" "Debug-iphonesimulator/framework-test-01.framework/framework-test-01" "Debug-iphoneos/framework-test-01.framework/framework-test-01" 

Note that framework-test-01 is my framework project name.


I've changed someone's script a bit to support all Simulator's architectures:

UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal

# make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"

# Step 1. Build Device and Simulator versions
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos  BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphonesimulator VALID_ARCHS="x86_64 i386" BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build

# Step 2. Copy the framework structure to the universal folder
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"

# Step 3. Create universal binary file using lipo and place the combined executable in the copied framework directory
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"

# Step 4. Convenience step to copy the framework to the project's directory
cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"

# Step 5. Delete temporary build directory in the project's directory
rm -rf "${PROJECT_DIR}/build"

# Step 6. Convenience step to open the project's directory in Finder
open "${PROJECT_DIR}"

Based on all the responses, the post on raywenderlich.com and the gist created by Chris Conway I came up with this.

Executing the following steps I was able to build a Cocoa Touch framework (including Swift and Objective-C files) that contains all architectures for both simulator and device:

  1. Create a new (Aggregate) target in your framework's project
  2. Under "Build Phases" select "Add Run Script" and copy the contents of this file
  3. Select the Aggregate target in the Scheme Selection drop down
  4. Build the target for the aggregate scheme

Hope it helps :)

UPDATE: Fixed an error in the gist where the paths in step #3 were incorrect. Thanks to Tokuriku!!

UPDATE In Xcode 7 and 8, click File>New>Target... and there select "Other" group to select Aggregate target