優雅的開發 Swift 和 Objective-C 混編的 Framework
前言
為什么要寫這樣一篇文章,因為昨天和一個朋友討論到Swift和Objective C如何混合開發Framework,中途發現了很多有意思的坑。
用Swift封裝OC的庫是一件比較常見的事情,畢竟對于大多數公司來說,老的代碼都是用OC寫的,而且經過多次迭代,這些OC的代碼已經被驗證了是穩定的,用Swift重寫代價太大。這就引入了一個需求:
- 用Swift和OC來混編一個Framework。
如果你之前沒有用Swift和Objective C混合開發,建議看看這篇文檔:
這篇文檔很詳細的講解了如何運用Objective C和Swift進行混合開發App和Framework。于是,我們先按照文檔來寫一個混編的Framework
按照文檔一步一步來
新建一個基于單頁面工程,然后新建一個一個Target,選中Cocoa Touch Framework。然后,分別新建一個Swift文件和Objective C類,注意Target Member Ship選中Framework。類的內容如下:
OCSource.h
#import <span class="hljs-title"><Foundation/Foundation.h></span>
@interface OCSource : NSObject
- (void)functionFromOC;
@end
</code></pre>
OCSource.m
#import "OCSource.h"
@implementationOCSource
- (void)functionFromOC{
NSLog(@"%@",@"Log from objective c in framework");
}
@end
</code></pre>
Swift調用OC
新建SwiftSource.swift
open class SwiftIt{
public init(){}
letocObject = OCSource()
public funcencapsulate(){
ocObject.functionFromOC()
}
}
然后,按照文檔中, 為了讓Swift文件訪問Objective C文件 ,我們應該在umbrella header,也就是 MixFramework.h 中,暴露所需要的header。
也就是, MixFramework.h ,
#import <MixFramework/OCSource.h>
然后,自信滿滿的點擊build。
Boom~~~,編譯不通過。

原因:OCSource.h默認編譯的時候是Project權限. 為了在umbrella header中使用,要把這個文件的權限改成Public
按照圖中的方式拖過去即可。

嗯,現在build,可以看到build成功了。
OC調用Swift
在SwiftSource.swift中,增加一個類,
openclass ClassForOC:NSObject{
public static lettextForOC = "textForOC"
}
然后,為了在OC中調用Swift的方法,我們需要導入頭文件,這時候,OCSource.m文件內容如下
#import "OCSource.h"
#import <MixFramework/MixFramework-Swift.h>
@implementation OCSource
- (void)functionFromOC{
NSLog(@"%@",[ClassForOC textForOC]);
}
@end
</code></pre>
然后,build,發現成功了,很開心。
外部調用
在ViewController.swift中,我們調用Framework中的內容。
importMixFramework
class ViewController: UIViewController {
var t = SwiftIt()
overridefuncviewDidLoad() {
super.viewDidLoad()
t.encapsulate()
// Do any additional setup after loading the view, typically from a nib.
}
overridefuncdidReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
然后運行,發現控制臺打印出
2017-03-02 16:08:24.000 HostApplication[19524:167669] textForOC
嗯,framework打包成功了。
問題
通常,我們希望暴露給外部的接口是 純Swift,而OC文件的具體接口應該隱藏 ,這就是我標題中的優雅兩個字的含義。
如果你好奇,你會發現,在ViewController.swift中你可以這么調用
var s = OCSource()
也就是說,OC的內容也暴露出來了, 這破壞了Framework的封裝特性 。
通過查看MixFramework的編譯結果,發現最后暴露出的接口是這樣子的
importFoundation
importMixFramework.OCSource
importMixFramework
importMixFramework.Swift
importSwiftOnoneSupport
importUIKit
//
// MixFramework.h
// MixFramework
//
// Created by Leo on 2017/3/2.
// Copyright ? 2017年 Leo Huang. All rights reserved.
//
//! Project version number for MixFramework.
public var MixFrameworkVersionNumber: Double
openclass ClassForOC : NSObject {
public static lettextForOC: String
}
open class SwiftIt {
public init()
public funcencapsulate()
}
這一行,把OC對應的實現暴露出來了
importMixFramework.OCSource
優雅的解決方案
不再通過umbrella header的方式讓framework中的Swift調用OC方法。而是通過 modulemap 。
新建一個 module.modulemap 文件,內容如下
module OCSource [system] {
//由于module.modulemap和OCSource.h是在同一個文件夾的,如果不是同一個,路徑要寫全
header "OCSource.h"
export *
}

這里的#(SRCROOT)是XCode的宏,會自動替換成項目所在的根目錄,這里輸入的路徑是module.modulemap文件所在的路徑。
然后,刪除 MixFramework.h (umbrella header)中#import 的OC header。
把OCSource.h的權限改回默認的project。

再編譯,發現OC的類被隱藏了。
總結
如果你要開發一個framework,一定要想清楚哪些接口暴露出去,哪些封裝起來,framework不是簡單把一包文件加個殼子。
來自:http://ios.jobbole.com/93115/