iOS的 gRPC 之路

做者: iOS 團隊 - 王玎html


##爲何要用?ios

  • 網絡層代碼直接按照定義好的proto 文件生成,簡單方便
  • 而從用戶角度來看,能夠節省流量,網絡請求速度更快了
  • 翁偉要求的

##咱們的指望c++

  • 支持 swift,有 swift 實現
  • 使用簡單
  • 方便維護

##現實狀況git

  • 只有 oc的 release 版本
  • 須要建立 podspec 文件,經過這個文件來管理和生成 grpc 的客戶端文件以及安裝依賴庫
  • 每次使用都須要建立 Service 實例
  • 不支持 Int 類型,若是要使用須要添加必定的代碼進行轉換
  • 網絡請求的回調格式不符合咱們裏面的代碼風格

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); } }];github

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

##怎麼辦? ###鑑於上述狀況,咱們有了如下的方案 經過 NS_REFINED_FOR_SWIFT 這個宏所標記的函數、方法和變量在 Obj-C 代碼中能夠正常使用,但當它們橋接到 Swift 的時候,名稱前面就會加上「__」。經過在 Obj-C 代碼中使用這個宏,咱們就能夠在 Swift 代碼中使用相同的名稱來提供一個更便於使用的 API ,就像下面這樣:swift

Paste_Image.png

Paste_Image.png

Paste_Image.png

Paste_Image.png

可是,對於每個生成的文件,極大的可能都會有一個這種轉換文件與之對應。這會形成運行時的體積增大,工程體積增大。api

###如今的方案swift-grpcruby

現有的資源: grpc-swift: github.com/grpc/grpc-s… swift-protobuf: github.com/apple/swift…bash

問題:

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

####初始的想法

因爲 grpc-objc 是肯定可使用的,那麼是否是可使用 swift 的代碼來徹底替代生成的 oc代碼,初步看來好像是可行的:

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

Paste_Image.png
將上面的代碼翻譯成 swift 是一件很簡單的事,這裏就不說了,值得注意的是這個地方,它須要傳入的是一個 class類型, 而咱們的 swift-protobuf只提供 struct, 這就尷尬了:

responseClass:[Template class]

接下來直接改 swift-protobuf 的編譯器,讓它生成 class 是不 是就能夠了?通過實踐的證實,它遠遠不是改一下編譯器那麼簡單的事情,在 swift-protobuf runtime library 中還須要咱們提供一個對 class 的序列化,看了下它們實現,

Paste_Image.png
徹底不知道怎麼寫這樣一個東西。到這裏這個想法已經進行不下去了,那再換一種吧。

####想法ing

既然有 grpc-swift,並且給出的有可運行 example, 經過驗證,這個代碼也是可行的(service是手動寫的,messae部分使用 swift-protobuf 生成),能夠從服務器請求和接收數據,能夠知足咱們工程中的須要。

有了上述的支持,咱們如今只須要一個 service 代碼 compiler 就能夠了,作爲一個沒有用過 c++的怎麼來改寫/編寫這樣一個 compiler,就不在這裏說了,各類坑是確定的了。

有了service 代碼 compiler以後,這個方案能夠算是完成了一半了。

接下來就能夠看看怎麼集成到咱們的工程中使用了,因爲 grpc-swift 中是直接將須要的依賴庫拖到工程中使用的,可是這種方式並非咱們想要的,是否是可使用 grpc-objc 的方式(pod install)來集成?

研究了 grpc-objc 的和 grpc-swift 以後,發現想要使用 grpc-swift須要 CgRPC(爲了支持 swift對grpc-Core 的封裝和拓展),SgRPC(使用 swift進行封裝)這兩個庫支持,踩了 N 多坑以後,終於將這兩個庫弄成了本地 pod repo。

接下就能夠集成到咱們的 ezbuy 工程裏了,可是事情老是沒有那麼順利,pod install 以後工程果斷編譯報錯,通過查找最後發現是因爲在 grpc-Core 裏定義了一個和系統庫重名string.h 文件(他們在 podspec 文件中說是故意定義成這樣的),而第三方庫HappyDNS 中使用了#include "string.h" 這樣的一種方式(不是標準的方式)來引用系統庫中 string.h 文件,從而致使了報錯,至於錯誤緣由在這裏就不說了,修改爲 #include <string.h> 這樣以後,編譯經過。

完成了這兩個庫的安裝後,終於能夠進入正題了。建立

Paste_Image.png
咱們工程的 podspec 文件來進行集成使用了。因爲前面的研究,咱們生成代碼須要的是這三個工具:

Paste_Image.png

因爲這三個工具是通過代碼修改生成的,因此咱們必須修改 podspec 文件來指定使用這三個工具,因爲沒有保存那個版本,因此就不展現了,有須要的能夠聯繫我。

大功告成!!2333333333

再測試一下,生成一個帶有引用文件,好比這個

Paste_Image.png

pod install 以後果斷報錯啊,

Paste_Image.png

經過一番查找嘗試,原來還要指定搜索參數。而後就有了如下的shell腳本 grpc.sh(邊搜邊寫,求不吐槽)和修改後的 podspec:

#####grpc.sh

#! /bin/sh
#! /bin/bash
 #定義須要搜索的目錄路徑,若是.proto 文件放置的位置有改變,須要更改這個地方的路徑
declare targetSearchPath=apidoc/ios_proto

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

if [[ ! -d "$targetSearchPath" ]]; then
 echo "$targetSearchPath 不存在,請確認.proto 文件的放置路徑。"
fi
 # 導入環境變量,以便 protoc 和 protoc-gen-swift 的使用
export PATH=$PATH:./grpc
 #定義接收搜索參數的變量"-I xxxx -I xxxxx -I xxxxx"
declare protoPath=""

function getProtoSearchPath() {

 fList=`ls $1`

 for folder in $fList
 do
  temp=${1}"/"${folder}
 # echo "當前搜索的目錄是:$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
複製代碼

到這裏已經可使用了,對於剩餘的一些須要修改代碼以及 compiler 的問題都是一此小問題了。

相關文章
相關標籤/搜索