Framework-iOSAPP
详细的静态库打包APP的方案,便于多APP集成
一、 创建子APP并打包
一个sonAPP,一个mainAPP,我们将把sonAPP的工程文件集成到mainAPP中。
1.1 编写sonAPP的代码
添加简单的事件和界面代码,并添加资源文件(分别在图片管理器assets和图片文件夹里添加文件)
1.2 将sonAPP的源代码制作成静态库
静态库:可以是.a和.framework格式,链接时,静态库会被完整地复制到可执行文件中,被多次使用就有多份冗余拷贝。
我们将使用.framework格式,创建framework步骤:
-
创建的时候 Embed in Application 选择 None, 我们不需要把静态库嵌入到工程中;
-
在Build Settings中修改mach-o为static library,设置为静态库;
-
在Build Settings中修改Build Active Architecture Only都为NO,方便在Bebug情况下的使用;
创建完成后,在info中Bundle OS Type code显示的是FMWK
1.3 编译阶段(Build Phases)简介
编译阶段是当前对象编译过程中的一个阶段,一般系统默认有对象依赖、汇编资源以及复制包资源等。此外,还可以自行添加额外的阶段,甚至还可以用Shell脚本以便在编译处理期间提供更为复杂、精确的操纵。
-
对象依赖(Target Dependencies)阶段可以让Xcode 知道必须在当前选择的对象编译之前编译的其他依赖对象(比如应用扩展、插件等等)。
-
编译源文件(Compile Sources)阶段按照Build Rules 选项卡中的定义,用合适的编译器来编译该对象的所有源文件。可以通过修改该阶段的Compiler Flags(编译器标识)来为每个单独文件设置其编译器标识,比如优化设置等等。
-
链接二进制文件和库(Link Binary With Libraries)中,可以控制该对象需要链接哪些二进制文件和库。只要使用了二进制文件或者库,就必须要链接它们。有些常用的框架(比如说Cocoa框架)并不会显式显示在里面,但是实际上它是包含的,重复添加并不会造成框架的重复引用。
-
复制包资源(Copy Bundle Resources)阶段中定义了对象中的资源文件,包括应用程序、图标、界面构造器、视频、模板等等。这些资源都会被复制到安装包的Contents/Resources文件夹下。
-
文件复制(Copy Files)阶段用来标识文件复制的目标路径。可以从Destination(目标)菜单中选择预定义的目标,也可以自己指定路径。文件复制阶段常常用来将文件复制到预定义的位置。预定义的路径包括程序安装包的Resources、Frameworks文件夹、共享框架路径等等。Subpath(子路径)字段则会在Destination菜单中选择的路径后附加一个子路径名称。“Copy only when installing”(仅在安装时复制)命令Xcode只在当前编译设置中包含Install标识时才复制文件。
-
头文件(Headers)阶段用来为产品指定其头文件以及可见性。作用域(Public、Private和Project)则用来确定头文件的可见性。Public域则包含在最终产品当中,作为可读源代码存在;Private域虽然也包含在最终产品当中,但是不能够直接读取;Project域则不包含在最终产品当中,只有项目在编译对象时才使用。
-
运行脚本(Run Script)阶段可以使脚本得以运行,脚本可以在脚本编辑器区域当中编辑,也可以直接放入一个脚本文件进来。
1.4 编写静态库的入口文件
入口文件负责界面的跳转与数据通信的功能,详细编写见SonSDK.h和SonSDK.m文件
1.5 静态库Build Phases的操作
-
在Compile Sources中,添加除main.m和AppDelegate.m之外的所有源文件
-
在Link Binary With Libraries中,添加所有的二进制文件和库,与工程APP的一样
-
在Copy Bundle Resources中,我们将专门制作一个bundle放资源,所以在静态库中的复制包资源可以不放资源文件。
-
在Headers中,我们将SonSDK放到Public中
1.6 制作资源Bundle
创建Bundle的步骤
-
在Build Settings中修改Build Active Architecture Only都为NO,方便在Bebug情况下的使用;
-
Base SDK选择iOS系统的
添加资源的步骤
-
将xib、storyboard等(除LaunchScreen.storyboard)IB文件放到Bundle中
-
将图片、Assets等图片资源放到Bundle中
-
将结构化文件等文件放到Bundle中
简而言之,就是和工程中的Bundle Phases中的Copy Bundle Resources中的文件一致,可以不用放LaunchScreen.storyboard。
二、创建主APP
因为我们是使用注册方法跳转到子APP,所以我们先添加注册信息。 在工程中我们添加SonBundle.bundle和SonSDK.framework
三、打包静态库
3.1 选择真机设备
Command+B 编译,如果编译成功,framework的红色将消失
3.2 选择模拟设备
同样Command+B 编译,选择模拟器的设备
3.3 打包静态库
首先找到相应的文件夹
在查看静态库,查看支持的框架信息,进入SDK的文件夹,使用lipo -info SonSDK.framework/SonSDK
最后合成一个可以在模拟器和真机都能运行的静态库
lipo -create Debug-iphonesimulator/SonSDK.framework/SonSDK Debug-iphoneos/SonSDK.framework/SonSDK -output SonSDK.framework
四、 主要的问题
4.1 资源加载
因为资源文件到放在Bundle中,文件的读取将从Bundle中读取。
举例:获取一个图片的代码
- (UIImage *)imageNamed:(NSString *)name {
NSURL* url = [NSBundle.mainBundle resourceURL];
url = [url URLByAppendingPathComponent:@"SonBundle"];
url = [url URLByAppendingPathExtension:@"bundle"];
NSBundle *bundle = [NSBundle bundleWithURL:url];
return [UIImage imageNamed:name
inBundle:bundle
compatibleWithTraitCollection:UIScreen.mainScreen.traitCollection];
}
4.2 名称冲突
名称冲突,一定要注意找到那些文件引起的冲突,建议是在冲突文件上面加前缀。
引入源文件冲突,需要将源文件的前缀名,如ViewController.m引起了冲突,可以改为SNViewController.m
资源冲突,如icon.png,在sonAPP中,我们可以修改为sn_icon.png
4.3 运行时文件
因为运行时代码作用在APP的运行过程中,如果引起错误的现象,一定要注意
4.4 跳转到SonAPP
使用注册的方式跳转到SonAPP,在SonSDK.m中编写打开Url文件
- (BOOL)openURL:(NSURL *)url
{
// 1.scheme建议设置为app的名称
NSString *scheme = @"Son";
if ([url.scheme isEqualToString:scheme]) {
// 1.数据处理
NSLog(@"%s %@", __FUNCTION__, [self queryComponents:url]);
// 2.界面跳转处理
SonController *sonVC = [[SonController alloc]init];
NSLog(@"%s %@", __FUNCTION__, sonVC);
UIViewController *rootVC = [UIApplication sharedApplication].keyWindow.rootViewController;
if ([rootVC isKindOfClass:[UINavigationController class]]) {
UINavigationController *navVC = (UINavigationController *)rootVC;
navVC.topViewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"BACK" style:UIBarButtonItemStyleDone target:self action:@selector(backModel)];
[navVC pushViewController:sonVC animated:YES];
}else {
[rootVC presentViewController:sonVC animated:YES completion:nil];
}
return YES;
}else {
return NO;
}
}
如果入口控制器是从StoryBoard中加载的,需要修改控制器初始化方法,如:
- (instancetype)init
{
NSBundle *bundle = [NSBundle bundleWithURL:[[NSBundle mainBundle] URLForResource:@"SonBundle" withExtension:@"bundle"]];
self = [UIStoryboard storyboardWithName:@"Main" bundle:bundle].instantiateInitialViewController;
if (self) {
}
return self;
}
4.5 类别文件Category
在静态库中如果添加了类别文件,需要在主工程中
Target -- Build Setting -- Linking
修改 Other Linker Flags 为: -all_load
或 -force_load
4.6 添加tbd库的引用
因为动态库只限于在当前项目中使用,所以在静态库中添加tbd库会报错。
解决方案1:
在静态库中,使用dylib库取缔tbd。举例:如果使用了 libc++.1.tbd,需要替换成 libstc++.6.0.9.dylib,可以从添加按钮 - add other - 使用 shift + command + g
跳转到 /usr/lib/中找到 libstc++.6.0.9.dylib。
解决方案2: 在静态库中,不添加tbd库,在主工程中添加tbd库。
4.7 引用第三方库
在静态库中引用了第三方库,如果添加到主工程中报错,需要在主工程中,添加第三方库,注意:第三方库的配置也需要在主工程配置。