iOS Framework 实用开发技巧

时间:2020年7月

版本:Xcode 11.5 (11E608c)

语言:Objective-C

作者:非著名程序员


介绍自己在实际开发SDK过程中遇到的实际问题,以及解决方案。仅供参考。

  1. Framework与主工程(App)实时联调
  2. Framework、主工程(App)以及 Pod 等三个工程实时联调
  3. 使用脚本合并真机、模拟器等多种架构的Framework
  4. Framework中使用.bundle资源文件
  5. Framework中使用Category
  6. Framework支持bitcode


一、Framework与主工程(App)实时联调

① 为什么需要实时联调?

首先说明,Framework指的是我们自己开发的Framework(SDK),主工程(App)泛指那些要接入你的SDK的App。

朕之前在写SDK进行调试的时候都是先创建一个Framework的工程,代码都写完之后打包成Framework静态库,然后再把Framework拉到App的主工程中去验证功能。这样的调试方式没什么不可以,但是非常的浪费时间,需要一遍又一遍的打包Framework,而且Framework中的问题不可以打断点排查。

② 实时联调最终要达到什么效果?

实时联调最终要达到的效果是:仅需要让主工程跑起来就可以同时调试Framework和App两个工程的代码

这样一来,Framework不用每次都去打包一遍,再拖进App工程。大致的流程可以理解为:先build Framework,Framework编译完成后自动添加到App中,最后build App。


③ 干

1. 创建App工程,命名为RealDemo

image.png


2. 创建Framework工程,命名为RealSDK

image.png

注意路径!

image.png


3. 设置Framework工程的Build Settings

image.png
image.png
image.png


4. 创建WorkSpace,命名为RealDemo

image.png

注意路径!

image.png


5. 连接Framework工程和App工程

打开 RealDemo.xcworkspace,毫无疑问,空空如也


image.png

直接把需要连接的Framework工程和App工程拖进来即可


image.png

哎~你瞧,这就有了


image.png


6. 把Framework添加到App工程中

image.png
image.png

添加完是这样的


image.png




有过SDK开发经验的道友到这里应该已经看明白了,所谓实时联调说白了就是用WorkSpace把两个工程连接起来而已,跟Pod的原理有几分相似。

继续,咱们验证一下


7. 给Framework加点功能

增加一个RealDog类,定义一个eat方法,实现里面打印一句话“吃骨头”。最后在SDK的公开头文件RealSDK.h中集中引用一下。

image.png

// RealDog.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface RealDog : NSObject

- (void)eat;

@end

NS_ASSUME_NONNULL_END
// RealDog.m

#import "RealDog.h"

@implementation RealDog

- (void)eat {
    NSLog(@"吃骨头");
}

@end

// RealSDK.h

#import <Foundation/Foundation.h>

//! Project version number for RealSDK.
FOUNDATION_EXPORT double RealSDKVersionNumber;

//! Project version string for RealSDK.
FOUNDATION_EXPORT const unsigned char RealSDKVersionString[];

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


#import <RealSDK/RealDog.h>


8. 在App的ViewController调用一下SDK的方法

// ViewController.m

#import "ViewController.h"
#import <RealSDK/RealSDK.h>

@interface ViewController ()

@property (nonatomic, strong) RealDog *dog;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.dog = [[RealDog alloc] init];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self.dog eat];
}

@end

OK,实时联调到此结束。


二、Framework、主工程(App)以及 Pod 等三个工程实时联调

再加上Pod之后,三个工程实时联调其实也没什么难的,只要保证刚刚第一部分的工程文件夹层级关系,然后直接创建Podfile,pod install即可。

image.png

注意:此处Podfile里面的target是RealDemo!

platform :ios, '10.0'

target 'RealDemo' do
  
  pod 'AFNetworking', '~> 4.0.1'
  
end

三个工程顺利组到一起,全部可实时联调。

image.png

三、使用脚本合并真机、模拟器等多种架构的Framework

  1. 添加一个Aggregate Target。路径:RealSDK Project -> TARGETS -> "+"(左下角) -> Cross-platform - Other -> Aggregate
    WX20200718-110427@2x.png


  1. Aggregate Target 命名为“RealDemo-Script”
    WX20200718-110553@2x.png

    WX20200718-110835@2x.png


  1. 依赖RealSDK


    WX20200718-112151@2x.png


  1. 添加脚本


    WX20200718-112823@2x.png

    WX20200718-113154@2x.png

脚本源码

这个脚本是通用的,各位道友直接复制粘贴即可~

# Type a script or drag a script file from your workspace to insert its path.
UNIVERSAL_OUTPUTFOLDER=../Framework/

# 创建输出目录,并删除之前的framework文件
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
rm -rf "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework"

# 分别编译模拟器和真机的Framework
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos  BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphonesimulator BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build

# 定义真机、模拟器Build文件夹路径变量
IPHONE_BUILD=${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework
SIMULATOR_BUILD=${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework

# 拷贝framework到univer目录
cp -R "${IPHONE_BUILD}" "${UNIVERSAL_OUTPUTFOLDER}/"

#cp -R "${SIMULATOR_BUILD}" "${UNIVERSAL_OUTPUTFOLDER}/"

# 定义输出路径变量
OUTPUT_PATH=${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework

# 合并framework,输出最终的framework到build目录 
lipo -create "${IPHONE_BUILD}/${PROJECT_NAME}" "${SIMULATOR_BUILD}/${PROJECT_NAME}" -output "${OUTPUT_PATH}/${PROJECT_NAME}"



  1. 运行脚本


    WX20200718-120005@2x.png


  1. 运行结果


    WX20200718-115622@2x.png


  1. 检验一下是否合成成功


    WX20200718-120516@2x.png

四、Framework中使用.bundle资源文件

  1. 首先,我们先随便创建一个Bundle工程。


    WX20200718-152228.png


  1. Bundle工程命名为RealSDKResource。在Build Settings中修改Base SDK为iOS。
    WX20200718-152500.png


  1. 去掉Bundle文件的info.plist文件
    WX20200718-153145.png


  1. 修改Bundle文件名称(可依需求选择)


    WX20200718-153421.png


  1. 将Versioning System设置为None,默认Xcode会通过agvtool生成对应的版本信息,并打包进bundle文件中,这会导致后续在SDK跟随使用的App提交到AppStore的时候报错。


    WX20200718-153623.png


  1. 加一张图片资源,打包


    WX20200718-160219.png

    WX20200718-160246.png


  1. 测试一下RealSDK是否可以引用Bundle资源,SDK中什么都不用设置,直接引用Bundle资源即可。注意引用Bundle资源的方式:用字符串路径读取图片。还可以用NSBundle获取文件。
WX20200718-160609.png

用NSBundle获取文件

WX20200718-161242.png


  1. 把Bundle文件拖到App工程中,写一个测试代码
WX20200718-161415.png


  1. 运行起来,看一下效果,OJBK
WX20200718-161706.png

五、Framework中使用Category

在Framework工程的Build Setting中添加-ObjC。另外,使用我们SDK的App的Build Setting中也要添加-ObjC

在网上看到有人建议用-all_load,我个人建议使用-ObjC足矣。-all_load会比-ObjC范围更大,没有必要的事情就不要做,免得浪费性能。具体区别我就不在这里具体说了,网上一大片。

六、Framework支持bitcode

WX20200718-165002.png