Swift 挺好用的,但写给 OC 调用的 SDK 过程中遇到不少坑。
此教程包含,使用 Swift 代码,调用 OC 库(比如 CocoaAsyncSocket),一键打包支持 Bitcode 的 Framework ,兼容 OC 项目调用等
目录
- 新建 Project
- Swift Framework 调用 OC (无需求可略过)
- 方便调试的 Framework
- 一键打包通用的 Framework
1. 新建 Project
1. 1 SwitfHello Framework
新建名为SwiftHello的Framework Project并添加一个Swift类文件SwiftHello
import Foundation
@objc(SwiftHello)
public class SwiftHello: NSObject {
@objc(hello)
public func hello() {
print("Swift Hello")
}
}
设置 Mach-O Type 为 Static Library
这时候第一个坑来了
默认是 Dynamic Library,这里必须设置成 Static Library。
苹果并不支持第三方 Framework 是动态的,只能是静态的。
Swift 项目引入的话不会报错,然而 OC 项目,引入后 run 即使什么也不做,也会报如下的错。
如果根据下面的关键字去搜索,就走上不归路了,基本都是叫大家添加库什么的
dyld: Library not loaded: @rpath/libswiftCore.dylib Referenced from: /Users/Jeans/Library/Developer/CoreSimulator/Devices/152765AA-E53B-4E93-BC59-61099A950C9D/data/Containers/Bundle/Application/E32F6BFA-39E5-41E8-A180-8725D75EBCB5/OCTestDemo.app/Frameworks/SwiftHello.framework/SwiftHello Reason: image not found
1. 2 OCTestDemo 调用Framework
打开 SwiftHello 项目,选中模拟器,build ,Show in Finder
将得到的 Framework 直接拖进 OCTestDemo
并在 Embedded Binaries 添加 SwiftHello.framework ,如下图所示
build 一下,妥妥的如下图所示,但是Framework是没问题的
第二个坑来了
这种情况只有在纯 OC 项目中会出现,纯 Swift 或 混编含 Swift 的项目都没问题。
这里新建个Swift 跟桥接文件就解决了。
创建桥接
结果
选择模拟器然后就可以 run 成功了,输出
2. Swift Framework 调用 OC (无需求可略过)
一般 Swift 项目调用 OC 代码,需要建立桥接文件,但是在 Framework 制作中是不允许桥接文件的。以下以调用 GCDAsyncSocket 示例,直接源码方式引入。
将 OC 文件拖进项目
头文件拖进去这里 Build Phases -> Headers -> Public
在 SwiftHello.h 添加一行
#import <SwiftHello/GCDAsyncSocket.h>
SwiftHello.h 这里的作用挺像桥接文件的,但是不完全是。
到这里,OC 的代码就引入完成了,在 Swift可以随意调用了, SwiftHello.swift 更新如下
import Foundation
@objc(SwiftHello)
public class SwiftHello: NSObject, GCDAsyncSocketDelegate {
@objc(hello)
public func hello() {
print("Swift Hello")
}
private lazy var socketQueue: DispatchQueue = DispatchQueue(label: "socketQueue")
private lazy var server: GCDAsyncSocket = GCDAsyncSocket(delegate: self, delegateQueue: self.socketQueue)
@objc(startListening)
public func startListening() {
do {
try server.accept(onPort: 6666)
print("监听成功")
}catch (let error) {
print("监听失败:\(error.localizedDescription)")
}
}
// MARK: - GCDAsyncSocketDelegate
/// 有客户端接入
public func socket(_ sock: GCDAsyncSocket, didAcceptNewSocket newSocket: GCDAsyncSocket) {
print("连接成功,连接地址:\(newSocket.connectedHost!),端口:\(String(newSocket.connectedPort))")
}
}
再 build 一下,把新生成的 Framework 文件替换到 OCTestDemo 项目中,更新代码
SwiftHello *h = [[SwiftHello alloc]init];
[h hello];
[h startListening];
输出 监听成功
3. 方便调试的 Framework
每次 build 一下,然后再拖 Framework 到 Demo 中调用测试很不方便。
新建一个 FrameworkTestDemo.xcworkspace
分别将 OCTestDemo.xcodeproj 和 SwiftHello.xcodeproj 拖进 workspace 中
删除原本 OCTestDemo 项目中的 Framework
重新再添加
OK ! Build 一下,妥妥的!
4. 一键打包通用的且支持 Bitcode 的 Framework
用 lipo 命令能看到,刚才 build 的 Framework, 只包含 x86_64 ,这只能在 64 位的模拟器上跑
lipo -info SwiftHello.framework/SwiftHello
输出
input file SwiftHello.framework/SwiftHello is not a fat file
Non-fat file: SwiftHello.framework/SwiftHello is architecture: x86_64
新建一个 target,Cross-platform -> Aggregate
新建一个脚本
# Merge Script
# 1
# Set bash script to exit immediately if any commands fail.
set -e
# 2
# 这里替换成自己的 Framework 名称
FRAMEWORK_NAME="SwiftHello"
# 3
# If remnants from a previous build exist, delete them.
if [ -d "${SRCROOT}/build" ]; then
rm -rf "${SRCROOT}/build"
fi
# 4
# Build the framework for device and for simulator (using
# all needed architectures).
xcodebuild -target "${FRAMEWORK_NAME}" -configuration Release -arch arm64 -arch armv7 -arch armv7s only_active_arch=no defines_module=yes BITCODE_GENERATION_MODE=bitcode OTHER_CFLAGS="-fembed-bitcode" -sdk "iphoneos"
xcodebuild -target "${FRAMEWORK_NAME}" -configuration Release -arch x86_64 -arch i386 only_active_arch=no defines_module=yes BITCODE_GENERATION_MODE=bitcode OTHER_CFLAGS="-fembed-bitcode-marker" -sdk "iphonesimulator"
# 5
# Remove .framework file if exists on Desktop from previous run.
if [ -d "${HOME}/Desktop/${FRAMEWORK_NAME}.framework" ]; then
rm -rf "${HOME}/Desktop/${FRAMEWORK_NAME}.framework"
fi
# 6
# Copy the device version of framework to Desktop.
cp -r "${SRCROOT}/build/Release-iphoneos/${FRAMEWORK_NAME}.framework" "${HOME}/Desktop/${FRAMEWORK_NAME}.framework"
# 7
# Replace the framework executable within the framework with
# a new version created by merging the device and simulator
# frameworks' executables with lipo.
lipo -create -output "${HOME}/Desktop/${FRAMEWORK_NAME}.framework/${FRAMEWORK_NAME}" "${SRCROOT}/build/Release-iphoneos/${FRAMEWORK_NAME}.framework/${FRAMEWORK_NAME}" "${SRCROOT}/build/Release-iphonesimulator/${FRAMEWORK_NAME}.framework/${FRAMEWORK_NAME}"
# 8
# Copy the Swift module mappings for the simulator into the
# framework. The device mappings already exist from step 6.
cp -r "${SRCROOT}/build/Release-iphonesimulator/${FRAMEWORK_NAME}.framework/Modules/${FRAMEWORK_NAME}.swiftmodule/" "${HOME}/Desktop/${FRAMEWORK_NAME}.framework/Modules/${FRAMEWORK_NAME}.swiftmodule"
# 9
# Delete the most recent build.
if [ -d "${SRCROOT}/build" ]; then
rm -rf "${SRCROOT}/build"
fi
设置成 10
选中 UniveralBuilder, Build 一下,桌面就生成了 Framework
再看一下lipo,已经变成如下,可以把这个发布出去了
Architectures in the fat file: SwiftHello are: armv7 armv7s i386 x86_64 arm64
本文示例的完整 demo 已放上 这里 如果遇到奇怪问题,检查下 Build Settings 相关设置是否一致