Using Open Source Static Libraries in Xcode 4
Using Open Source Static Libraries in Xcode 4
Xcode 4.0.1 allows us to more easily create and use third party libraries in iOS projects. I think the process is still more complicated than it needs to be. Xcode’s documentation suggests that it should automatically detect implicit dependencies and index
classes across workspaces but I have not found this to be the case. Here I’ll cover the steps I have found for creating and sharing code between projects and with other developers.
Background
Workspaces:
Xcode 4 introduced the concept of workspaces as
containers for multiple projects. There are a couple of key behaviors of workspaces which we want to build on when choosing how to share code across projects.
- By default, all the Xcode projects in a workspace are built in the same directory, referred to as the workspace build directory.
- Xcode examines the files in the build directory to discover implicit dependencies.
- Each project in a workspace continues to have its own independent identity.
Schemes:
Within a workspace or within a project which is a member of a workspace we have schemes.
Schemes replace the Active Target, Build Configuration, and Executable settings from Xcode 3 and define which targets to build, the order in which to build them, and what action to take when a build is complete. We’ll want our shared code to easily fit into
the scheme of any projects which use it. The Xcode
4 Transition Guide covers this new structure in more detail.
Targets:
Within a scheme we have one or more build targets which
define a set of source files to build, the settings used to build those files, and any dependencies on the build products of other targets which must be completed first. Ultimately we would like a consumer of our code to be able to state that their project’s
build target depends on our shared code and have Xcode build this shared code and make it available to the active build target. We can achieve that by providing a project containing the code to be shared and a static library build target which packages it
into a build product other developers can add as a build target dependency.
Using a static library
- Creating
a workspace - Adding
projects to a workspace - Adding
build target dependencies - Adding
the static library’s headers - Configuring
the project’s scheme - Fixing
indexing
Creating a workspace
We can create a new empty workspace from Xcode’s file menu or open an existing project and select “Save As Workspace…” to create a new workspace containing our project. This will create a “.xcworkspace” package in the file system.
An empty Xcode 4 workspace
Adding projects to a workspace
Once we have a workspace we can right-click in the workspace’s navigator to create a new project or add an existing “.xcodeproj” package.
Adding a new project to a workspace
Adding an existing project to a workspace
We want to end up with a single workspace containing our app’s project and the projects for any static libraries we are going to depend on. It is worth noting that these projects are all siblings in the workspace, our static libraries are not added as references
within our app’s project.
Adding build target dependencies
With all of the projects we need available in our workspace we can select our app’s build target and add a static library to the “Link Binary With Libraries” build phase.
Libraries and frameworks available to add to the "Link Binary With Libraries" build phase
A static library added to the "Link Binary With Libraries" build phase
Adding the static library’s headers
We also need to make sure that our app’s build target can locate the public headers used in this static library. Open the “Build Settings” tab and locate the “User Header Search Paths” setting. Set this to “$(BUILT_PRODUCTS_DIR)” (or “$(BUILT_PRODUCTS_DIR)/static_library_name”
if we want to be more specific but then we’ll have to update this setting every time we add another library) and check the “Recursive” check box. Now our built target will search our workspace’s shared build directory to locate linkable header files.
Setting the User Header Search Paths
User Header Search Paths set
The “User Header Search Paths” setting defines the headers available as quoted imports (eg “#import “MyLibraryClass.h”) while the “Header Search Paths” setting defines those headers available as bracketed imports (eg “#import ). I’ve found that Xcode will only
autocomplete header names in the quoted form so I always add libraries to the user header search path even though, from my project’s perspective, they might be more appropriate as system level (angle bracketed) libraries.
When using a static library which includes categories we will also have to add the “-ObjC” flag to the “Other Linker Flags” build setting. This will force the linker to load all objective-c classes and categories from the library. If the library contains only
categories “-all_load” or “-force_load” may be needed as well. See Technical
Q&A QA1490 for a more detailed explanation of these settings.
Configuring the project’s scheme
At this point Xcode should have detected this implicit dependency between our app’s project and the static library’s project and have automatically configured our schemes correctly. Unfortunately I haven’t found this to be the case in practice. Instead we will
have to edit our current scheme and add the static library’s build target before our app’s build target.
Setting the scheme's target build order
Fixing indexing
At this point we should be able to include headers from dependent static libraries, use the included classes, and still successfully build our app. Unfortunately Xcode will not show any classes from these linked static libraries in code completion despite the
workspace documentation stating that “indexing is done across the entire workspace, extending the scope of content-aware features such as code completion and refactoring.”
As a workaround we can drag the public headers from the static library’s project into our app’s project, adding them as references. These headers do not need to be included in any of our build targets, simply having references to the headers in our project
will allow their classes to appear in code completion.
Creating a Static Library
If we plan on releasing some of our own code for reuse as a static library there are several things we should do to make sure that the process described above is as easy and simple as possible for our library’s users.
- Namespace
classes appropriately - Create
a build target - Expose
public headers - Set
the installation directory - Set
the public header path - Exclude
user specific files from VCS
Namespace classes appropriately
Use an appropriate prefix for
classes, protocols, functions, and constants in the library to prevent collisions with names in the library’s user’s project.
Create a build target
Provide a static library build target in the project for our users to link against. Xcode provides templates for creating projects with static libraries or adding static library build targets to existing projects.
Expose public headers
Determine which header files should be visible to users of the library. Provide a clearly named group containing these headers so that our library’s users can easily locate them as part of the workaround described in “Fixing Indexing” above. This also helps
us clarify what the public interface our library provides is and what classes are implementation details which are likely to change as the library evolves.
For each public header file make sure it is set as “public” in the “Target Membership” section of the inspector pane. Only public headers are going to be available for our users to import.
Making a header file public
Set installation directory
Our static library build target is going to be a member of a user’s workspace and subject to that workspace’s installation rules. Our static library build product could therefore be installed in a location set by Xcode’s preferences, in the derived data path,
or in a path specified by our build target. Since we can’t control the user’s settings we should make sure our library is well behaved in all cases. I set the “Installation Directory” build setting to “$(BUILT_PRODUCTS_DIR)” so that the static library build
product can be found in a known location and set the “Skip Install” build setting to “Yes” to avoid accidentally installing iOS libraries into “/usr/local/lib”.
Setting the installation directory
Set the public header path
We need to specify a location to copy our static library’s public headers to so that they can be included in our users’ header search paths. Setting the “Public Headers Folder Path” to “$(TARGET_NAME)” will create a folder named after our static library build
target in the workspace’s shared build directory and be indexed by the “User Header Search Paths” setting described above.
Setting the public headers path for the static library
Exclude user specific files from VCS
Our workspace and project include a number of files which contain data relevant only to our user account; window positions, open files, and so on. There’s no need to check these into source control, at least not in our release branch, so let’s set some reasonable
ignore rules in git or whatever VCS we are using. Github provides a convenient set of .gitignore
files
Future Improvements
Hopefully Xcode 4 will eventually live up to the promise of it’s documentation and consistently auto-detect implicit dependencies and index files across the workspace correctly. There certainly seem to be a number of other developers struggling with this behavior:[1],
[2], [3],
[4], [5],
[6], [7],
[8].
Until that indexing improves I find that this process is at least somewhat simpler and cleaner than trying to maintain simulator and device compatible static library builds in Xcode 3.
I’ve found this pattern preferable to copying third party classes directly into my projects as it allows me to easily keep version history and make updates to static library projects in my workspace and avoids coupling my project too closely to the private
structure and contents of the static library.
Please let me know if you can see any areas where this pattern could be improved or if you’ve found your own alternative means of sharing code.
Using Open Source Static Libraries in Xcode 4的更多相关文章
- Creating and Using Static Libraries for iPhone using Xcode 4.3
Recently, after developing a collection of applications for iPhone that were intended to be used as ...
- Building Objective-C static libraries with categories
Q: How do I fix "selector not recognized" runtime exceptions when trying to use category m ...
- 「操作系统」:Linker Use static Libraries
While static libraries are useful and essential tools, they are also a source of confusion to progra ...
- Building Objective-C static libraries with categories(ObjC、all_load、force_load)
https://developer.apple.com/library/mac/qa/qa1490/_index.html 之所以使用该标志,和Objective-C的一个重要特性:类别(cat ...
- zz A list of open source C++ libraries
A list of open source C++ libraries < cpp | links http://en.cppreference.com/w/cpp/links/libs Th ...
- The Ultimate List of Open Source Static Code Analysis Security Tools
https://www.checkmarx.com/2014/11/13/the-ultimate-list-of-open-source-static-code-analysis-security- ...
- Openssl - Static libraries (w32, mingw) 以及对Qt静态编译时的设置
Openssl static libraries created for Windows 32bit using MinGW compiler Compiled with: ./Con ...
- Static, Shared Dynamic and Loadable Linux Libraries
转载:http://www.yolinux.com/TUTORIALS/LibraryArchives-StaticAndDynamic.html Why libraries are used: Th ...
- Build fat static library (device + simulator) using Xcode and SDK 4+
155down votefavorite 185 It appears that we can - theoretically - build a single static library that ...
随机推荐
- linux源码阅读笔记 asm函数
在linux源码中经常遇到__asm__函数.它其实是函数asm的宏定义 #define __asm__ asm,asm函数让系统执行汇编语句. __asm__常常与__volatile__一起出现. ...
- java基础知识回顾之---java String final类构造方法
/** * String 构造方法学习 * String(byte[ ] bytes):通过byte数组构造字符串对象. * String(byte[] bytes, int offs ...
- git init 与 git init --bare 的区别
git init 和 git init –bare 的区别 使用命令"git init --bare"(bare汉语意思是:裸,裸的)初始化的版本库(暂且称为bare repos ...
- 架构探险——从零开始写Java Web框架》第二章照作
沉下来慢慢看实现了. 越来越觉得可以和DJANGO作对比. package org.smart4j.chapter2.model; /** * Created by sahara on 2016/3/ ...
- DataRow.RowState 属性
RowState 的值取决于两个因素:已对该行执行的操作的类型,以及是否已对 DataRow 调用了 AcceptChanges. private void DemonstrateRowState() ...
- 【nginx运维基础(7)】常用PHP开源程序的NginxRewrite示例
在写伪静态的时候,可以先用一个打印$_GET的PHP文件来测试,并且一定注意浏览器缓存,另外正则里如果有"{}",正则要用双引号包起来 dedecms location / { r ...
- 对象的类型转换P109
类作为一种应用数据类型,和基本数据类型的变量一样.不同类中存在对象与对象之间的类型转问题,对象的类型转换只能在 具有继承关系的 父类对象-----子类对象 之间进行 子类通常比父类拥有更多的域和 ...
- 64位下好神奇啊(增加了PatchGuard技术保护自己,SSDT是相对地址,参数通过寄存器与rdi来传递)
近期可能会有一个64位平台的驱动开发任务,找了些资料,对64位平台下的驱动开发略知一二了,好神奇. 一.在64位系统下,有一项PatchGuard技术,它是微软为了防止自己的代码被Patch,进而影响 ...
- linux 免交互状态下修改用户密码
当利用某些工具对linux用户进行远程密码更改时,输入[ passwd 用户名 ] 后需要输入两次密码, 但是如果你利用的某些工具无法与linux进行交互的情况下,就没办法变更用户密码了,这个时候可以 ...
- Java API —— 泛型
1.泛型概述及使用 JDK1.5以后出现的机制 泛型是一种特殊的类型,它把指定类型的工作推迟到客户端代码声明并实例化类或方法的时候进行.也被称为参数化类型,可以把类型当作参数一样传递过来,在传递过来之 ...