iOS兩個客戶端代碼復用小技巧

SangDahl 7年前發布 | 13K 次閱讀 Git CocoaPods iOS開發 移動開發 UITableView

一般一個App只有一個客戶端,因此也只有一份代碼倉庫,也就無所謂復用不復用。但兩個客戶端也不是沒有,美團、餓了么等就分商家版和買家版,貝聊App也分為老師版和家長版兩個客戶端。有兩個客戶端代碼復用就在所難免,比如基本的工具類,比如一些共用的業務。本文就以貝聊App為例,分享當有兩個客戶端時代碼復用的小技巧。

repository倉庫劃分

貝聊APP遠程倉庫劃分為三個,一個 家長端repository ,一個 老師端repository ,一個兩端共用的 BLKit repository 。

家長端的本地 repository 包含遠程的 家長端repository 和兩端共用的 Kit repository 。

老師端的本地 repository 包含遠程的 老師端repository 和兩端共用的 Kit repository 。

用submodule管理共用模塊BLKit

一個端要包括兩個 repository ,而且這兩個 repository 還是相互依賴,怎么管理也是個問題。

git 中 submodule 允許在一個 主git 中存在另外獨立的 子git ,而且 主git 還能記錄每一次 commit 中 子git 所在的 commit ,這是完美的倉庫獨立卻又相互依賴的管理方案。

如下圖:

通過cocoapods將BLKit引入項目

雖然利用 submodule 可以很好的管理共用模塊 BLKit ,但又如何將 BLKit 引入到 project 中呢?

有兩個方案,

直接加入主project

可以通過 add files to project ,選擇所有 BLKit 中的文件,添加到 主project 中。但這樣做的隱患非常大:

  • BLKit不獨立

    BLKit 是兩端共用模塊,因此 BLKit 只能使用 BLKit 內部的類,否則在另外一個端中是不能編譯的。如果 BLKit 的文件被添加到 主project 中,很容易在 BLKit 的某個文件中初始化了主項目中的一個類,導致 BLKit 很難維護。

  • BLKit不能更新

    BLKit 直接添加到 主project 中,如果一端有添加新文件,另外一端拉取下來后,還得手動通過 add files to project 將新添加的文件添加到 主project 中。如果每次只是添加一個文件還好,如果文件一多,想死的心都有。

通過cocoapods管理

將 BLKit 像其他第三方依賴庫一樣,用 cocoapods 管理,只不過 pods 的文件源連接到本地的 BLKit 倉庫而已。

首先在主項目中的 submodule 文件夾中,創建一個 BLKit.podspec ,然后按照 podspec 的規則,填寫 podsspec ,然后在主項目中的 Podfile ,像添加普通 pod 一樣,將 BLKit 添加到 Podfile 中,

pod 'BLKit', :path => "./BLKit/"

然后 pod update 即可,這時 BLKit 就像普通的第三方依賴庫一樣,出現在 pods 中:

用 cocoapods 管理好處是很明顯的:

  • BLKit完全獨立
    BLKit 由于在 pods 中,不能引用主項目中的類,完全獨立,不會出現一端更新,另外一端不能使用的情況。
  • BLKit自動更新
    由于 BLKit 其實就是個 pods 而已,如果 遠程BLKit 有更新,拉取到本地后,運行 pod update --no-repo-update 即可。千萬加上 --no-repo-update 這個選項,因為 pod update 很慢很慢很慢。(當然如果利用 proxychain , pod update 也很快,但實在沒有必要為樂更新 BLKit 更新整個 pod )

如何在BLKit中引用主項目業務模塊

兩端共用的模塊,一般都是基礎功能模塊,比如網絡模塊,數據緩存模塊、圖片下載模塊、視頻發送模塊等,都完全獨立,與主項目沒有耦合。但有時業務模塊也有共用,比如聊天模塊、大圖瀏覽模塊等。業務模塊與主項目很難完全沒有耦合,比如大圖瀏覽模塊長按時的操作邏輯,比如聊天模塊長按消息的跳轉邏輯,況且有時,有些業務模塊被抽象成共用的,有些業務模塊卻沒有,共用模塊卻要引用主項目中沒有抽象成共用的業務模塊。比如貝聊App中的聊天模塊是共用的聊天模塊,但大圖瀏覽模塊卻不是共用的,因為歷史原因,家長端和老師端各有各的實現,但點擊聊天頁面的圖片消息,會進入大圖瀏覽模塊。

那如何在 BLKit 中引用主項目中業務模塊?

以 BLKit 中的聊天模塊為例,

// 這個類在Pods中
class ChatViewController: UIViewController {
    let chatID: String
    init(chatID: String) {
        self.chatID = chatID
    }
    // 其他業務代碼

    // Table Cell delegate
    func didTapImageIn(cell: UITableViewCell) {
        // 進入大圖瀏覽模塊
        let allImages: [URL] = [self getAllMessageImages]
    }
}

聊天的 Cell 的代理為 ChatViewController ,當點擊到圖片 Cell ,會調用代理方法 didTapImageIn(cell:) 。 ChatViewController 實現了這個代理方法,獲取到所有圖片的 URL 后,需要調用主項目中的大圖瀏覽模塊。但 Pods 中是不可能直接調用主項目中的方法的。所以需要一個橋梁。

定義一個信使協議

可以定義一個信使協議,然后在主項目中初始化 ChatViewController 時,傳入具體實現這個信使協議的類。

// pods中
// 信使協議
protocol ChatMessenger {
    func openPhotoViewer(with photoes: [URL], in context: UIViewController)
}

然后主項目中創建一個類實現 ChatMessenger 協議,

// 主項目中
class ChatMessengerConcrete: ChatMessenger {
    func openPhotoViewer(with photoes: [URL], in context: UIViewController) {
        // 具體調用主項目中的大圖瀏覽模塊
    }
}

修改 ChatViewController 接受一個 ChatMessenger 作為初始化參數,然后在點擊圖片的 cell 代理方法中調用 ChatMessenger 的大圖瀏覽:

// 這個類在Pods中
class ChatViewController: UIViewController {
    let chatMessenger: ChatMessenger
    let chatID: String
    // 初始化方法中傳入ChatMessenger
    init(chatMessenger: ChatMessenger, chatID: String) {
        self.chatMessenger = chatMessenger
        self.chatID = chatID
    }
    // 其他業務代碼

    // Table Cell delegate
    func didTapImageIn(cell: UITableViewCell) {
        let allImages: [URL] = [self getAllMessageImages]
        // 進入大圖瀏覽模塊
        chatMessenger.openPhotoViewer(with: allImages, in: self)
    }
}

然后在主項目中的聊天入口,初始化 ChatViewController ,并傳入實現 ChatMessenger 協議的 ChatMessengerConcrete :

// 比如消息列表頁
class ChatListViewController: UIViewController {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // 獲取對應indexPath的聊天ID
        let chatID = ""
        let chatVC = ChatViewController(chatMessenger: ChatMessengerConcrete(), chatID: )
        navigationController?.pushViewController(chatVC, animated: true)
    }
}

使用協議擴展

雖然可以在主項目中聊天的入口處,傳入 ChatMessenger 的實體類,達到在 Pods 中調用主項目業務模塊,但如果聊天入口也都抽象到 Pods 中的 BLKit 中,那在 Pods 中是無法初始化一個主項目中的 ChatMessenger 實體類的。

這時可以利用 Swift 中的 protocol extension ,在主項目中給 Pods 中的 ChatMessenger 協議一個默認實現。

// 主項目中
extension ChatMessenger {
    func openPhotoViewer(with photoes: [URL], in context: UIViewController) {
        // 具體調用主項目中的大圖瀏覽模塊
    }
}

再讓 Pods 中聊天頁類 ChatViewController 遵循 ChatMessenger 協議,然后需要主項目中的業務直接調用對應的方法即可。

// 這個類在Pods中
class ChatViewController: UIViewController, ChatMessenger {
    let chatID: String
    // 初始化方法中傳入ChatMessenger
    init(chatID: String) {
        self.chatID = chatID
    }
    // 其他業務代碼

    // Table Cell delegate
    func didTapImageIn(cell: UITableViewCell) {
        let allImages: [URL] = [self getAllMessageImages]
        // 進入大圖瀏覽模塊
        openPhotoViewer(with: allImages, in: self)
    }
}

協議擴展是實現 Pods 中調用主項目業務模塊的最好方式。

總結

當有兩個客戶端時,

1)建立一個或多個兩端共用的 repository ,管理兩端相同的業務模塊和功能模塊

2)利用 submodule 在主項目中管理兩端共用的 repository

3)利用私有 pods 將 submodule 中的共用代碼引入 project 中

4)利用協議擴展輕松實現在 pods 中調用主項目中的業務模塊

 

來自:http://shellhue.github.io/2017/04/08/sharecode/

 

 本文由用戶 SangDahl 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!