內容概述
SDK定義上是指軟件開發包,對應iOS端來說就包含了庫文件、頭文件、資源文件等文件的集合。SDK開發就是在保證sdk源代碼安全的情況,給開發者一個可方便快速接入的,兼容多個iOS系統、便于真機模擬器調試、可以上線AppStore的庫。這句話包含的幾個重要的信息將在本文中逐步詳解。
本文將從iOS系統中SDK開發過程中需要考慮和注意這兩方面入手,講述SDK的設計:
1. 工程設置
2. 接口設計
3. SDK需要考慮的方面
4. checklist
工程設置
創建一個SDK工程通常通過下面方式:
目前SDK主流開發方式是第一種,即工程的產物提供framework,因為可以將庫和頭文件、資源都放在其中,當然可以選擇第二種,工程的產物是靜態庫,如果分發需要將頭文件與庫一并提供。
對于framework來說,需要注意xcode6以后framework在Xcode默認配置的是動態庫:
“Frameworks for iOS. iOS developers can now create dynamic frameworks. Frameworks are a collection of code and resources to encapsulate functionality that is valuable across multiple projects. Frameworks work perfectly with extensions, sharing logic that can be used by both the main application, and the bundled extensions.”
大意是xcode6(iOS8)后開發者可以創建自己的動態framework,其中包含可以跨工程使用的代碼、資源。動態framework只能用于企業證書分發的ipa包中,對于上傳appstore的所有產品必須使用靜態framework,否則會被拒絕。但是系統提供的動態庫dylib和framework是可以使用的,? 更正為:iOS8開始,apple放開了動態庫的使用,用戶可以通過xcode創建動態庫的framework,并且集成到app中,提交到appstore已經不會被拒絕。動態庫和靜態庫的區別可參考之前的文章,? 下圖是靜態framework的使用:
所以注意工程需要配置一下:
對于動態framework的使用稍有區別,需要單獨將framework嵌入到ipa安裝包中,因為動態庫的特點就是運行時加載,所以app的可執行程序中并不會鏈接其使用的動態庫,需要將動態庫和可執行程序同時放在安裝包中:
此工程編譯打包出來的app文件中,如下圖所示,會增加一個Frameworks文件夾,里面存放的正式嵌入的動態庫:
接口設計
總體來說SDK接口設計和類的設計相似:
1. 首先需要根據SDK中模塊劃分根據“單一職責原則”、代碼的依賴性分為不同的類;
所謂單一職責其實就是高內聚,保證一個類、一個接口只完成一個職責,不貪大貪全。
2. 接口設計也要滿足“單一職責原則”;
例如即時通信的群組消息中一個群大概有群名稱、群成員、群主、群頭像、群主題等幾個信息要素,我們設計接口時不能因為簡便而用一個或兩個接口提供所有這些信息,而是應該根據常見業務將上面信息分為以下接口: 獲取群信息(群主、名稱、頭像), 獲取群成員、獲取群主題 這三個接口,或者根據情況將獲取群信息分解為三個獨立的接口。
這樣設計的好處就是業務調用方可以根據實際情況某些case只調用其中一個接口,等到其他case再調用其他接口,這樣可以保證每個接口的信息量沒那么大,保證請求的及時響應和信息的及時展示。
3. 接口命名可讀性強,拼寫正確;?
要避免以下幾種形式的接口命名:
`createGroup:(NSString *)title`
`GroupWithTitle:(NSString *)title`
`creatGroupWithTitle:(NSString *)title`
三種命名分別的問題是: 第一種命名沒有對參數進行描述, 第二種命名沒有對接口的動作進行描述,第三種命名create單詞拼寫錯誤。
所以一個正確的命名應該是保證拼寫正確的情況下,按照(省略主語)謂賓這樣的格式:
`createGroupWithTitle:(NSString *)title`
4. 接口參數校驗:考慮用戶使用情況,給予充分提示,減少問答
有些接口圖方便可能設置成這樣:
- (void)queryGroupMasterViaGroupId:(NSString *)groupId? requestSucess:(RequestGroupMasterSucess)success requestError:(void(^)(NSString *errorType)) error;
可以看出對于錯誤的反饋是通過參數error的block回調出來,block中的errorType對應錯誤信息,但這樣的接口設計可能無法滿足開發者的需求:只有錯誤描述字符串,實際開發者可能使用一個int型code用于判斷錯誤性能更高,代碼更清晰,即使直接將errorType字符串的錯誤以toast形式展示出來,這個字符串也有可能不是開發者實際想要的提示語。
所以最好的錯誤回調應該是至少包含錯誤碼,最好包含錯誤描述(中英文兩種),如下面例子:
-(void)requestGroupMasterViaGroupId:(NSString *)groupId? requestSucess:(RequestGroupMasterSucess)success requestError:(RequestGroupMasterError) error;
其中回調的枚舉定義如下:
typedef void (^RequestGroupMasterError) (CMIMError * error);//獲取群主詳情失敗回調
回調的參數是一個CMIMError實例:
@interface CMIMError : NSObject
@property (nonatomic, assign) CMIMErrorCode code;
@property (nonatomic, readwrite, copy) NSString *codeDescription;
@property (nonatomic, readwrite, copy) NSString *detailDescription;
+ (instancetype)errorWithCode:(CMIMErrorCode)aCode;
- (instancetype)initWithCode:(CMIMErrorCode)aCode;
從該實例的定義可知道,實例中包含錯誤碼和錯誤簡單描述、詳細描述。
需要考慮的方面:
SDK開發就是在保證sdk源代碼安全的情況,給開發者一個可方便快速接入的,兼容多個iOS系統、便于真機模擬器調試、可以上線AppStore的庫。這句話包含的幾個重要的信息將在本文中逐步詳解。
還記得開篇時說的這個定義? 這個定義對應了下面幾個需要注意的地方:
1. 必要調試信息
對于我們提供給別人的庫,默認應該是關閉log選項的,但是有些時候為了協助客戶或者開發者定位SDK中問題,可能需要log信息,這樣我們在設計時需要在代碼中配置log,可以參考大牛的框架,有整個log的各個級別的開關,控制log的總量和詳細程度。
2. category、第三方庫、類的命名、宏定義命名需要獨一無二
我們xcode編譯鏈接時經常會報出這樣的錯誤:盜個圖:
這類問題都是由于重復的定義導致,包括類名的同名、宏定義的同名、category的同名等,解決方法最簡單的就是將同名之一改成其他名字,如果無法修改則需要通過lipo指令來解決。
3. 頭文件、屬性暴露合理
保證代碼安全就是保證頭文件暴露合理,并且頭文件中只暴露開發者需要用到的屬性(定義好readonly屬性)和接口,這樣可以保證開發者不會錯誤調用其他接口或屬性使SDK內部狀態或邏輯錯亂。
4. 支持的最低系統
通過app可能根據用戶群體有不同的最低系統支持,例如目前很多app已經不再支持ios8以下系統,但是SDK作為app的內核,需要保證不同客戶的需求都能滿足,所以SDK應該盡可能保證支持最低系統,一般到iOS6.
5. 支持模擬器調試
為了便于開發者調試,需要支持模擬器調試,所以在打包framework時需要有x86和i386版本,并通過lipo指令將二者合并到armv7 v7s arm64中去。
6. crash跟蹤
為了及時定位客戶使用SDK導致的crash,SDK中也要處理crash信息,并及時上報給我們。不建議使用友盟或bugly等商用SDK來收集crash,因為開發者一般也會集成這類SDK,所以很容易沖突,最好是自己寫代碼實現,原理首先是收集到程序的exception或signal,這時將相應的堆棧信息打印輸出上傳到服務器或者我們的郵箱中。
發布前checklist
為了保證每次上線的準確無誤,必須制定一個完備的checklist,每次發布之前對照checklist來逐個檢查,才能保證各項準備達到。下面列出幾個基本的檢查項:
1. 系統架構是否覆蓋完全,
使用lipo指令查看支持的架構,模擬器是否支持? arm64必須支持,否則無法上架
2. 版本號及發布日志?
確定準確的版本號,并根據當前版本詳細描述此次發布的更新點,這樣方便開發者熟知改動的地方,便于評估對以前版本影響和使用新版本的成本,也便于我們定位問題。
3. log系統
關閉log輸出,并屏蔽一些開發的調試代碼或者提示框。對于一些尚不穩定的初始幾個版本,由于問題比較多,開發者可以暫時打開log開關,這樣在出現問題的時候便于定位,等一些版本后相對穩定之后,關閉log。
注意:日志需要制定相應的清除策略,最基本的包含每個log文件多大,最多幾個log文件,log文件總大小達到多少時開始覆蓋之前的文件等,其實這些方面大神的源碼都考慮好了,直接用輪子更省心。
4. 冒煙測試
發布之前需對預發布版本進行主流程的測試,例如對于一個即時通信SDK需要測試普通消息互通、文件類消息互通、離線消息收取、apns推送正常、群消息正常等。
p.s. 本文提到的一些[命令]將在后面文章中單獨講一下。