Swift打包Framework,兼容Objective-C项目调用(Xcode9.4 iOS11

Swift 挺好用的,但写给 OC 调用的 SDK 过程中遇到不少坑。
此教程包含,使用 Swift 代码,调用 OC 库(比如 CocoaAsyncSocket),一键打包支持 Bitcode 的 Framework ,兼容 OC 项目调用等

目录

  1. 新建 Project
  2. Swift Framework 调用 OC (无需求可略过)
  3. 方便调试的 Framework
  4. 一键打包通用的 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 相关设置是否一致

转载于:https://my.oschina.net/jeans/blog/1913466

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值