為什麼要用?
- 網絡層代碼直接按照定義好的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 ,就像下面這樣:

但是,對于每一個生成的檔案,極大的可能都會有一個這種轉換檔案與之對應。這會造成運作時的體積增大,工程體積增大。
現在的方案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 代替:
iOS的 grpc 之路
responseClass:[Template class]
接下來直接改 swift-protobuf 的編譯器,讓它生成 class 是不 是就可以了?經過實踐的證明,它遠遠不是改一下編譯器那麼簡單的事情,在 swift-protobuf runtime library 中還需要我們提供一個對 class 的序列化,看了下它們實作,
完全不知道怎麼寫這樣一個東西。到這裡這個想法已經進行不下去了,那再換一種吧。
想法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>
這樣之後,編譯通過。
完成了這兩個庫的安裝後,終于可以進入正題了。建立
我們工程的 podspec 檔案來進行內建使用了。由于前面的研究,我們生成代碼需要的是這三個工具:
由于這三個工具是經過代碼修改生成的,是以我們必須修改 podspec 檔案來指定使用這三個工具,由于沒有儲存那個版本,是以就不展示了,有需要的可以聯系我。
大功告成!!2333333333
再測試一下,生成一個帶有引用檔案,比如這個
pod install 之後果斷報錯啊,
通過一番查找嘗試,原來還要指定搜尋參數。然後就有了以下的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" => "[email protected]" }
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 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 的問題都是一此小問題了。