iOS的 grpc 之路

为什么要用?

  • 网络层代码直接按照定义好的proto 文件生成,简单方便
  • 而从用户角度来看,可以节省流量,网络请求速度更快了
  • 翁伟要求的

我们的期望

  • 支持 swift,有 swift 实现
  • 使用简单
  • 方便维护

现实情况

  • 只有 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);
}
}];

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

怎么办?

鉴于上述情况,我们有了以下的方案

通过 NS_REFINED_FOR_SWIFT 这个宏所标记的函数、方法和变量在 Obj-C 代码中可以正常使用,但当它们桥接到 Swift 的时候,名称前面就会加上“__”。通过在 Obj-C 代码中使用这个宏,我们就可以在 Swift 代码中使用相同的名称来提供一个更便于使用的 API ,就像下面这样:

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

但是,对于每一个生成的文件,极大的可能都会有一个这种转换文件与之对应。这会造成运行时的体积增大,工程体积增大。

现在的方案swift-grpc

现有的资源:
grpc-swift: https://github.com/grpc/grpc-swift
swift-protobuf: https://github.com/apple/swift-protobuf

问题:

  • 没有可用的 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 的问题都是一此小问题了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容

  • 为什么要用? 网络层代码直接按照定义好的proto 文件生成,简单方便 而从用户角度来看,可以节省流量,网络请求速...
    太逸阅读 2,308评论 1 2
  • 有一天你将破蛹而出,成长得比人们期待的还要美丽,但这个过程会很痛,会很辛苦,有时候还会觉得灰心。面对着汹涌而来的现...
    陈雨啊阅读 261评论 0 1
  • 很郁闷,郁闷到无法呼吸,你有酒吗?心愁仍然在不断地发酵,明天也只不过是今天的复制。 2017.8.7 星期一 ...
    七炽阅读 270评论 1 1
  • 猪头肉y阅读 316评论 1 3
  • 你现在拥有的不一定是你比别人更聪明,可能,你只是比别人要运气好一点。 ...
    唐映君阅读 179评论 0 0