Hi, I'm Adam. Email me about: CTOs, Mobile/iOS, Project Management, and Development

Using shared libraries for iPhone with multiple projects

You would think this is easy, seeing as how it’s an essential, simple, part of any programming project. HA! You obviously haven’t used Apple’s shockingly bad IDE (Xcode) much, have you?

You can read the extremely widely-linked blog post – http://www.stormyprods.com/blogger/2008/11/using-static-libraries-with-iphone-sdk.html – which is incomplete, and doesn’t work properly if you want to do auto-updates when source changes (which, in most cases, you do). Unfortunately, it doesn’t give any pointers to making linked projects work. I don’t blame the author for stopping at that point, their solution “works”, but I wanted to get the full benefits of auto recompilation.

You can google to try and find some documentation from Apple. I can’t find any, although I can find lots of other people on forums asking for documentation on how to do it themselves.

Eventually I worked it out, after hours of trial and error, and reading as much of the Xcode docs as I could find that seemed to relate to what was going on.

  1. Make a new project to hold your shared library
  2. Put all the .m and .h files for your library in that project (drag/drop them to the groups and files list)
    • XCode “feature”: it will by default fail to copy the files, even though that means everything will break later on, when you delete the originals. You need to tick the “copy files if necessary” checkbox)
  3. Create a “Static Library” build target
  4. Drag the .m files *only* to the static library build target’s “sources” twisty
  5. DO NOT DELETE THE FILES FROM THE MAIN PROJECT (because you’ve just created references, not copies, although it doesn’t tell you that … so Xcode will silently delete them everywhere, and OS X still doesn’t support undelete; Sulka tells me that Undelete will be coming to OS X in the next version. Oh, how I laughed)
  6. DO delete the files from the main Build Target
    • (again, they’re not actually files there, just references, so “deleting” them merely gets rid of the reference, not the file)
    • is this another bug in XCode? It seems strange that you use the “delete” funciton to do something that isn’t really a delete
  7. Click the NEW build target (the static library) and right click and select “build”.
    • XCode “feature”: if you highlight the thing you want to build, and hit Cmd-B to build, it WILL NOT BUILD the selected target, it will instead build the main target and ignore the target you actually wanted built
    • The *only* way to make cmd-b build the thing you want is to change some of the options on the Target dropdown at top left of the window – but you’ll have to change it back and forth each time you want to build somethign different with cmd-b … there seems to be no context-sensitive Build command in Xcode (bug?)
  8. If you want to test that your static lib built correctly (hint: you probably do)
    1. Add the static library to the main target
      • XCode bug: you can add the static library to the “direct dependencies” section. This has no effect; don’t do it. The static library MUST be added to the “linked libraries” section, i.e. the bottom panel of the General tab of the Inspector for the Build Target
    2. Write some code that uses your static lib – I suggest putting it in the app delegate class that Xcode auto-generated when you created the new project – and try building the main target (you CAN use cmd-b for this … unless you switched targets above, in which case you’ll need to switch them back first). If you have added the static lib in “the wrong place” or created it “in the wrong way” then this will fail with missing class errors etc

    All that has achieved is to compile your source code. No, seriously – it is THAT HARD. Now you have to actually try using it from another project.

    This is where the StormyProds link takes the easy way out (which only partially works): it simply copies the .a file (generated by the compiler) to somewhere on your hard disk, and has you hard-link to that file from other XCode projects. This will work; but it will also get out of date rapidly, and cause all sorts of bugs if you forget to update it. It will never auto-compile when there are changes.

    What Apple wants you to do (and for once I agree with them :) – this makes sense for most developers who aren’t using an external build system) is to link the projects directly, so that changes in one cause the other to automatically recompile *if necessary*.

    NB: for all my disappointments with Xcode, this is one feature I think has been extremely well done – it handles “links” between discrete Xcode projects extremely well, and (although the menus and docs for it are a bit hard to find) it mostly Just Works.

    1. Link the two projects
      1. Following Apple’s instructions here: http://developer.apple.com/DOCUMENTATION/DeveloperTools/Conceptual/XcodeProjectManagement/040-Files_in_Projects/chapter_5_section_7.html#//apple_ref/doc/uid/TP40002666-CJBJHJCJ – i.e.:
      2. Open the project that will import the static library
      3. Go to the “Project” menu and select “Add to Project”
      4. Find the .xcodeproj file on your harddisk that representst the other project, and select it
      5. Check it worked:
        • At the top of Groups and Files panel, you should see a blue twisty for your project, and just inside it a blue twisty for your imported / linked project
        • Inside the second twisty MIGHT be a .app file (representing the main target of the imported project, which you won’t need – you MIGHT have created a project wihtout a main executable, or deleted the spec for it, but by default you’ll have this)
        • Inside the second twisty should be a .a file (representing the static library from the imported project, which you DO NEED)
    2. Make the secondary project import the compiled static library from the first project
      1. Drag the .a file from inside the second twisty, and drop it inside the “Link Binary with Libraries” section of the main Build Target of the main project
        • DO NOT drop it anywhere else in the Built Targets section – it will work, but will have no effect (you do that in the next step)
        • If you do it correctly, you should see the static library appear with the same icon as each of your included Frameworks (all iPhone projects have at least 2 Frameworks normally: UIKit.framework, Foundation.framework), and appear right next to them in the same twisty underneath Build Targets
        • ALSO open the inspector for your build target and add a second dependency in the main dependencies section to the static lib – NOT the app – from the imported project.
        • (if you don’t do this, you’ll see weird things like the build compiling for iPhone, but not for the Simulator, for no apparent reason)

    Finally … drag and drop the header files from the original project’s location in Finder into the new project’s source folders. Do NOT check the “copy files if necessary”. Xcode will do a reference / softlink, so that changes to the header files in the original project are picked up. NB: you will need to do a drag/drop each time you add a new header file to the original project.

    (You *might* be able to get away with putting all the header files in a sub-fodler of their own in the original project, and drag/dropping that FOLDER into the new project, but … I tried that, and it seemed to sometimes fail to notice the new header files, so I’m not sure I’d risk it).

    Some fun errors:

    Line Location Tool:0: “_OBJC_CLASS_$_ScoreUploader”, referenced from:

    Various causes of this useless error message:

    1. you added a class to the source project … but forgot to drag/drop that .m file into the “Compile Sources” part of the Target for the Static Library in the source project (or you did so, but you didn’t link your xcodeproj’s directly … but forgot to recompile the source project (linking the projects, as described above, triggers auto-rebuilds and you dont see this problem so often))
    2. you had all the source in one project originally … but haven’t deleted FROM YOUR HARD DISK the source files for the source inside the original, main project

    (this is IMHO another bug in Xcode: it silently leaves the files in place, by default, and yet silently (secretly) uses them when compiling – but ONLY when dealing with imported libraries, NOT when dealing with local compilation. Huh?)

    Line Location Tool:0: literal-pointer@__OBJC@__cls_refs@NSMutableList in libhiscorelib.a(XMLSuperParser.o)

    You haven’t added the static lib as a non-lib dependency

    Some things to be aware of

    The header files that you drag/dropped from one project to the other have NOT been copied to the new project and are therefore NOT real files – they are references.

    If you delete the original files (in the statically compiled lib project) then the references in all other projects will vanish; be careful!

    If you updarte the original files (in the statically compiled lib project) then the referneces in all ohter projects will automatically update (with no UI indication of this).

    Unlike most normal IDE’s, there is no obvious UI indication that the files are not inside the current project (you can check by doing cmd-i on any of the files and looking at the value of the “Full Path” field, but otherwise you won’t see a difference).

10 thoughts on “Using shared libraries for iPhone with multiple projects”

Comments are closed.