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 的问题都是一此小问题了。

相关文章
相关标签/搜索