So, it appears that Apple® has changed their App validation process; fat binaries are no longer passing validation, and, per our support inbox, this is a very real and present problem for a lot of people.
Well, fear not! After some nifty hacking and finagling we have come up with a process that is simple and straight-forward, continues to support universality in development, and archives artifacts that pass validation.
If you implement this method, please, let us know about it in the comments, and, if it is working for you, be sure to flood your social media with links back to this article. No, this is not a shameless attempt to get traffic, though traffic sure is sweet. This is evidently a very real problem for many developers, and it is certain that if they are currently sitting at their keyboard, slamming their face into it because they can’t resolve this issue, they will be thankful you gave them a heads up!
Alright, enough bloviating. Here is a “step by step” of the new process.
Creating a Framework
- Fire up Xcode and start a new Cocoa Touch Framework project (Fig. 1)
(Figure 1) - Go to the Build Settings of your project target and confirm or set the “Architectures” to “Standard Architectures.” These are arm64 and armv7, and are likely the default. (Fig. 2)
- Set “Build Active Architecture” to “NO” – Yes your build times may be a little longer, but this is what is going to allow your Frameworks to be “universal”. So, if you don’t do this, running on your sim will be a problem. Besides, if your builds are debilitatingly slow in a framework because you set “Build Active Architecture” to “NO,” you probably aren’t maximizing modularity. (Fig. 2)
- Set or confirm “Valid Architectures” to arm64, armv7 and armv7s. This is likely the default. (Fig. 2)
(Figure 2) -
Go to the Build Phases section of your project target and append a new Run Script phase. Paste this script in in it:UPDATE: Due to the latest version of Xcode changing its compilation process this script can no longer be placed in the Build Phases, as this causes the script to run too early, not giving the build process enough time to generate the module.modulemap files for your framework. If you previously used this process be sure to follow these new instructions for this step and remove the Build Phase Run Script that this step previously requested.
NEW INSTRUCTIONS: Add the script below to your project’s build Scheme as an Archive Post-action:
Archive Post-action script
set -e DEVICE_BIN="${OBJROOT}/UninstalledProducts/${TARGET_NAME}.framework" SIMULATOR_BIN="${SYMROOT}/../../../../Products/Debug-iphonesimulator/${TARGET_NAME}.framework" ARCHIVE_PATH="${SRCROOT}/_Archive" rm -rf "${ARCHIVE_PATH}" mkdir "${ARCHIVE_PATH}" if [ "${CONFIGURATION}" = "Release" ]; then if [ -d "${DEVICE_BIN}" ]; then DEVICE_PATH="${ARCHIVE_PATH}/Release" mkdir "${DEVICE_PATH}" cp -r "${DEVICE_BIN}" "${DEVICE_PATH}" fi if [ -d "${SIMULATOR_BIN}" ]; then SIMULATOR_PATH="${ARCHIVE_PATH}/Debug" mkdir "${SIMULATOR_PATH}" cp -r "${DEVICE_BIN}" "${SIMULATOR_PATH}" lipo -create "${DEVICE_BIN}/${TARGET_NAME}" "${SIMULATOR_BIN}/${TARGET_NAME}" -output "${SIMULATOR_PATH}/${TARGET_NAME}.framework/${TARGET_NAME}" fi fi exit 0;
There you go. Now you will simply develop.
Assuming that you are using your simulators and devices to test regularly during development before archiving, you will not need to take special note of this, BUT do know that to get your framework artifact you will have to Archive your project. Doing so will add an _Archive/ directory (Fig. 3) to the root of your project in Finder in which you will find your artifact. Know that you will have to build the project with a simulator and archive with a device to get a universal. If someone out there wants to contribute an xcodebuild or xctool script to build both of these on the fly, please, feel free. It would be a great contribution and a welcome addition for may, I’m sure!
(An example of a working framework template can be found on here on kodmunki™ Github.)
That’s it! Now, you have a Framework. But, if you are anything like me, you have likely built a kernel or some other low level Framework and will expect other higher level Frameworks to depend on this Framework. Like, say, persistence, or data. To do this you will do the following:
Creating Dependent Frameworks
- Perform steps 1-6 from above to create the Framework.
- In the Build Settings of both the project Target and the test Target, set “Always Search User Paths” to “YES”. (Fig. 4)
(Figure 4) - Add a /lib directory to your Framework that will contain all of your project’s custom Frameworks, i.e. kernel if this is data. (Fig. 5)
- Put the desired Framework artifacts in this directory. When done correctly, the /lib directory will contain a /Debug and a /Release directory each containing your binary dependencies. (Fig. 5)
(Figure 5) - In the “Framework Search Paths” of both your project Target and your test Target add “$(PROJECT_DIR)/lib/$(CONFIGURATION)” (Fig. 6)
(Figure 6) - In the “Runpath Search Paths” of both your project Target and your test Target add the following paths: “$(inherited), @executable_path/Frameworks, @loader_path/Frameworks, $(PROJECT_DIR)/lib/$(CONFIGURATION)” (Fig. 7)
(Figure 7)
All should be working successfully! Run your unit tests, build with both the simulator and device, and archive your project to verify.
(An example of a working dependent framework template can be found on here on kodmunki™Github.)
Now you want to add this awesome modularity to an application you say? Okay, here’s how:
Adding Frameworks to an App
- Create a /lib directory at the project root that will contain the custom Frameworks that you want to add.
- Put the desired Framework artifacts in this directory. (Be sure to add both the Debug and Release bins at the directory level. This is just like in step #4 of the previous section)
- In the“Runpath Search Paths” and “Framework Search Paths” of your project target and test target add “$(PROJECT_DIR)/lib/$(CONFIGURATION)”
- Navigate to the General section of your project Target and add the desired Frameworks to the “Embedded Binaries” section. Add the binaries that are found in Release! The ones in Debug will not pass validation with Apple®! Note, that this should automatically add them to the “Linked Frameworks and Libraries” below. After this step, you should have see your Frameworks in both sections. (Fig. 9). Point of note: ensure that they aren’t duplicated in the “Linked Frameworks and Libraries” section!
(Figure 9)
(An example of a working application template can be found on here on kodmunki™ Github.)
Some worthwhile points of note:
- These instructions are for those projects that support Cocoa Touch Frameworks, i.e. iOS 8+. Efforts to use these instructions for prior versions, may not be futile, but are untested and will, likely, be in vain.
- If you would like to port an iOS Framework built using the “kstenerud/iOS-Universal-Framework” to a Cocoa Touch Framework, I recommend you do the following: just start a new Cocoa Touch Framework, follow the steps that I have outlined here, copy and paste the files from your old project into your new project directory tree, and add them to the project. To answer a question some of you might have, yes, you could just change the settings, phases, and scripts of your current project, but I do not condone or recommend this, as you are certain to lose hours or days of your life fiddling with settings and scouring the ends of the earth for some obscure missed step. That said; if you decide to go this route, please, do notemail me with issues. I don’t recommend it, and I will not offer support for it. — Well, free support anyway … paid, hourly support, maybe :{)}
- Xcode is horrifically finicky, skittish, and unpredictable. I have tested this method with numerous libraries and have had nothing but success. Though, I must say, I have run into compiler errors and warnings from time to time for no good reason, whatsoever. That said, please be sure to try cleaning your project, clearing your derived data, re-running your build steps, and doing any of the other “regular” Xcode cleans before assuming this doesn’t work. I absolutely hate having to make this disclaimer, but if you have any experience with Xcode you, too, know this sad but inevitable truth.
There you have it. Modularity revived in Xcode 6 the remix! This should work now just as it did in the previous article on this topic. Though, now, the processes passes Apple® validation when attempting to deploy your App to the app store! :{)}
I hope that you enjoyed this post and find it useful! And, if you do, you share it with others.
Please, comment on and share this post with others if you enjoy it; follow @kodmunki on Twitter for regular updates on new developments, deployments, articles, and tools; and check out kodmunki™ on github for cool, managed, and tested kodmunki™ projects.
Happy hacking! :{)}
以下转载自:
http://foggry.com/blog/2014/06/12/wwdc2014zhi-iosshi-yong-dong-tai-ku/
WWDC2014之iOS使用动态库
苹果的开放态度
WWDC2014上发布的Xcode6 beta
版有了不少更新,其中令我惊讶的一个是苹果在iOS上开放了动态库,在Xcode6 Beta
版的更新文档中是这样描述的:
Frameworks for iOS. iOS developers can now create dynamic frameworks. Frameworks are a collection of code and resources to encapsulate functionality that is valuable across multiple projects. Frameworks work perfectly with extensions, sharing logic that can be used by both the main application, and the bundled extensions.
详情见官方文档New Features in Xcode 6 Beta。
framework是Cocoa/Cocoa Touch程序中使用的一种资源打包方式,可以将将代码文件、头文件、资源文件、说明文档等集中在一起,方便开发者使用,作为一名Cocoa/Cocoa Touch程序员每天都要跟各种各样的Framework打交道。Cocoa/Cocoa Touch开发框架本身提供了大量的Framework,比如Foundation.framework/UIKit.framework/AppKit.framework等。需要注意的是,这些framework无一例外都是动态库。
但残忍的是,Cocoa Touch上并不允许我们使用自己创建的framework。不过由于framework是一种优秀的资源打包方式,拥有无穷智慧的程序员们便想出了以framework的形式打包静态库的招数,因此我们平时看到的第三方发布的framework无一例外都是静态库,真正的动态库是上不了AppStore的。
WWDC2014给我的一个很大感触是苹果对iOS的开放态度:允许使用动态库、允许第三方键盘、App Extension
等等,这些在之前是想都不敢想的事。
iOS上动态库可以做什么
和静态库在编译时和app代码链接并打进同一个二进制包中不同,动态库可以在运行时手动加载,这样就可以做很多事情,比如:
- 应用插件化
目前很多应用功能越做越多,软件显得越来越臃肿。因此插件化就成了很多软件发展的必经之路,比如支付宝这种平台级别的软件:
首页上密密麻麻的功能,而且还在增多,照这个趋势发展下去,软件包的大小将会不可想象。目前常用的解决方案是使用web页面,但用户体验和Native界面是没法比的。
设想,如果每一个功能点都是一个动态库,在用户想使用某个功能的时候让其从网络下载,然后手动加载动态库,实现功能的的插件化,就再也不用担心功能点的无限增多了,这该是件多么美好的事!
- 软件版本实时模块升级
还在忍受苹果动辄一周,甚至更长的审核周期吗?有了动态库妈妈就再也不用担心你的软件升级了!
如果软件中的某个功能点出现了严重的bug,或者想在其中新增功能,你的这个功能点又是通过动态库实现的,这时候你只需要在适当的时候从服务器上将新版本的动态库文件下载到本地,然后在用户重启应用的时候即可实现新功能的展现。
- 共享可执行文件
在其它大部分平台上,动态库都可以用于不同应用间共享,这就大大节省了内存。从目前来看,iOS仍然不允许进程间共享动态库,即iOS上的动态库只能是私有的,因为我们仍然不能将动态库文件放置在除了自身沙盒以外的其它任何地方。
不过iOS8上开放了App Extension
功能,可以为一个应用创建插件,这样主app和插件之间共享动态库还是可行的。
PS: 上述关于动态库在iOS平台的使用,在技术上都是可行的,但本人并没有真正尝试过做出一个上线AppStore的应用,因此并不保证按照上述方式使用动态库一定能通过苹果审核!
2014-6-23修正:
经@唐巧_boy提醒,sandbox会验证动态库的签名,所以如果是动态从服务器更新的动态库,应该是签名不了的,因此应用插件化、软件版本实时模块升级估计很难实现,一切都只能等到iOS8正式版发布以后再验证了。感谢@唐巧_boy!
创建动态库
1、创建动态库
- 创建工程文件
在下图所示界面能够找到Cocoa Touch动态库的创建入口:
跟随指引一步步操作即可创建一个新的动态库工程,我的工程名字叫Dylib,Xcode会同时创建一个和工程target同名的.h文件,比如我的就是Dylib.h。
- 向工程中添加文件
接下来就可以在工程中随意添加文件了。我在其中新建了一个名为Person的测试类,提供的接口如下:
1 2 3 4 5 |
|
对应的实现部分:
1 2 3 4 5 6 7 8 9 10 11 |
|
- 设置开放的头文件
一个库里面可以后很多的代码,但是我们需要设置能够提供给外界使用的接口,可以通过Target—>Build Phases—>Headers来设置,如下图所示:
我们只需将希望开放的头文件放到Public列表中即可,比如我开放了Dylib.h
和Person.h
两个头文件,在生成的framework的Header目录下就可以看到这两个头文件,如下图所示:
一切完成,Run以后就能成功生成framework文件了。
2、通用动态库
经过第一步我们只是创建了一个动态库文件,但是和静态库类似,该动态库并同时不支持真机和模拟器,可以通过以下步骤创建通用动态库:
- 创建Aggregate Target
按下图所示,在动态库工程中添加一个类型为Aggregate的target:
按提示一步步操作即可,我给Aggregate
的Target的命名是CommonDylib
。
- 设置Target Dependencies
按以下路径设置CommonDylib
对应的Target Dependencies
:
1 |
|
将真正的动态库Dylib Target添加到其中。
- 添加Run Script
按以下路径为CommonDylib
添加Run Script
:
1 |
|
添加的脚本为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
添加以后的效果如图所示:
该脚本是我根据一篇文章中介绍的脚本改写的,感谢原文作者。
脚本的主要功能是:
1.分别编译生成真机和模拟器使用的framework; 2.使用lipo命令将其合并成一个通用framework; 3.最后将生成的通用framework放置在工程根目录下新建的Products目录下。
如果一切顺利,对CommonDylib
target执行run操作以后就能生成一个如图所示的通用framework文件了:
使用动态库
添加动态库到工程文件
经过以上步骤的努力,生成了最终需要的framework文件,为了演示动态库的使用,新建了一个名为FrameworkDemo
的工程。通过以下方式将刚生成的framework添加到工程中:
1 |
|
同时设置将framework作为资源文件拷贝到Bundle中:
1 |
|
如图所示:
仅仅这样做是不够的,还需要为动态库添加链接依赖。
自动链接动态库
添加完动态库后,如果希望动态库在软件启动时自动链接,可以通过以下方式设置动态库依赖路径:
1 |
|
由于向工程中添加动态库时,将动态库设置了Copy Bundle Resources,因此就可以将Runpath Search Paths
路径依赖设置为main bundle,即沙盒中的FrameworkDemo.app目录,向Runpath Search Paths
中添加下述内容:
1 |
|
如图所示:
其中的@executable_path/
表示可执行文件所在路径,即沙盒中的.app目录,注意不要漏掉最后的/
。
如果你将动态库放到了沙盒中的其他目录,只需要添加对应路径的依赖就可以了。
需要的时候链接动态库
动态库的另一个重要特性就是即插即用
性,我们可以选择在需要的时候再加载动态库。
- 更改设置
如果不希望在软件一启动就加载动态库,需要将
1 |
|
中Dylib.framework
对应的Status由默认的Required
改成Optional
;或者更干脆的,将Dylib.framework
从Link Binary With Libraries
列表中删除即可。
- 使用dlopen加载动态库
以Dylib.framework
为例,动态库中真正的可执行代码为Dylib.framework/Dylib
文件,因此使用dlopen时如果仅仅指定加载动态库的路径为Dylib.framework
是没法成功加载的。
示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
以dlopen方式使用动态库不知道是否能通过苹果审核。
- 使用NSBundle加载动态库
也可以使用NSBundle来加载动态库,实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
使用动态库中代码
通过上述任一一种方式加载的动态库后,就可以使用动态库中的代码文件了,以Dylib.framework
中的Person
类的使用为例:
1 2 3 4 5 6 7 8 |
|
注意,如果直接通过下属方式初始化Person
类是不成功的:
1 2 3 4 5 6 7 |
|
监测动态库的加载和移除
我们可以通过下述方式,为动态库的加载和移除添加监听回调:
1 2 3 4 5 |
|
github上有一个完整的示例代码,
从这里看出,原来就算空白工程软件启动的时候也会加载多达一百二十多个动态库,如果这些都是静态库,那该有多可怕!!
Demo
本文使用的例子已经上传到github上,需要的朋友请自取。
另外,本文对某些东西可能有理解错误的地方,还请指出。
参考文档:
相关推荐
本文将深入探讨iOS中的动态库,即Dynamic Framework,它们是如何工作的,以及如何在项目中使用和创建。 动态库与静态库的区别在于,静态库在编译时会被合并到可执行文件中,而动态库则在运行时被加载。动态库的优势...
在iOS开发中,创建一个Framework静态库是一种常见的方式,它可以帮助开发者封装常用代码,提高代码重用性,并保护知识产权。本篇文章将详细讲解如何通过一个简单的demo来创建一个iOS的静态库。 首先,理解框架...
library`命令创建了一个动态库,`set_target_properties`设置了框架属性,`install`命令指定了安装(构建输出)路径,最后的`add_custom_command`用于生成框架结构,包括复制库文件到framework目录下,并创建一个...
在iOS中,这通常通过`dlopen()`函数实现,它会在运行时动态加载动态链接库。 3. **符号解析**:加载框架后,需要解析框架中的函数或方法符号,以便能够调用它们。`dlsym()`函数可以完成这个任务,它会返回指定符号...
在iOS开发中,dylib(动态链接库)是一种共享代码的方式,可以被多个应用程序同时使用,从而节省设备的存储空间并提高代码复用性。Xcode是Apple官方提供的集成开发环境,用于创建iOS和macOS应用。下面我们将深入探讨...
dylib 是纯动态链接库,通常由系统或第三方服务提供,而framework则包含头文件、资源文件和dylib,更适合开发者的日常使用。 创建动态库通常涉及以下步骤: 1. **创建项目**:在Xcode中新建一个Cocoa Touch Static ...
非静态Framework与静态Framework的主要区别在于,前者在运行时动态链接,而后者在编译时静态链接到应用程序中。动态框架允许更小的应用包大小,因为多个应用可以共享同一库,同时也能在发布后更新框架内的代码。 ...
本教程将详细讲解如何制作非静态的iOS Framework,这是一种动态链接库,允许在运行时加载代码,提高了应用的灵活性。 一、iOS Framework类型 iOS Framework主要有两种类型:静态库(Static Library)和动态库...
在iOS开发中,动态链接库(dylib)是一种常见的代码复用方式,它允许开发者将通用功能封装到单独的库中,多个应用可以共享这些库,以减少代码重复和提高效率。本篇文章将引导初学者快速入门iOS开发,通过创建一个...
IOS静态库和Framework区别 一、什么是库? 库是共享程序代码的方式,一般分为静态库和动态库。 二、静态库与动态库的...系统的.framework是动态库,我们自己建立的.framework是静态库。 六、a与.framework有什么区
`iOS Framework`是iOS SDK的一个重要组成部分,它是一个包含了可执行代码和资源文件的容器,可以理解为一种特殊的静态库或动态库。开发者可以创建自定义的Framework来封装特定的功能,然后在多个项目中重复使用。 ...
通常,我们接触到的iOS框架有两种类型:静态库(Static Library)和动态库(Dynamic Library)。本教程将重点讨论如何制作动态框架,因为标题明确指出“这次不是静态包”。动态框架在iOS 8及以上版本中被引入,它...
动态库的创建通常涉及创建一个新的Cocoa Touch Framework工程。例如,可以创建一个名为`DynamicLink`的框架,并在其中定义对外公开的方法,如`- (void)startWithObject:(id)object withBundle:(NSBundle *)bundle;`...
在iOS平台上,OpenCV框架以动态库的形式提供,便于开发者在Xcode项目中引用和使用。 在"opencv-4.4.0-ios-framework.zip"压缩包中,"opencv2.framework"是主要的文件,它是一个包含所有OpenCV库代码和资源的iOS框架...
1. **创建静态库项目**:在Xcode中选择"File" -> "New" -> "Project",然后在模板选择器中找到"iOS" -> "Framework & Library" -> "Cocoa Touch Static Library",填写项目名称并确定。 2. **编写库代码**:在新...
本篇文章将详细探讨iOS SDK中的关键组成部分,包括.framework静态库、.bundle资源文件以及脚本打包SDK和.bundle的相关知识。 首先,我们来看.framework静态库。.framework是iOS中的一种库文件格式,它包含了一个或...
- 动态库(Dynamic Library):运行时通过动态链接器加载,不包含在应用程序包内,减少了应用程序的体积。 2. **创建动态库项目**: - 在Xcode中选择"File" -> "New" -> "Project",然后选择"Cocoa Touch ...
在iOS开发中,动态库(Dynamic Library)是一种重要的软件组件形式,它允许程序在运行时加载和使用代码,而不是在编译时将其嵌入到主应用程序中。动态库的使用可以减少应用程序的大小,因为多个应用可以共享同一库,...