在Objective-C的SDK内部采用Swift混编(中)

1,392 阅读2分钟

装模作样写个前言

背景:拥有悠久历史的大型SDK,这个历史当然是Objective-C。又当然这个是没有问题的,而且每天都在服务上千万用户,但是不巧的是Apple出了一个StoreKit2,按理说每年苹果爸爸都要出很多API,这很正常。但是又不巧的是它只支持 Swift 编写,而且这样的API我估计只会成为常态,所以 Swift 化已经刻不容缓了。

结果:半夜执笔

温馨提示:本文实例代码已上传github,附录于(下)中。

编译环境:Xcode13.0 Swift5.5 arm64

如有不正,请在评论区告知我。

一、 在SDK内部OC调用Swift

|新建两个 Swift 文件

import UIKit

@objc(TESTSWIFT)
@objcMembers
public
class TestSwift: NSObject {
    public static func run() -> Void {
        print("(#fileID): (#function)")
        TestSwift2.run()
    }
}


//TestSwift2.swift


class TestSwift2: NSObject {
    static func run() -> Void {
        print("(#fileID): (#function)")
    }
}

解释一下:

  • @objc 在这里主要是为了修改类的名称。

  • @objcMembers在类名前面是使用objcMembers修饰,系统会在自动给这个类的所有方法添加@objc,暴露给OC。

  • public 是为了暴露给外部OC调用者。

|在OC中调用该Swift类

#import "TestOC.h"
#import "TestOC2.h"
#import <SWSDK/SWSDK-Swift.h>
@implementation TestOC

+ (void) run  {
    NSLog(@"TestOC:SDK外放OC类");
    [TESTSWIFT run];
    [TestOC2 run];
}

@end
  • 这里主要操作是 #import <SWSDK/SWSDK-Swift.h>, 所有OC调用 Swift 的地方必须导入该头文件,为了方便导入,我们可以在 PCH 文件中加入该头文件。

  • 我们可以看到Swift内部类不需要添加 @objc、@objcMembers、public 等关键词。

|我们来比对一下加入Swift前后的文件结构变化

. //无Swift
├── Headers
│   ├── SWSDK.h
│   └── TestOC.h
├── Info.plist
├── Modules
│   └── module.modulemap
├── SWSDK
└── _CodeSignature
    ├── ...


. //增加Swift
├── Headers
│   ├── SWSDK-Swift.h
│   ├── SWSDK.h
│   └── TestOC.h
├── Info.plist
├── Modules
│   ├── SWSDK.swiftmodule
│   │   ├── ...
│   └── module.modulemap
├── SWSDK
└── _CodeSignature
    ├──...
    
//SWSDK-Swift.h文件
SWIFT_CLASS_NAMED("TestSwift")
@interface TESTSWIFT : NSObject
+ (void)run;
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end
  • 增加了 SWSDK-Swift.hSWSDK.swiftmodule 目录。 PS: Swiftmodule文件夹需要模拟器与真机合并才可以对外使用。

  • SWSDK-Swift.h 主要是为了方便内部OC调用。这里 Headers 的增加: 没有进行Build Phases 的 public 拖拽,属于自动外放。

  • SWSDK.swiftmodule:公共接口的定义文件。


Question:有人可能有疑问,为什么SWSDK-Swift.h变成外放了?

|我们一起来看看modulemap文件的前后变化

framework module SWSDK { 
  umbrella header "SWSDK.h"

  export *
  module * { export * }
}

//加入Swift后新增的SWSDK.Swift
module SWSDK.Swift {
    header "SWSDK-Swift.h"
    requires objc
}

|所以内部可以调用SWSDK-Swift.h,由 modulemap 外放到 Headers 中,所以按逻辑来说,在外部工程中也可以显式的调用 SWSDK-Swift.h。

在AppDelegate.h中调用
#import "AppDelegate.h"
//#import <SWSDK/SWSDK.h>
#import <SWSDK/SWSDK-Swift.h>
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//    [TestOC run];
    [TESTSWIFT run];
    return YES;
}

//输出
SWSDK/TestSwift.swift: run()
SWSDK/TestSwift2.swift: run()

|所以外部类也可以正常调用Swift,只需要加上 #import<SWSDK/SWSDK-Swift.h> 头文件。

下一篇我们将介绍SDK内部Swift调用OC的Module化方法

在Objective-C的SDK内部采用Swift混编(下)