文档章节

Xcode Static Library Subprojects and Submodules[1]

whj
 whj
发布于 2014/03/02 22:03
字数 1519
阅读 79
收藏 0

In which a method is presented for reliably building static libraries with subprojects in Xcode, and it is suggested that this method, combined with Git submodules or other similar mechanisms, provides the best way to share libraries, frameworks, or other code between projects.

I’m sure I don’t need to tell you that it’s a useful thing to be able to share code between projects. On the small end of the scale, you might have created some nice views, or text processing classes, and want to be able to include them in multiple apps. On the larger end, perhaps you produce games written with a shared framework, or you’re maintaining an open source library.

One way to do this is to copy files - or even just snippets of code - between your projects. At a very small scale this works, but as things get larger, inevitably, over time, it leads to a codebase that’s hard to maintain, and probably buggy, because it’s nigh on impossible to keep all the copypasta in sync.

It would be better to keep the shared source together in one place. Doing this, you ensure that bug fixes easily propagate to all the apps using the code, and you can keep tests etc. in a central location.

For iOS (and I would argue, Mac, although I’m focusing on iOS in this post) the ideal way to do this is to create an Xcode project that builds a static library, then include that as a subproject in your app projects. Using features like Git’s submodules, or Subversion’s externals, you can ensure that the files for the same subproject in every app that uses it are kept in sync, with one master copy of the subproject kept centrally. This also enables easier collaboration - whether it’s with colleagues in your company, or the app-developing-public at large via GitHub.

I use this method with libEucalyptus to allow it to be included in multiple apps, some of which I don’t control, but keep it in a central location, and it works well.

“Someone on StackOverflow told me this could never work work.
Or, wait…, maybe they said it always worked automatically?…”

There’s a lot of confusion - and misinformation - out there on the web about how to do this. Most people seem to think that creating a static library project in Xcode, and using this as a subproject in other projects, is fraught with peril - a path filled with custom project configurations, header path manipulation, and strange project-editor voodoo to get things to work. Xcode 4 actually provides fairly good support for doing this though. Set things up correctly once, and you’ll be good to go.

By the end of this blog post, you’ll know how to create an Xcode project that compiles a static library, exposing public headers to other projects in a way that makes them easy to #import, and how to use that project as a subproject in other projects.

You’ll also be able to include this project in superprojects as a submodule using Git, or, if you’re not a Git user, it’ll hopefully be fairly obvious how you could use your favourite version control system to accomplish something similar (for example, using SVN externals).

Create an Xcode project for the static library

Let’s get started making our static library. First, we’ll fire up Xcode and create a new project:

Welcome To Xcode dialog with Create New Project button highlighted

We’ll use the Cocoa Touch Static Library template:

Choose A Template For Your New Project sheet with Cocoa Touch Static Library selected and highlighted

Next, we get to choose a name for our library. Let’s go with the imaginative name of SampleSubproject:

Choose Options For Your New Project sheet with the Product Name input box highlighted

After this, you’ll get a file dialog allowing you to choose where to put the project. I also let Xcode create a Git repository for me - there’s a checkbox in the dialog for that - we’ll use that later.

Hurray, a static library project!

Xcode showing the new static library subproject

Configure the static library target’s header location

Now, there’s basically only one thing to do to get this ready for other projects to use. By default, Xcode will place your public library headers in place where superprojects can include them like this: #import <HeaderFromTheSubproject.h>. This strikes me as a little messy. I like to change things so that I can include the headers like this: #import <SampleSubproject/HeaderFromTheSubproject.h> - just as you would when using a header from a framework (or, indeed, from many system-provided dynamic libraries). Xcode’s default configuration will also treat the library headers as ‘part of’ your finished product, and when you do an Archive build they’ll be included in the Archive. This is obviously wrong for an iOS app, and besides being messy will also cause your app to fail verification - not desirable.

To fix these problems, we’ll need to change one setting in the project editor. Select the project in the Navigator sidebar, then select theSampleSubproject library target, and make sure that All, not Basic, is selected at the top of the list of settings. Then, in the Packaging section, find the setting entitled Public Header Folders Path. Change it, in the target’s column, to read include/$(TARGET_NAME). This will make Xcode place the headers in a subfolder named after the target, and the lack of a ‘/’ at the start of the path means they’ll be placed next to the library, rather than in what Xcode sees as being a location on the target system, so they won’t be included in the products of Archive builds.

Xcode window showing the Public Header Folders Path of SampleSubproject being edited

That’s it. Before we go on to setting up an app project to use this subproject though, let’s make the library do something so we can see it working later.

Write the static library code

First, let’s make a simple class to use - we’ll call it “SSHelloer”. It will, in fine sample code tradition, return strings with a “hello” message. I won’t walk you through adding a class to an Xcode project, since I’m sure you know how to do it already, but here’s the code:

#import <Foundation/Foundation.h>@interface SSHelloer : NSObject- (NSString *)hello;@end

#import <SSHelloer.h>@implementation SSHelloer- (NSString *)hello{
    return @"Hello from SSHelloer in SampleSubproject!";}@end

Create a global header for the static library

Next, it’s a bit odd, really, that the template sets us up a class called SampleSubproject. Usually we’d expect a statement like#import <SampleSubproject/SampleSubproject.h> to import all the headers from the SampleSubproject library, not a SampleSubproject class. Let’s rectify this. Delete the SampleSubproject.m file entirely, and change the interface in SampleSubproject.h file to just #import <SampleSubproject/SSHelloer.h>. It’s not very helpful in our simple single-class library, but in a big project with lots of headers you could list all the headers in a list here, and then clients could just #import <SampleSubproject/SampleSubproject.h> to get everything from the library.

Xcode window showing the global library header

Set the visibility for the library headers

The last thing to do is to set the visibility of the headers in the project. For each header, there are three options. Public means that clients of the library will be able to see the header. This is what we want for headers that we’re intending to be visible to the apps including our project as a subproject. Project means that only files in this project will be able to see the headers. It’s useful for classes that are intended to be available to the library you’re creating, but not exposed to clients of the library. Private is a little misleading. Intuitively, you might think its a synonym for Project, but it’s not. It’s intended for headers that are intended for some clients of the library, but are not public. It’s useful, for example, for Apple, which can have headers for private APIs that are available at build-time for ‘internal’ clients to use, but will not be shipped out to us in Mac OS X SDKs. It doesn’t really make any sense in our situation, so it’s best to just ignore that it’s there, and choose from Public or Package.

From that explanation, you can hopefully see that both our headers, SampleSubproject.h and SSHelloer.h need to be Public. You can set this in the “Target Membership” section of the right-hand “Utilities” panel:

Xcode window showing the global library header

Library subproject ready!

Lastly, just as a sanity check, build the library, and all should be well.

That’s it, our subproject is ready to go. At this point, I committed everything to Git, and pushed it to GitHub at http://github.com/th-in-gs/SampleSubproject. I’ll use the repo from GitHub later to clone it into the app that’s using it. If you want to do something similar, even if you’re using Git, it’s not necessary to use GitHub. You can use submodules from any Git host - or even the local filesystem. You could also use another version control system that supports submodules or externals, or even just filesystem symlinks if you like to live dangerously.


本文转载自:http://www.blog.montgomerie.net/easy-xcode-static-library-subprojects-and-submodules

共有 人打赏支持
whj

whj

粉丝 20
博文 32
码字总数 6865
作品 0
海淀
私信 提问
iOS-universal-framework怎么都装不成功的解决方案

(亲测xcode5.02 xcode5.1可通过) iOS-universal-framework使用install.sh装Real Framework怎么都不成功的时候,一般是application下目录的权限问题,可以不用install.sh脚本来安装,手动c...

iSsss
2015/03/03
5
0
iOS FrameW Create

http://db-in.com/blog/2011/07/universal-framework-iphone-ios-2-0/ Universal Framework iPhone iOS (2.0) Hello my friends, Due to some bugs and questions with the old tutorial, I’......

晨曦之光
2012/05/28
208
0
如何快速构建一个简单的程序

首先我们通过内置的工程模板创建一个空工程: 这个时候xmake将会产生一些工程文件,如下: 这个简单的程序仅仅只是为了打印输出: 是基于lua语法的工程描述文件,它很简单: 现在我们开始编译这...

ruki
2016/07/16
14
0
iOS 自己封装的SDK 打包与合并,新手教程!!!

前言 (1)这个时候就得说下静态库,动态库区别。 静态库:1.模块化,分工合作。2.避免少量改动经常导致大量的重复编译链接。3.也可以重用,注意不是共享使用。 动态库:1.使用动态库,可以将...

醉丨灬爱雪
2018/06/26
0
0
iOS 自己封装的SDK 打包与合并,新手教程!!!

前言 (1)这个时候就得说下静态库,动态库区别。 静态库:1.模块化,分工合作。2.避免少量改动经常导致大量的重复编译链接。3.也可以重用,注意不是共享使用。 动态库:1.使用动态库,可以将...

醉丨灬爱雪
2017/05/03
0
0

没有更多内容

加载失败,请刷新页面

加载更多

C++ vector和list的区别

1.vector数据结构 vector和数组类似,拥有一段连续的内存空间,并且起始地址不变。 因此能高效的进行随机存取,时间复杂度为o(1); 但因为内存空间是连续的,所以在进行插入和删除操作时,会造...

shzwork
今天
3
0
Spring之invokeBeanFactoryPostProcessors详解

Spring的refresh的invokeBeanFactoryPostProcessors,就是调用所有注册的、原始的BeanFactoryPostProcessor。 相关源码 public static void invokeBeanFactoryPostProcessors(Configu......

cregu
昨天
4
0
ibmcom/db2express-c_docker官方使用文档

(DEPRECIATED) Please check DB2 Developer-C Edition for the replacement. What is IBM DB2 Express-C ? ``IBM DB2 Express-C``` is the no-charge community edition of DB2 server, a si......

BG2KNT
昨天
3
0
Ubuntu 18.04.2 LTS nvidia-docker2 : 依赖: docker-ce (= 5:18.09.0~3-0~ubuntu-bionic)

平台:Ubuntu 18.04.2 LTS nvidia-docker2 版本:2.0.3 错误描述:在安装nvidia-docker2的时候报dpkg依赖错误 nvidia-docker2 : 依赖: docker-ce (= 5:18.09.0~3-0~ubuntu-bionic) 先看一下依......

Pulsar-V
昨天
4
0
学习笔记1-goland结构体(struct)

写在前面:若有侵权,请发邮件by.su@qq.com告知。 转载者告知:如果本文被转载,但凡涉及到侵权相关事宜,转载者需负责。请知悉! 本文永久更新地址:https://my.oschina.net/bysu/blog/3036...

不最醉不龟归
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部