iOS应用框架程序员iOS Developer

创建同时适用于 iOS 和 OSX 的 Framework

2016-05-30  本文已影响502人  mconintet

之前分享过一篇文章 Universal iOS / OS X Frameworks,使用文中描述的方法,就是可以使用同一个 code base 来产生同时适用于 iOS 和 OSX 的框架,否则你就需要在项目中添加针对各个平台的 targets 然后在其中维护不同的版本,当然你也可以对各个 targets 中的文件使用 soft link,但是还是没有文中的方法来得方便。不过文中的方式还有有一些坑的,后面会讲到。

现在,让我们一步一步的创建一个 framework,然后按照文中的方法来配置(我的 Xcode 版本为 7.3)。

1. 创建项目

这里我们以创建一个 OSX 下的 Framework 为起点,点击 Next 来输入一些项目的基本信息

2. 输入基本信息

在这一步中,你需要输入一些关于即将创建的项目的基本信息,下面是我的项目的基本信息的截图:

这里我给项目起名 UFD,就是 Universal Framework Demo 的缩写。并且语言选择了 swift。

再次点击 Next 你将需要选择合适的磁盘位置去用于存放项目文件。

3. 修改 Support Platforms

按照上图中序号标注的步骤,点击图中椭圆标记的部分,会看到下拉菜单,选择下拉菜单中的 Other,像下面这样:

点击了 Other 之后,会出现下图:

点击图中标记的 + 号,然后分别输入 iphoneosiphonesimulator,像下面这样:

这一步完成之后,你可以发现我们项目的可选的 build devices 变得不再是只有 Mac 了:

4. 修改 Valid Architectures

现在我们开始修改 Valid Architectures 选项的内容。

双击图中标记的部分,将会出现编辑区域,我们需要添加 arm64 armv7 armv7s,像下面这样:

现在按下 CMD+R,如果你的设备选择的是 My Mac,那么编译并没有什么问题,现在试试将设备改为 iphone 模拟器后再次 CMD+R。不出意外的话,你会看到下面的错误:

这是因为在 iOS 的 SDK 中并没有 <Cocoa/Cocoa.h> 这个头文件,于是我们需要加上条件编译语句,其实就是简单的预处理,根据当前不同的编译平台来选择合适的代码分支。

现在你将你的项目中的 项目名称.h 文件中的 #import 语句改为下面的样子:

#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#elif TARGET_OS_MAC
#import <Cocoa/Cocoa.h>
#endif

然后选择设备为 My Mac,运行下 CMD+R 发现没有什么问题,现在讲设备切换为 iphone 模拟器,再次 CMD+R,发现了什么?居然还是报错!:

发生这个错误的原因我觉得是 Xcode 的 bug,这个 bug 原因与框架所采用的语言有关(你在创建框架项目时选择的语言),如果你的项目语言和我一样选择的是 swift,那么你就会遇到和我一样的问题。不信的话你可以另外创建一个使用 Objc 语言的框架项目,按照与上面相同的步骤,你会发现它们会工作的很好。

为什么我会想到这个问题可能和项目所采用的语言有关呢?因为根据上面的过程,我们的预处理语句在设备为 My Mac 的时候工作的很好,只是当我将设备选择为了 iphone 模拟器之后,相应的宏似乎没有被自动的设置,而恰好在我的项目使用的是 swift 语言,并且我们知道在 swift 中不再使用预处理(Preprocessor)了,取而代之的是功能相对单一的构建配置 (Build Configuration)有可能就是这里出了问题,果然新建一个新的 Objc 框架之后同样的代码是可以的。

那么怎么解决这个问题?

如果你的项目是纯 Swift 的,那么很简单,将 项目名称.h 文件内容改成下面的样子就可以了:

#import <Foundation/NSObjCRuntime.h>

//! Project version number for UFD.
FOUNDATION_EXPORT double UFDVersionNumber;

//! Project version string for UFD.
FOUNDATION_EXPORT const unsigned char UFDVersionString[];

// In this header, you should import all the public headers of your framework using statements like #import <UFD/PublicHeader.h>

FOUNDATION_EXPORT 的定义在头文件 <Foundation/NSObjCRuntime.h> 中,它的作用就是根据文件是 C/C++ 而替换成 extern 或者 extern "C",所以这里你直接将文件内容换成这样也行:

extern double UFDVersionNumber;

//! Project version string for UFD.
extern const unsigned char UFDVersionString[];

如果你的项目不是纯 swift 的,那么有时预处理就显得很重要,而现在的问题是当选择了项目语言为 swift 语言时,并不是所有的设备都不能通过编译。折中的方法就是,我们可以在可以通过编译的设备下先写着,而需要为那些在 Xcode 选了之后无法通过编译的设备采用手动的编译 - 因为 TARGET_OS_IPHONE 只是一个宏,它的定义在 TargetConditionals.h ,如果它没有被自动设置的话我们可以手动的设置它,通过使用 xcodebuild 的 GCC_PREPROCESSOR_DEFINITIONS 参数就可以了。

Carthage

不得不说的是,采用上面方式的项目代码并不能和 Carthage 一起工作,这也是我刚开始分享那个文章没有注意到的,当我动手试了之后,发现了这个问题。

不过这似乎并也不是什么大的问题,我们依然可是使用 xcodebuild 来手动的编译,要知道 Carthage 其实也是使用的是 xcodebuild。

那么现在我们就开始手动的编译一下,(更多的 xcodebuild 参数见 Xcode Build Setting Reference )首先是 iOS 版本,使用下面的命令行即可:

/usr/bin/xcrun xcodebuild clean build -project \
/path/to/your/workspace/YourProject/YourProject.xcodeproj -scheme YourProject \
-configuration Release \
-sdk iphoneos \
ARCHS='armv7 armv7s arm64' \
VALID_ARCHS='armv7 armv7s arm64' \
BITCODE_GENERATION_MODE=bitcode \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGN_IDENTITY= \
GCC_PREPROCESSOR_DEFINITIONS='TARGET_OS_IPHONE=1' \
CONFIGURATION_BUILD_DIR='/path/to/place/your/framework.ios'

然后是 iphone 模拟器的:

/usr/bin/xcrun xcodebuild clean build  -project \
/path/to/your/workspace/YourProject/YourProject.xcodeproj -scheme YourProject \
-configuration Release \
-sdk iphonesimulator \
ARCHS='x86_64' \
VALID_ARCHS='x86_64' \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGN_IDENTITY= \
GCC_PREPROCESSOR_DEFINITIONS='TARGET_OS_IPHONE=1' \
CONFIGURATION_BUILD_DIR='/path/to/place/your/framework.ios.simulator'

最后是 OSX 的:

/usr/bin/xcrun xcodebuild clean build  -project \
/path/to/your/workspace/YourProject/YourProject.xcodeproj -scheme YourProject \
-configuration Release \
-sdk macosx \
ARCHS='x86_64' \
VALID_ARCHS='x86_64' \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGN_IDENTITY= \
GCC_PREPROCESSOR_DEFINITIONS='TARGET_OS_IPHONE=0' \
CONFIGURATION_BUILD_DIR='/path/to/place/your/framework.osx'

暂时就这么多吧,算是给看之前分享的文章填个坑。

上一篇下一篇

猜你喜欢

热点阅读