iOS的 grpc 之路

為什么要用?

  • 網(wǎng)絡(luò)層代碼直接按照定義好的proto 文件生成,簡(jiǎn)單方便
  • 而從用戶角度來(lái)看,可以節(jié)省流量,網(wǎng)絡(luò)請(qǐng)求速度更快了
  • 翁偉要求的

我們的期望

  • 支持 swift,有 swift 實(shí)現(xiàn)
  • 使用簡(jiǎn)單
  • 方便維護(hù)

現(xiàn)實(shí)情況

  • 只有 oc的 release 版本
  • 需要?jiǎng)?chuàng)建 podspec 文件,通過(guò)這個(gè)文件來(lái)管理和生成 grpc 的客戶端文件以及安裝依賴庫(kù)
  • 每次使用都需要?jiǎng)?chuàng)建 Service 實(shí)例
  • 不支持 Int 類型,如果要使用需要添加一定的代碼進(jìn)行轉(zhuǎn)換
  • 網(wǎng)絡(luò)請(qǐng)求的回調(diào)格式不符合我們里面的代碼風(fēng)格

grpc的:[service unaryCallWithRequest:request handler:^(RMTSimpleResponse *response, NSError *error) {
if (response) {
NSLog(@"Finished successfully with response:\n%@", response);
} else if (error) {
NSLog(@"Finished with error: %@", error);
}
}];

我們的:public class func getHomePageShowArea(success: @escaping ([TRHomePageArea]) -> Void, failure: ((Error) -> Void)? = nil)

怎么辦?

鑒于上述情況,我們有了以下的方案

通過(guò) NS_REFINED_FOR_SWIFT 這個(gè)宏所標(biāo)記的函數(shù)、方法和變量在 Obj-C 代碼中可以正常使用,但當(dāng)它們橋接到 Swift 的時(shí)候,名稱前面就會(huì)加上“__”。通過(guò)在 Obj-C 代碼中使用這個(gè)宏,我們就可以在 Swift 代碼中使用相同的名稱來(lái)提供一個(gè)更便于使用的 API ,就像下面這樣:

Paste_Image.png
Paste_Image.png
Paste_Image.png
Paste_Image.png

但是,對(duì)于每一個(gè)生成的文件,極大的可能都會(huì)有一個(gè)這種轉(zhuǎn)換文件與之對(duì)應(yīng)。這會(huì)造成運(yùn)行時(shí)的體積增大,工程體積增大。

現(xiàn)在的方案swift-grpc

現(xiàn)有的資源:
grpc-swift: https://github.com/grpc/grpc-swift
swift-protobuf: https://github.com/apple/swift-protobuf

問(wèn)題:

  • 沒(méi)有可用的 release 版本
  • 有寫好的模板代碼,但是沒(méi)有生成工具
  • 沒(méi)有集成方式的示例
  • swift-protobuf生成的是 struct而不是 class

初始的想法

由于 grpc-objc 是確定可以使用的,那么是不是可以使用 swift 的代碼來(lái)完全替代生成的 oc代碼,初步看來(lái)好像是可行的:

  • service的代碼按照oc的代碼的直接翻譯,message 直接使用 swift-protobuf 代替:
Paste_Image.png

將上面的代碼翻譯成 swift 是一件很簡(jiǎn)單的事,這里就不說(shuō)了,值得注意的是這個(gè)地方,它需要傳入的是一個(gè) class類型, 而我們的 swift-protobuf只提供 struct, 這就尷尬了:

responseClass:[Template class]

接下來(lái)直接改 swift-protobuf 的編譯器,讓它生成 class 是不 是就可以了?經(jīng)過(guò)實(shí)踐的證明,它遠(yuǎn)遠(yuǎn)不是改一下編譯器那么簡(jiǎn)單的事情,在 swift-protobuf runtime library 中還需要我們提供一個(gè)對(duì) class 的序列化,看了下它們實(shí)現(xiàn),


Paste_Image.png

完全不知道怎么寫這樣一個(gè)東西。到這里這個(gè)想法已經(jīng)進(jìn)行不下去了,那再換一種吧。

想法ing

既然有 grpc-swift,而且給出的有可運(yùn)行 example, 通過(guò)驗(yàn)證,這個(gè)代碼也是可行的(service是手動(dòng)寫的,messae部分使用 swift-protobuf 生成),可以從服務(wù)器請(qǐng)求和接收數(shù)據(jù),可以滿足我們工程中的需要。

有了上述的支持,我們現(xiàn)在只需要一個(gè) service 代碼 compiler 就可以了,做為一個(gè)沒(méi)有用過(guò) c++的怎么來(lái)改寫/編寫這樣一個(gè) compiler,就不在這里說(shuō)了,各種坑是肯定的了。

有了service 代碼 compiler之后,這個(gè)方案可以算是完成了一半了。

接下來(lái)就可以看看怎么集成到我們的工程中使用了,由于 grpc-swift 中是直接將需要的依賴庫(kù)拖到工程中使用的,但是這種方式并不是我們想要的,是不是可以使用 grpc-objc 的方式(pod install)來(lái)集成?

研究了 grpc-objc 的和 grpc-swift 之后,發(fā)現(xiàn)想要使用 grpc-swift需要 CgRPC(為了支持 swift對(duì)grpc-Core 的封裝和拓展),SgRPC(使用 swift進(jìn)行封裝)這兩個(gè)庫(kù)支持,踩了 N 多坑之后,終于將這兩個(gè)庫(kù)弄成了本地 pod repo。

接下就可以集成到我們的 ezbuy 工程里了,但是事情總是沒(méi)有那么順利,pod install 之后工程果斷編譯報(bào)錯(cuò),經(jīng)過(guò)查找最后發(fā)現(xiàn)是由于在 grpc-Core 里定義了一個(gè)和系統(tǒng)庫(kù)重名string.h 文件(他們?cè)?podspec 文件中說(shuō)是故意定義成這樣的),而第三方庫(kù)HappyDNS 中使用了#include "string.h" 這樣的一種方式(不是標(biāo)準(zhǔn)的方式)來(lái)引用系統(tǒng)庫(kù)中 string.h 文件,從而導(dǎo)致了報(bào)錯(cuò),至于錯(cuò)誤原因在這里就不說(shuō)了,修改成 #include <string.h> 這樣之后,編譯通過(guò)。

完成了這兩個(gè)庫(kù)的安裝后,終于可以進(jìn)入正題了。創(chuàng)建


Paste_Image.png

我們工程的 podspec 文件來(lái)進(jìn)行集成使用了。由于前面的研究,我們生成代碼需要的是這三個(gè)工具:

Paste_Image.png

由于這三個(gè)工具是經(jīng)過(guò)代碼修改生成的,所以我們必須修改 podspec 文件來(lái)指定使用這三個(gè)工具,由于沒(méi)有保存那個(gè)版本,所以就不展示了,有需要的可以聯(lián)系我。

大功告成!!2333333333

再測(cè)試一下,生成一個(gè)帶有引用文件,比如這個(gè)

Paste_Image.png

pod install 之后果斷報(bào)錯(cuò)啊,

Paste_Image.png

通過(guò)一番查找嘗試,原來(lái)還要指定搜索參數(shù)。然后就有了以下的shell腳本 grpc.sh(邊搜邊寫,求不吐槽)和修改后的 podspec:

grpc.sh
#! /bin/sh
#! /bin/bash

#定義需要搜索的目錄路徑,如果.proto 文件放置的位置有改變,需要更改這個(gè)地方的路徑
declare targetSearchPath=apidoc/ios_proto

if [[ ! -f "/usr/local/lib/libprotobuf.10.dylib" ]]; then
 echo "請(qǐng)按照grpcInstall.md文件安裝 grpc & protobuf"
 open grpcInstall.md
fi

if [[ ! -d "$targetSearchPath" ]]; then
 echo "$targetSearchPath 不存在,請(qǐng)確認(rèn).proto 文件的放置路徑。"
fi

# 導(dǎo)入環(huán)境變量,以便 protoc 和 protoc-gen-swift 的使用
export PATH=$PATH:./grpc

#定義接收搜索參數(shù)的變量"-I xxxx -I xxxxx -I xxxxx"
declare protoPath=""

function getProtoSearchPath() {

 fList=`ls $1`

 for folder in $fList
 do
  temp=${1}"/"${folder}
  # echo "當(dāng)前搜索的目錄是:$temp"
  if [[ -d $temp ]]; then
   protoPath=${protoPath}" -I $temp"
   # echo "The directory is>> $protoPath"
   getProtoSearchPath $temp $protoPath
  fi
 done
}

getProtoSearchPath $targetSearchPath

#Path where protoc and gRPC plugin
protoc="grpc/protoc"
plugin="grpc/grpc_swift_plugin"

#name of pod spec
name="ezbuyGRPC"
pod_root=Pods

# Directory where the generated files will be placed.
generated_path=$pod_root"/"$name

mkdir -p $generated_path

protoPath=$protoPath" -I $targetSearchPath"
p_command="${protoc} --plugin=protoc-gen-grpc=$plugin --swift_out=$generated_path --grpc_out=$generated_path $protoPath $targetSearchPath/*/*.proto"
echo $p_command
eval $p_command
ezbuyGRPC.podspec
  #
#  Be sure to run `pod spec lint ezbuyGRPC.podspec' to ensure this is a
#  valid spec and to remove all comments including this before submitting the spec.
#
#  To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html
#  To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/
#

Pod::Spec.new do |s|

  s.name         = "ezbuyGRPC"
  s.version      = "0.0.3"
  s.summary      = "This is useful to install grpc easily."
  s.description  = <<-DESC
                    Use 'pod install' to generate proto files.
                    When you change the proto file and want to use 'pod install', you should change the property of version in this file.
                   DESC

  s.homepage     = "http://www.grpc.io/"

  s.author             = { "wangding" => "wangding@ezbuy.com" }

  s.ios.deployment_target = "8.0"
  s.osx.deployment_target = "10.9"

  s.source       = { :path => "."}


  # Base directory where the .proto files are.
  # src = "apidoc/proto/*"

  # Pods directory corresponding to this app's Podfile, relative to the location of this podspec.
  pods_root = 'Pods'

  # Path where Cocoapods downloads protoc and the gRPC plugin.
  # protoc_dir = "."
  # protoc = "#{protoc_dir}/protoc"
  # plugin = "./grpc_swift_plugin"


  # swift_protoc_plugin = "protoc-gen-swift"

  # Directory where the generated files will be placed.
  dir = "#{pods_root}/#{s.name}"

  # s.prepare_command = <<-CMD
  #   rm -f /usr/local/bin/#{swift_protoc_plugin}
  #   cp #{swift_protoc_plugin} /usr/local/bin/
  #   mkdir -p #{dir}
  #   #{protoc} \
  #   --plugin=protoc-gen-grpc=#{plugin} \
  #   --swift_out=#{dir} \
  #   --grpc_out=#{dir} \
  #   -I #{src} \
  #   -I #{protoc_dir} \
  #   -I #{src}/* \
  #   #{src}/*.proto
  # CMD

    s.prepare_command = <<-CMD
      chmod 777 grpc/grpc.sh
      ./grpc/grpc.sh
    CMD

  # Files generated by protoc
  s.subspec "Messages" do |ms|
  ms.source_files = "#{dir}/*.pb.swift"
  ms.header_mappings_dir = dir
  ms.requires_arc = true
  # The generated files depend on the protobuf runtime. The version is 0.9.24
  ms.dependency "SwiftProtobuf"
  end

  # Files generated by the gRPC plugin
  s.subspec "Services" do |ss|
  ss.source_files = "#{dir}/*.pbrpc.swift"
  ss.header_mappings_dir = dir
  ss.requires_arc = true
  # The generated files depend on the gRPC runtime, and on the files generated by protoc.
  # ss.dependency "gRPC-ProtoRPC"
  ss.dependency "#{s.name}/Messages"
  ss.dependency "SwiftGRPC"
  ss.dependency "GRPCError"
  end

  # s.pod_target_xcconfig = {
  # # This is needed by all pods that depend on Protobuf:
  # 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1',
  # # This is needed by all pods that depend on gRPC-RxLibrary:
  # 'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES',
  # }

end

到這里已經(jīng)可以使用了,對(duì)于剩余的一些需要修改代碼以及 compiler 的問(wèn)題都是一此小問(wèn)題了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 為什么要用? 網(wǎng)絡(luò)層代碼直接按照定義好的proto 文件生成,簡(jiǎn)單方便 而從用戶角度來(lái)看,可以節(jié)省流量,網(wǎng)絡(luò)請(qǐng)求速...
    太逸閱讀 2,361評(píng)論 1 2
  • 有一天你將破蛹而出,成長(zhǎng)得比人們期待的還要美麗,但這個(gè)過(guò)程會(huì)很痛,會(huì)很辛苦,有時(shí)候還會(huì)覺(jué)得灰心。面對(duì)著洶涌而來(lái)的現(xiàn)...
    陳雨啊閱讀 315評(píng)論 0 1
  • 很郁悶,郁悶到無(wú)法呼吸,你有酒嗎?心愁仍然在不斷地發(fā)酵,明天也只不過(guò)是今天的復(fù)制。 2017.8.7 星期一 ...
    七熾閱讀 305評(píng)論 1 1
  • 豬頭肉y閱讀 351評(píng)論 1 3
  • 你現(xiàn)在擁有的不一定是你比別人更聰明,可能,你只是比別人要運(yùn)氣好一點(diǎn)。 ...
    唐映君閱讀 198評(píng)論 0 0

友情鏈接更多精彩內(nèi)容