iOS SDK 動態引入第三方SDK

引言

最近在做SDK,我們的SDK中需要集成很多第三方庫,比如Twitter、Facebook等,我們SDK中需要用到這些第三方SDK中的登錄、分享等功能,這些第三方庫我們不會打包到我們自己的SDK中,而是接入方通過Pod或者其他方式導入到他們的工程。接我們SDK的人需求是不一樣的,有的只用到Facebook、有的只用到Twitter。如果有一個接入我們SDK的人只用到Facebook,但是我們要求接入者不僅要導入Facebook的SDK,還要導入Twitter的SDK(因為SDK中集成了Twitter的東西,不導入會報錯),這樣是不是不太好,而且接入者可能會說,我們都沒有用到這個第三方,為什么要導入他們的SDK。

那么問題來了,我們要怎么樣才能做到我們SDK中既集成所有的第三方SDK,又能滿足接入者用到哪個第三方才導入對應的第三方SDK?

解決方案

經過一段時間的琢磨、想出了幾個方案:
方案一:在SDK內部判斷是否導入了某個第三方庫,然后通過預編譯的方式,是否要編譯某段代碼(這個方案行不通)
方案二:SDK內部通過runtime的方式調用第三方SDK的東西,通過動態的方式使用類,方法及屬性(這個方法可以)
方案三:SDK 分成兩個部分,一個部分是Framework部分,一個部分是源碼部分,結合方案一和方案二(最終的方案)

下面詳細的說說這三種方案,包括這三種方案使用時遇到的困難以及優缺點

注意:本文中的代碼只是為了簡單的測試,在正常開發的時候,最好進行一些相應的判斷,以免引起不必要的麻煩

方案一

直接上代碼,這是.m文件所有示例代碼

#import "Test.h"

// 判斷是否存在TwitterKit/TWTRKit.h,如果存在說明導入了Twitter的SDK
#define TwitterSDKModule __has_include(<TwitterKit/TWTRKit.h>)

// 在導入了Twitter的SDK情況下,才引入
#if TwitterSDKModule
@import TwitterKit;
#endif

// 遵循Twitter的某個協議,以及使用Twitter的某個類的對象最為屬性
#if TwitterSDKModule
@interface Test()<這兒是Twitter的某個協議>
@property (nonatomic, strong) TWTRTwitter *twitter;
@end
#endif

// 跟Twitter沒關系的屬性
@interface Test()
@property (nonatomic, copy) NSString *message;
@end

@implementation Test

#if TwitterSDKModule

/// 調用Twitter的方法
- (void)loginByTwitter {
    [[Twitter sharedInstance] logInWithCompletion:^(TWTRSession * _Nullable session, NSError * _Nullable error) {
        
    }];
}

#endif

@end

原本信心滿滿,但是慘慘招打臉,為什么這種方式不行呢?原因就在于我們打包Framework時,我們SDK內部的代碼就編譯好了,是否存在<TwitterKit/TWTRKit.h>在打包Framework時就決定了,而不是取決于接入者的工程中是否包含<TwitterKit/TWTRKit.h>。所以這種方式宣告失敗

方案二

使用runtime方式。我們可以通過runtime的方式,在不引入頭文件的情況下進行某個類的調用
具體參考代碼如下:

    // 獲取Person類
    Class personClass = NSClassFromString(@"Person");
    // 獲取方法編號
    SEL shareSEL = NSSelectorFromString(@"shared");
    // 調用Person類的類方法
    [personClass performSelector:shareSEL withObject:nil];
    // 創建實例化對象
    id person = [[personClass alloc] init];
    // 獲取對象的屬性
    NSString *name = [person valueForKey:@"name"];
    // 給屬性賦值
    [person setValue:@"xiao hai" forKey:@"name"];
    // 獲取方法編號
    SEL runSEL = NSSelectorFromString(@"run");
    // 實例方法的調用
    [person performSelector:runSEL withObject:nil];

這里獲取類、調用類方法、獲取屬性值、給屬性賦值、調用實例方法都有了,那么問題來了,我們要怎么去遵循協議、實現協議的方法,常規寫法如下:

@interface Test ()<PersonDelegate>
@end

@implementation ViewController
- (void)eatWithPerson:(Person *)person {
    
}
@end

那么問題來了,如果不引入頭文件的情況下,PersonDelegate、Person都是未知的,代理方法里還有很多未知的類,我們應該如何解決,首先“@interface Test ()<PersonDelegate>”中, PersonDelegate代理完全不用寫,之所以要寫,我個人理解是為了寫代碼方便,減少寫代碼帶來的錯誤
遵循代理解決了,那么代理方法的實現怎么解決,Person是未知的,直接寫上去肯定會報錯,我們只需要將Person用id代替就行了,之所以可以這么寫,是因為這兩種寫法,方法名是一樣的,都是“eatWithPerson:”,這樣就解決了代理相關問題,簡直perfect,實例代碼如下:

@interface Test ()
@end

@implementation Test
- (void)eatWithPerson:(id)person {
    
}
@end

本以為問題都解決了,但是又遇到問題了,performSelector方法不支持多參數,于是苦苦尋找解決方法,終于在GitHub上找到了解決辦法,MXRuntimeUtils幫我解決了這個問題,非常感謝MXRuntimeUtils的作者。本應該再次高興時,問題又又又出現了,MXRuntimeUtils不支持block類型的參數,于是我對MXRuntimeUtils做了改進,增加了對block類型參數的支持,如果有需要的小伙伴可以聯系我
這種方式終于結束了,下面我說說這種方式很大的一個弊端,每個使用第三方SDK的地方都要用這種方式來寫,我只能說太累,太麻煩了

方案三(最終方案)

把方案一和方案二結合,并進行改進。我以SDK集成Facebook、Twitter為例子,下面我直接給出我們SDK在這塊的架構示意圖


7E80D089C5A36B2FAA30D77A4FF12579.jpg

我針對這個圖解釋一下,整個SDK分成兩個部分,Framework部分和源文件部分,Framework部分是我們常規把包達成Framework的形式,源文件部分,主要是對第三方SDK的接口進行二次封裝,直接向SDK接入者暴露源代碼(完全不牽扯到我們SDK內部業務,代碼暴露給接入者沒什么關系)。源文件中的Adapter(下面簡稱A)是對需要的第三方接口進行整合,比如我們SDK需要集成Twitter和Facebook的登錄功能,A中就提供一個登錄接口(通過一個參數來判斷要調用Twitter的登錄還是Facebook的登錄)源文件中的對Twitter、Facebook接口的二次封裝,通過方案一中使用的方法封裝。Framework中的Adapter(下面簡稱B)是對A進行翻譯,供Framework部分中其他地方直接調用。B是framework內部文件,A是Framework外部文件,B如何調用A呢,可以通過方案二中的runtime形式調用。
分析:這種方案也用到了跟方案一中幾乎一樣的方案,為什么方案一達不到效果,而這種方案可以,原因在于在這個方案中,把方案一中用到的東西放到了源代碼中。這兩種方案,區別在于我們寫的有關預編譯的部分的編譯時機,方案一,預編譯部分的代碼在打包Framework的時候就編譯好了,而這種方案,預編譯部分的代碼在接入方編譯時才編譯,達到了我們想要的效果。

結束語

如果有什么寫的不對的地方,歡迎指正,如果有什么不明白的地方也很歡迎一起探討
作者聯系方式:QQ 782304472 (要加的小伙伴請寫明在此文章中看到的,謝謝)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 一、說明 從2019年3月份接手游戲SDK任務以來,我們完善了 登錄模塊、支付模塊、事件 統計模塊、分享模塊 等功...
    hylccmh閱讀 1,966評論 1 1
  • 這幾天公司有一個新的需求,就是要將我們開發的某些東西都抽取出來,打個比方我們開發了A項目,可以以后B項目要包含A項...
    黃魚兒啦啦啦閱讀 6,190評論 2 16
  • 你一成不變的冷漠 讓我在八九年的八月 渡過一個冬天 在最最敏感的時候 你凄婉的作別 無論是不是玩笑 都重創我易受傷...
    夫瑋閱讀 349評論 6 13
  • 業務流程時序圖 仔細閱讀并看懂 (不是看過,是看明白+看完 ?) 微信官方文檔 微信開放平臺文檔; 1.下載微信支...
    刀魚要到島上掉閱讀 4,254評論 0 4
  • ?“當我開始數算所獲得的恩福,我的生命就立刻煥發光彩。”? ??感恩樓里的一個鄰居,每次看見我都會沖著我微笑并且打...
    Ivy亞琪閱讀 215評論 0 0