21

This is for Xcode 4.5.x iOS armv7 armv7s and the sim and specifcially about Xcode project setup / project build setup:

I have a project "A" that is an app in the app store. I have a project "B" that is a library that will be used in A as a dependency, but also distributed as a 3rd-party library to other companies to use in their apps. (other companies' 3rd party apps are represented in this case as "Y").

Here are the requirements:

  • Must be able to run "A" in debug mode, and of course debug the nested "B" project concurrently, in the same build/session.
  • From "A" I can CMD+Click on a method signature from "B" and jump right into that src file, where I'm free to edit and then recompile, as if it were from the same project.
  • A dev "X" at some other company must be able to easily drag our library "B" into his project "Y", where "B" is a static library with only the required header files exposed. "Y" of course calls methods from a subset of "B"'s actual header files. Only the files from this subset should be included in distribution for Dev "X".
  • Dev "X" should not need to modify anything at all in his Xcode project, just drag the folder for "B" (which contains the static lib and subset of header files) into his project and click "Copy resources, create references, etc".
  • I need to be able to generate the static library build of "B" easily, based on the same files I have been editing this whole time as I iterate and debug this project "B" inside of its dependent project "A".
  • "B" has no resources aside from source code -- there are no image assets, xibs, or anything like that.
  • From "B", I click "Archive" and Poof! there's a static lib (must be fat binary, by that I mean it works on the simulator + armv7 + armv7s, please!!) with the essential header files ready to be distributed.
  • All of this must be app store approval-friendly
  • Also this must be reliable. It's no good if I have to keep coming back to make a lot of config changes every time I add one file.

UPDATE:
* MOST IMPORTANT: This needs to be a repo I can check out that is a full end-to-end template of what am looking for, and I need to be able to open up Xcode 4.5.2+ and click play and see this thing build, pain-free.

500 points to anyone who can provide me a template project that demonstrates everything I have described above, "A", "B", and "Y" (with "B" static lib used as a dep). All i need is a set of skeleton projects ("A", "B" (nested inside "A"), and "Y") that shows how this can be done. Please don't hold back the answer until the bounty is posted. If it meets my requirements, I'll make sure you get my bounty points.

I'm somewhat worried that with the limitations of Xcode that this is not even possible in a way that is not a complete hassle. Please prove me wrong.

UPDATE: I decided I don't care about armv6 anymore. Goodbye, armv6. Extra credit if you can get armv6 rolled into the dist output along with armv7, armv7s, i386/simulator.

P.S. I promise that I will be reasonable awarding the points. I'm not looking to withhold them on a technicality. If you make my life dramatically less painful in this one area, I will gladly award you the points.

jpswain
  • 14,642
  • 8
  • 58
  • 63

3 Answers3

6

This will not be possible in Xcode alone. You will need some build Scripts (which you can call from within Xcode of course) because of the compilation target switch (simulator, device, etc.).

I think you will have to add additional distribution headers to a "Copy files" build step at least. But other modifications should not be necessary if you change something.

I did something like this for libturbojpeg, see https://github.com/dunkelstern/libturbojpeg-ios for reference. It currently puts a fat library into "lib" if you call the "build.sh" file from the terminal, but omits distribution headers. In the case of libturbojpeg I needed 2 project files because each target compiles a different subset of assembler files into the library (better do not look at the assembler makefile stuff). To compile you will need a recent version of NASM as the version apple ships is ancient (get it with brew). I will post a template for such a library build project on the same account shortly. (Will edit or comment if done here with appropriate links)

Basically it works like this:

  1. Create a build script that calls xcodebuild for each needed platform target
  2. The Xcode library project has to contain a script to drop the built libraries in a directory the build script can find
  3. Additional headers have to be copied by a "Copy files" target action
  4. The build script has to merge all library builds with lipo
  5. Add the build script as "Run Script" target to your build, but be aware you do not create an infinite loop (or just call it from terminal for creating a release build)
  6. In your main project add the library subproject

You can then distribute the output dir with the copied header files and the lipo merged universal library and use the library normally as subproject in your workspace as you would do normally (it builds and links only the needed libs then, not the universal lib but that should be no problem)

This does not in fact solve the problem of creating DSYM files for the library. But normally the debugging symbols should be in the library itself when building a debug build. It will strip the debugging symbols on release build and you will have no DSYM then.

Link to example project: https://github.com/dunkelstern/StaticLibraryTemplate

Dunkelstern
  • 1,624
  • 12
  • 18
  • Thanks for answering, looks promising at first glance. I'll dive into it later today once I have a free moment. If this solves my problems, I'll award you the points. It says "12 more hours" till I can offer the bounty anyways. Thanks!! – jpswain Nov 08 '12 at 18:17
  • So I was just going thru your libturbojpeg projects. Looks like some baller C-code. I know how to use lipo to combine the builds, but really above all else want a way to easily run my static lib project both as normal source code inside the dependent project and as a static library equally well and easily swappable. Do you happen to have an example that incorporates libturbojepg in that way? Thanks!! Jamie – jpswain Nov 15 '12 at 07:29
  • Just drop the project file for your library build into your project, do not create a workspace but drop it into your project tree. It won't work with the libturbojpeg project because it has 2 distinct architectures. Then create a dependency in your main project build phase and insert the *.a file in the link step. Like this: http://media.dunkelstern.de/images/Xcode_library_project.png – Dunkelstern Nov 15 '12 at 12:47
  • Oh and make sure you don't have the project open in another instance of Xcode at the same time, it won't build then because the other instance locks the project – Dunkelstern Nov 15 '12 at 12:54
  • Hi @Dunkelstern, I can't get the "ProjectUsingLibrary" to see the files in the nested "StaticLibrary" library. I tried adding a "sayHello" method to StaticLibrary and using it from didBecomeActive in ProjUsingLib, but I get a build error: ...../StaticLibraryTemplate/ProjectUsingLibrary/ProjectUsingLibrary/AppDelegate.m:12:9: fatal error: 'StaticLibrary.h' file not found #import "StaticLibrary.h" ^ 1 error generated. :-( Can you provide an example that shows it in action, even if it's just a simple sayHello method call from the ProjectUsingLibrary to the StaticLibrary. Thanks! – jpswain Nov 19 '12 at 06:07
  • Ok, here you go. The ProjectUsingLibrary was missing the Header search path. Implemented a simple @property and its usage in ViewController.m – Dunkelstern Nov 19 '12 at 16:28
  • I finally got a chance to look over your project some more! Thanks so much, it's 90% of the way there. My only real complication is that I don't want to ever check in the modified header search path or non-public src files, etc to my company's repo, but I'm going to experiment a bit and see if I can find a way to factor that out and get the workflow perfect. This is a HUGE gain tho, thanks again, and sorry for my delay in awarding the points. – jpswain Nov 22 '12 at 23:40
  • Using git? just .gitignore the Library directory or include it as another git repo (submodule) and build a smudge/clean filter for the project file to exclude the header search path on checkin: http://git-scm.com/book/ch7-2.html#Keyword-Expansion – Dunkelstern Nov 23 '12 at 09:17
  • Yes we are using git! That looks awesome! I have never seen smudge filters before, and I thought I knew git pretty well! Thanks @Dunkelstern !! – jpswain Nov 23 '12 at 18:55
3

I'm using https://github.com/jverkoey/iOS-Framework to achieve something quite similar to your needs. Give him all the credit, I'm just summarizing how I do it.

Create a static library as usual plus these tweaks:

  • Add a copy files phase to copy the headers. I don't use a proper "Copy headers" phase because I read somewhere it's not recommended for iOS static libraries.
    • Destination: Products Directory
    • Subpath: ${PROJECT_NAME}/Headers
  • Change a few settings:
    • "Dead Code Stripping" => No (for all settings)
    • "Strip Debug Symbols During Copy" => No (for all settings)
    • "Strip Style" => Non-Global Symbols (for all settings)
  • Add a run script to prepare a framework with the library:
    • Use the script prepare_framework.sh.

You can use the static library project in your app: drag it to your app project, add the .a library as dependency and link with it. You can debug the library along with your app, step into methods, navigate to symbol definitions, etc.

The prepared framework will be used to distribute a binary version:

In the same static library project add an Aggregate target:

  • Add the static library as dependency.
  • Add a run script phase to build the missing architectures. Use the script build_framework.sh.

The script guess what's the other platform and use xcodebuild to compile it. Then use lipo to create a fat binary with all the architectures. The destination of the fat static library will be the framework tree we created before. The final framework is copied to the products folder in the build folder.

With this approach you can:

  • Build and debug your app with the static library as nested project.
  • Build a distributable version of the library in one step with the headers embedded in a framework-like bundle. The built framework is located in the project Products directory.
  • Use Xcode Archive function. For some reason final files are not copied to the archive location. You can use it to build a stripped version of the framework.

Feel free to clone a project using this technique to package the library: json-framework fork. I have modified slightly the scripts, check my fork of iOS-framework.

Regarding armv6, I guess you need and old iOS SDK 4.3 and add manually the literal armv6 to the list of valid architectures and actual architectures. I don't have and old SDK to test it right now.

djromero
  • 19,551
  • 4
  • 71
  • 68
  • Thanks man! I'll look forward to trying both your solution and @Dunkelstern's solution tonight after work. As for armv6, I don't really care enough about it to do any more brain damage there. Apple is pretty much forcing everyone to abandon that arch anyways by removing it from Xcode. – jpswain Nov 15 '12 at 22:05
  • This thing is pretty cool, not sure I'm ready to switch it over to a framework just yet, but I really like it at the same time. I can especially see how it would be great for projects that use a lot of resources like Xibs, images, etc. – jpswain Nov 22 '12 at 23:41
  • 1
    I didn't include a bundle of resources (xibs, pngs, etc.) in my answer, just the 'fake' iOS framework. In fact, this framework has exactly the *same* static library created with the *same* lipo tool and run script (there is no other way). It has the advantage of automatically configuring the headers path and showing up as a bundle in Xcode and Finder. It's a no brainer IMHO. – djromero Nov 23 '12 at 07:22
  • OK, I was trying it out, and your forked change says "- Make it work with build & archive (it doesn't archive, just build a stripped version)", but I cannot get the framework to archive at all. I get this: "cp: /Users/tuna/Library/Developer/Xcode/DerivedData/DependentApp-ffmwzfyurwbnswgljtisvuzftivs/Build/Intermediates/ArchiveIntermediates/Framework/BuildProductsPath/Release-iphoneos/SerenityHeaders/: No such file or directory" Can you tell me the sequence of steps that is supposed to work with your fork of the iOS-frameowrk project? Thanks! – jpswain Nov 23 '12 at 08:04
  • Are you using my forks? The original didn't work with Archive. I didn't update the Serenity sample (IIRC), look my json-framework fork. – djromero Nov 23 '12 at 08:09
  • I'm using "commit 89ff819f653467e8ef1f393d42b92e92b6bba3a6" from iOS / serenity. Is it not supposed to be the authoritative example? How can it build and archive the lib for distribution? – jpswain Nov 23 '12 at 08:12
  • So I'm looking at DisplayPretty here, the JSON example one, and still the same thing, I can't get it to do anything in terms of distributing the build. I can launch and run the demo app with the embedded library no problem, but nothing about the "distribute a static library part" works. – jpswain Nov 23 '12 at 08:21
  • Open SBJson.xcodeproj and just run Product > Archive. In DerivedData you'll find sbjson-ios.framework. I'll update the sample in iOS-framework, eventually. – djromero Nov 23 '12 at 08:26
2

Cocoapods covers your needs. Although the standard way to use it is to submit pod specs to a central git repo. It supports adding alternate repos for distribution, or manually creating them, see here. The advantages of using cocoapods would be that it both meets all your requirements and that it is becoming a very standard way of distributing libraries (e.g. used by companies such as facebook & stackmob) and open source (e.g. afnetworking). So, if you depend on 3rd party libraries now or in the future, chances are that cocoapods will help you handle that dependency.

combinatorial
  • 9,132
  • 4
  • 40
  • 58
  • Unfortunately, the need for CocoaPods is not something I want to impose upon 3rd-party clients. I personally haven't tried it yet, but it does sound like a cool idea. – jpswain Nov 22 '12 at 23:35