因?yàn)?a target="_blank" rel="nofollow">https://blog.csdn.net/urdfmqcul2/article/details/78788962
,博客搬家至https://juejin.im/user/59fd6315f265da4321536990
翻譯自:Introduction to Protocol Buffers on iOS
對(duì)大多數(shù)的應(yīng)用來說,后臺(tái)服務(wù)、傳輸和存儲(chǔ)數(shù)據(jù)都是個(gè)重要的模塊。開發(fā)者在給一個(gè) web service 寫接口時(shí),通常使用 JSON 或者 XML 來發(fā)送和接收數(shù)據(jù),然后根據(jù)這些數(shù)據(jù)生成結(jié)構(gòu)并解析。
盡管有大量的 API 和框架幫助我們序列化和反序列化,來支持一些后臺(tái)接口開發(fā)的日常工作,比如說更新代碼或者解析器來支持后臺(tái)的模型變化。
但是如果你真的想提升你的新項(xiàng)目的健壯性的話 ,考慮下用 protocol buffers,它是由 Google 開發(fā)用來序列化數(shù)據(jù)結(jié)構(gòu)的一種跨語言的方法。在很多情況下,它比傳統(tǒng)的 JSON 和 XML 更加靈活有效。其中一個(gè)關(guān)鍵的特點(diǎn)就是,你只需要在其支持的任何語言和編譯器下,定義一次數(shù)據(jù)結(jié)構(gòu)——包括 Swift! 創(chuàng)建的類文件就可以很輕松的讀寫成對(duì)象。
在這篇教程中,會(huì)使用一個(gè) Python 服務(wù)端與一個(gè) iOS 程序交互。你會(huì)學(xué)到 protocol buffers 是如何工作,如何配置環(huán)境,最后怎樣使用 protocol buffers 傳輸數(shù)據(jù)。
怎么,還是不相信 protocol buffers 就是你所需要的東西?接著往下讀吧。
注意:這篇教程是基于你已經(jīng)有了一定的 iOS 和 Swift 經(jīng)驗(yàn),同時(shí)有一定的基本的服務(wù)端和 terminal 基礎(chǔ)。
同時(shí),確保你使用的是蘋果的 Xcode 8.2或以后的版本.
準(zhǔn)備開始
RWCards這個(gè)APP可以用來查看你的會(huì)議門票和演講者名單。下載Starter Project并打開根目錄Starter。先熟悉一下這下面這三部分:
The Client
在 Starter/RWCards下,打開 RWCards.xcworkspace,我們來看看這幾個(gè)主要的文件:
- SpeakersListViewController.swift 管理了一個(gè)用來展示演講者名單的table view。這個(gè)控制器現(xiàn)在還只是個(gè)模板因?yàn)槟氵€沒有為其創(chuàng)建模型。
- SpeakersViewModel.swift 相當(dāng)于 SpeakersListViewController 的數(shù)據(jù)源,它會(huì)包含有演講者的名單數(shù)據(jù)。
- CardViewController.swift 用來展示參會(huì)者的名片和他的社交信息.
- RWService.swift 管理客戶端和后端的交互。你可能會(huì)用到 Alamofire 來發(fā)起服務(wù)請(qǐng)求。
- Main.storyboard 整個(gè) APP 的 storyboard.
整個(gè)工程使用 CocoaPods 來拉取這兩個(gè)框架:
- Swift Protobuf 支持在 Xcode 中使用 Protocol Buffers.
- Alamofire 一個(gè) HTTP 網(wǎng)絡(luò)庫,你會(huì)用到它來請(qǐng)求服務(wù)器。
注意:這篇教程中你會(huì)用到 Swift Protobuf 0.9.24 和 Google’s Protoc Compiler 3.1.0. 它們已經(jīng)打包在項(xiàng)目里了,所以你不需要再做別的。
Protocol Buffers 是如何工作的?
開始使用 protocol buffers 前,首先要定義一個(gè) .proto 文件。在這個(gè)文件中指定了你的數(shù)據(jù)結(jié)構(gòu)信息。下面是一個(gè) .proto 文件的示例:
syntax = "proto3";
message Contact {
enum ContactType {
SPEAKER = 0;
ATTENDANT = 1;
VOLUNTEER = 2;
}
string first_name = 1;
string last_name = 2;
string twitter_name = 3;
string email = 4;
string github_link = 5;
ContactType type = 6;
string imageName = 7;
};
這個(gè)文件里定義了一個(gè) Contact 的 message 和它的相關(guān)屬性。
.proto 文件定義好了后,你只需要把這個(gè)文件交給 protocol buffer 的編譯器,編譯器會(huì)用你選擇的語言創(chuàng)建好一個(gè)數(shù)據(jù)類(Swift 中的 結(jié)構(gòu))。你可以直接在項(xiàng)目中使用這個(gè)類/結(jié)構(gòu),非常簡單!

編譯器會(huì)將 .proto 中的 message 轉(zhuǎn)換成事先選擇的語言,并生成模型對(duì)象的源文件。后面會(huì)提到定義.proto信息的更多細(xì)節(jié)。
另外在考慮 protocol buffers 之前,你應(yīng)該考慮它是不是你項(xiàng)目的最佳方案。
優(yōu)勢
JSON 和 XML 可能是目前開發(fā)者們用來存儲(chǔ)和傳輸數(shù)據(jù)的標(biāo)準(zhǔn)方案,而 protocol buffers 與之相比有以下優(yōu)勢:
- 快速且小巧:按照 Google 所描述的,protocol buffers 的體積要小3-10倍,速度比XML要快20-100倍??梢栽谶@篇文章 ,它的作者是 Damien Bod,文中比較了一些主流文本格式的讀寫速度。
- 類型安全:Protocol buffers 像 Swift 一樣是類型安全的,使用 protocol buffers 時(shí) 你需要指定每一個(gè)屬性的類型。
-
自動(dòng)反序列化:你不需要再去編寫任何的解析代碼,只需要更新 .proto 文件就行了。
file and regenerate the data access classes. - 分享就是關(guān)心:因?yàn)橹С侄喾N語言,因此可以在不同的平臺(tái)中共享數(shù)據(jù)模型,這意味著跨平臺(tái)的工作會(huì)更輕松。
局限性
Protocol buffers 雖然有著諸多優(yōu)勢,但是它也不是萬能的:
- 時(shí)間成本:在老項(xiàng)目中去使用 protocol buffers 可能會(huì)不太高效,因?yàn)樾枰D(zhuǎn)換成本。同時(shí),項(xiàng)目成員還需要去學(xué)習(xí)一種新的語法。
- 可讀性:XML 和 JSON 的描述性更好,并且易于閱讀。Protocol buffers 的原數(shù)據(jù)無法閱讀,并且在沒有 .proto 文件的情況下沒辦法解析。
- 僅僅是不適合而已:當(dāng)你想要使用類似于XSLT這樣的樣式表時(shí),XML是最好的選擇。所以 protocol buffers 并不總是最佳工具。
- 不支持:編譯器可能不支持你正在進(jìn)行中的項(xiàng)目所使用的語言和平臺(tái)。
盡管并不是適合于所有的情況,但 protocol buffers 確確實(shí)實(shí)有著很多的優(yōu)勢。
把程序運(yùn)行起來試試看吧。

不幸的是你現(xiàn)在還看不到任何信息,因?yàn)閿?shù)據(jù)源還沒有初始化。你要做的是請(qǐng)求服務(wù)端并且將演講者和參會(huì)者數(shù)據(jù)填充到頁面上。首先,你會(huì)看到項(xiàng)目中提供的:
Protocol Buffer 模板
Head back to Finder and look inside Starter/ProtoSchema. You’ll see the following files:
打開 Starter/ProtoSchema 目錄,你會(huì)看到這些文件:
- contact.proto 用 protocol buffer 的語法定義了一個(gè) contact 的結(jié)構(gòu)。之后會(huì)更詳細(xì)地說明這個(gè)。
- protoScript.sh 這個(gè) bash 腳本使用 protocol buffer 的編譯器讀取 contact.proto 分別生成了 Swift 和 Python 的數(shù)據(jù)模型。
服務(wù)端
Starter/Server 目錄下包括:
-
RWServer.py 是放在Flask上的一個(gè) Python 服務(wù)。包含兩個(gè) GET 請(qǐng)求:
- /currentUser 獲取當(dāng)前參會(huì)者的信息。
- /speakers 獲取演講者列表。
RWDict.py 包含了 RWServer 將要讀取的演講者列表數(shù)據(jù).
現(xiàn)在是時(shí)候配置環(huán)境來運(yùn)行 protocol buffers 了。在下面的章節(jié)中,你會(huì)創(chuàng)建好運(yùn)行 Google 的 protocol buffer編譯器環(huán)境,Swift 的 Protobuf 插件,并安裝 Flask 來運(yùn)行你的 Python 服務(wù)。
環(huán)境配置
在使用 protocol buffers 之前需要安裝許多的工具和庫。starter 項(xiàng)目中包含了一個(gè)名為 protoInstallation.sh 的腳本幫你搞定了這些。它會(huì)在安裝之前檢查是否已經(jīng)安裝過這些庫。
這個(gè)腳本需要花一點(diǎn)時(shí)間來安裝,尤其是安裝 Google 的 protocol buffer 庫。打開你的終端,cd 命令進(jìn)入到 Starter 目錄執(zhí)行下面這個(gè)命令:
$ ./protoInstallation.sh
注意:執(zhí)行的過程中你可能會(huì)被要求輸入管理員密碼。
腳本執(zhí)行完成后,再運(yùn)行一次以確保的到以下輸出結(jié)果:

如果你看到這些,那表示腳本已經(jīng)執(zhí)行完畢。如果腳本執(zhí)行失敗了,那檢查下你是不是輸入了錯(cuò)誤的管理員密碼。并重新運(yùn)行腳本;它不會(huì)重新安裝那些已經(jīng)成功的庫。
這個(gè)腳本做了這些事:
- 安裝 Flask 以運(yùn)行 Python 本地服務(wù)。
- 從 Starter/protobuf-3.1.0 目錄下生成 protocol buffer 編譯器。
- 安裝 protocol buffer 的 Python 模塊,這樣服務(wù)端可以使用 Protobuf 庫。
- 將 Swift Protobuf 插件 protoc-gen-swift 移至 /usr/local/bin. 使 Protobuf 編譯器可以生成 Swift 的結(jié)構(gòu)。
注意:你可以用編輯器打開 protoInstallation.sh 文件來了解這個(gè)腳本是如何工作的。這需要一定的 bash 基礎(chǔ)。
好了,現(xiàn)在你已經(jīng)做好了使用 protocol buffers 的所有準(zhǔn)備工作。
定義一個(gè) .proto 文件
.proto 文件定義了 protocol buffer 描述你的數(shù)據(jù)結(jié)構(gòu)的 message。把這個(gè)文件中的內(nèi)容傳遞給 protocol buffer 編譯器后,編譯器會(huì)生成你的數(shù)據(jù)結(jié)構(gòu)。
注意:在這篇教程中,你將使用 proto3 來定義 message,這是 protocol buffer 語言的最新版本??梢栽L問Google’s guidelines以獲取更多的 proto3 的信息。
用你最習(xí)慣的編輯器打開 ProtoSchema/contact.proto ,這里已經(jīng)定義好了演講者的 message:
syntax = "proto3";
message Contact { // 1
enum ContactType { // 2
SPEAKER = 0;
ATTENDANT = 1;
VOLUNTEER = 2;
}
string first_name = 1; //3
string last_name = 2;
string twitter_name = 3;
string email = 4;
string github_link = 5;
ContactType type = 6;
string imageName = 7;
};
message Speakers { // 4
repeated Contact contacts = 1;
};
我們來看一下這里面包含了哪些內(nèi)容:
The Contact model describes a person’s contact information. This will be displayed on their badges in the app.
- Contact 模型用于描述名片信息。在 app 中會(huì)被顯示在 badges 頁。
- 每一個(gè) contact 應(yīng)該分類,這樣才能區(qū)別出是訪客還是演講者。
- proto 文件中的每一條 message 和 enum 必須指派一個(gè)增量且唯一的數(shù)字標(biāo)簽。這些數(shù)字用來用于區(qū)分信息二進(jìn)制格式的域,這很重要。訪問reserved fields可以了解更多關(guān)于標(biāo)簽和域的信息。
- Speakers 模型包含了 contacts 的集合,* repeated* 關(guān)鍵字表示一個(gè)對(duì)象的數(shù)組。
生成 Swift 結(jié)構(gòu)
把 contact.proto 傳遞給 protoc 程序,proto 文件中的 message 將會(huì)被轉(zhuǎn)化生成 Swift 的結(jié)構(gòu)。這些結(jié)構(gòu)會(huì)遵循 ProtobufMessage.protoc 并提供 Swift 中構(gòu)造、方法來序列化和反序列化數(shù)據(jù)的途徑。
注意:想了解更多關(guān)于 Swift 的 protobuf API, 訪問蘋果的 Protobuf API documentation.
在終端中,進(jìn)入** Starter/ProtoSchema **目錄,用編輯器打開 protoScript.sh,你會(huì)看到:
#!/bin/bash
echo 'Running ProtoBuf Compiler to convert .proto schema to Swift'
protoc --swift_out=. contact.proto // 1
echo 'Running Protobuf Compiler to convert .proto schema to Python'
protoc -I=. --python_out=. ./contact.proto // 2
這個(gè)腳本對(duì) contact.proto 文件執(zhí)行了兩次 protoc 命令,分別創(chuàng)建了 Swift 和 Python 的源文件。
回到終端,執(zhí)行下面的命令:
$ ./protoScript.sh
你會(huì)看到以下輸出結(jié)果:
Running ProtoBuf Compiler to convert .proto schema to Swift
protoc-gen-swift: Generating Swift for contact.proto
Running Protobuf Compiler to convert .proto schema to Python
你已經(jīng)創(chuàng)建好了 Swift 和 Python 的源文件。
在 ** ProtoSchema** 目錄下,你會(huì)看到一個(gè) Swift 和一個(gè) Python 文件。同時(shí)分別還有一個(gè)對(duì)應(yīng)的 .pb.swift 和 .pb.py. pb 前綴表示這是 protocol buffer 生成的類。

把 contact.pb.swift 拖到 Xcode 的 project navigator 下的 Protocol Buffer Objects 組. 勾上“Copy items if needed”選項(xiàng)。同時(shí)將 contact_pb2.py 拷貝到 Starter/Server 目錄。
看一眼 ** contact.pb.swift** 和 contact_pb2.py中的內(nèi)容,看看 proto message 是如何轉(zhuǎn)換成目標(biāo)語言的。
現(xiàn)在你已經(jīng)有了生成好的模型對(duì)象了,可以開始集成了!
運(yùn)行本地服務(wù)器
示例代碼中包含了一個(gè) Python 服務(wù)。這個(gè)服務(wù)提供了兩個(gè) GET 請(qǐng)求:一個(gè)用來獲取參會(huì)者的名牌信息,另一個(gè)用來列出演講者。
這個(gè)教程不會(huì)深入講解服務(wù)端的代碼。盡管如此,你需要了解到它用到了由 protocol buffer 編譯器生成的 contact_pb2.py 模型文件。如果你感興趣,可以看一看 RWServer.py 中的代碼,不看也無妨(手動(dòng)滑稽)。
打開終端并 cd 至 Starter/Server 目錄,運(yùn)行下面的命令:
$ python RWServer.py
運(yùn)行結(jié)果如下:

測試 GET 請(qǐng)求
通過在瀏覽器中發(fā)起 HTTP 請(qǐng)求,你可以看到 protocol buffer 的原數(shù)據(jù)。
在瀏覽器中打開 http://127.0.0.1:5000/currentUser 你會(huì)看到:

再試試演講者的接口,http://127.0.0.1:5000/speakers:

注意:測試 RWCards app的過程中你可以退出、中止和重啟本地服務(wù)以便調(diào)試。
現(xiàn)在你已經(jīng)運(yùn)行了本地服務(wù)器,它使用的是由 proto 文件生成的模型,是不是很cooool?
發(fā)起服務(wù)請(qǐng)求
現(xiàn)在你已經(jīng)把本地服務(wù)器跑起來了,是時(shí)候在 app 中發(fā)起服務(wù)請(qǐng)求了。**RWService.swift **文件中將 RWService 類替換成下面的代碼:
class RWService {
static let shared = RWService() // 1
let url = "http://127.0.0.1:5000"
private init() { }
func getCurrentUser(_ completion: @escaping (Contact?) -> ()) { // 2
let path = "/currentUser"
Alamofire.request("\(url)\(path)").responseData { response in
if let data = response.result.value { // 3
let contact = try? Contact(protobuf: data) // 4
completion(contact)
}
completion(nil)
}
}
}
這個(gè)類將用來與你的 Python 服務(wù)器進(jìn)行交互。你已經(jīng)實(shí)現(xiàn)了獲取當(dāng)前用戶的請(qǐng)求:
- shared 是一個(gè)發(fā)起網(wǎng)絡(luò)請(qǐng)求的單例。
- getCurrentUser(_:) 方法通過 /currentUser 路徑發(fā)起了獲取用戶信息的網(wǎng)絡(luò)請(qǐng)求,后臺(tái)會(huì)返回一個(gè)硬編碼的用戶信息。
- if let 獲取了數(shù)據(jù)。
- data 中包含了服務(wù)端返回的 protocol buffer 二進(jìn)制數(shù)據(jù)。 Contact 的構(gòu)造器以 data 作為入?yún)?,解碼數(shù)據(jù)。
解碼數(shù)據(jù)只需要把 protocol buffer 的數(shù)據(jù)傳遞給對(duì)象的構(gòu)造器即可,不需要其他的解析。 Swift 的 protocol buffer 庫幫你處理了所有的事情。
現(xiàn)在請(qǐng)求已經(jīng)完成,可以展示數(shù)據(jù)了。
集成參會(huì)者的名片
打開 CardViewController.swift 文件并在 viewWillAppear(_:) 之后添加下面這些代碼:
func fetchCurrentUser() { // 1
RWService.shared.getCurrentUser { contact in
if let contact = contact {
self.configure(contact)
}
}
}
func configure(_ contact: Contact) { // 2
self.attendeeNameLabel.attributedText = NSAttributedString.attributedString(for: contact.firstName, and: contact.lastName)
self.twitterLabel.text = contact.twitterName
self.emailLabel.text = contact.email
self.githubLabel.text = contact.githubLink
self.profileImageView.image = UIImage(named: contact.imageName)
}
這些方法會(huì)幫你取得服務(wù)端傳過來的數(shù)據(jù),并用來配置名片:
- fetchCurrentUser() 請(qǐng)求服務(wù)器去獲取當(dāng)前用戶的信息,并使用 * contact* 來配置 * CardViewController*。
- configure(_:) 通過傳入的 contact 配置UI。
用起來很簡單,但是還需要拿到一個(gè) ContactType 枚舉用來區(qū)分參會(huì)者的類型。
自定義 Protocol Buffer 對(duì)象
你需要添加一個(gè)方法來把枚舉類型轉(zhuǎn)換成 string, 這樣名片頁面才能顯示 SPEAKER 而不是一個(gè)數(shù)字0.
但是這有個(gè)問題,如果不重新生成 .proto 文件來更新 message,怎樣才能往模型里添加新功能呢?

Swift extensions 可以搞定這個(gè),它可以讓你添加一些信息到類中而不需要改變類本身的代碼。
創(chuàng)建一個(gè)名為 contact+extension.swift 的文件,并添加到 Protocol Buffer Objects 目錄。添加以下代碼:
extension Contact {
func contactTypeToString() -> String {
switch type {
case .speaker:
return "SPEAKER"
case .attendant:
return "ATTENDEE"
case .volunteer:
return "VOLUNTEER"
default:
return "UNKNOWN"
}
}
}
contactTypeToString() 方法將 ContactType 映射成了一個(gè)對(duì)應(yīng)的顯示用的字符串。
打開 CardViewController.swift 并添加下面的代碼到 configure(_:):
self.attendeeTypeLabel.text = contact.contactTypeToString()
將代表contact type的字符串傳遞給了 * attendeeTypeLabel。
最后在 viewWillAppear(_:) 中,applyBusinessCardAppearance()* 之后添加下面代碼:
if isCurrentUser {
fetchCurrentUser()
} else {
// TODO: handle speaker
}
- isCurrentUser* 已經(jīng)被硬編碼成 true, 當(dāng)被設(shè)置為演講者時(shí)這個(gè)值會(huì)被修改。*fetchCurrentUser() * 方法在默認(rèn)情況下會(huì)被調(diào)用,獲取名片信息并將其填充到名片上。
運(yùn)行程序來看看參會(huì)者的名片頁面:

集成演講者列表
My Badge 選項(xiàng)卡完成后,我們來看看 Speakers 選項(xiàng)卡。
打開 RWService.swift 并添加下面的代碼:
func getSpeakers(_ completion: @escaping (Speakers?) -> ()) { // 1
let path = "/speakers"
Alamofire.request("\(url)\(path)").responseData { response in
if let data = response.result.value { // 2
let speakers = try? Speakers(protobuf: data) // 3
completion(speakers)
}
}
completion(nil)
}
看上去很熟悉是吧,它和 getCurrentUser(_:) 類似,不過他獲取的是 Speakers 對(duì)象,包含了一個(gè) contact 的數(shù)組,用于表示回憶的演講者。
打開 SpeakersViewModel.swift 并將代碼替換為:
class SpeakersViewModel {
var speakers: Speakers!
var selectedSpeaker: Contact?
init(speakers: Speakers) {
self.speakers = speakers
}
func numberOfRows() -> Int {
return speakers.contacts.count
}
func numberOfSections() -> Int {
return 1
}
func getSpeaker(for indexPath: IndexPath) -> Contact {
return speakers.contacts[indexPath.item]
}
func selectSpeaker(for indexPath: IndexPath) {
selectedSpeaker = getSpeaker(for: indexPath)
}
}
SpeakersListViewController 顯示了一個(gè)參會(huì)者的列表,SpeakersViewModel中包含了這些數(shù)據(jù):從 /speakers 接口中獲取的contact對(duì)象組成的數(shù)組。 SpeakersListViewController將在每一行中顯示一個(gè)speaker。
viewmodel創(chuàng)建好了之后,就該配置cell了。打開 SpeakerCell.swift,添加下面的代碼到 SpeakerCell:
func configure(with contact: Contact) {
profileImageView.image = UIImage(named: contact.imageName)
nameLabel.attributedText = NSAttributedString.attributedString(for: contact.firstName, and: contact.lastName)
}
傳入了一個(gè)contact對(duì)象并且通過其屬性來配置cell的 image 和 label。這個(gè)cell會(huì)顯示演講者的照片,和他的名字。
接下來,打開 SpeakersListViewController.swift 并添加下面的代碼到 viewWillAppear(_:)中:
RWService.shared.getSpeakers { [unowned self] speakers in
if let speakers = speakers {
self.speakersModel = SpeakersViewModel(speakers: speakers)
self.tableView.reloadData()
}
}
getSpeakers(_:)發(fā)起了一個(gè)請(qǐng)求去獲取演講者列表的數(shù)據(jù),創(chuàng)建了一個(gè) * SpeakersViewModel* 的對(duì)象,并返回 speakers。 tableview 接下來會(huì)更新這些獲取到的數(shù)據(jù)。
你需要給 tableview 的每一行指定一個(gè)speaker用于顯示。替換tableView(_:cellForRowAt:)的代碼:
let cell = tableView.dequeueReusableCell(withIdentifier: "SpeakerCell", for: indexPath) as! SpeakerCell
if let speaker = speakersModel?.getSpeaker(for: indexPath) {
cell.configure(with: speaker)
}
return cell
getSpeaker(for:) 根據(jù)當(dāng)前列表的 indexPath返回 speaker數(shù)據(jù),通過cell的configure(with:)配置cell。
當(dāng)點(diǎn)擊列表中的一個(gè)cell時(shí),你需要跳轉(zhuǎn)到 CardViewController 展示選擇的演講者信息,打開 CardViewController.swift 并在類中添加這些屬性:
var speaker: Contact?
后面會(huì)用到這個(gè)屬性用來傳遞選擇的演講者。將// TODO: handle speaker替換為:
if let speaker = speaker {
configure(speaker)
}
這個(gè)判斷用來確定 speaker 是否已經(jīng)填充過了,如果是,調(diào)用 configure(),在名片上更新演講者的信息。
回到 SpeakersListViewController.swift 傳遞選擇的 speaker。在 tableView(_:didSelectRowAt:)中, performSegue(withIdentifier:sender:) 上方添加:
speakersModel?.selectSpeaker(for: indexPath)
將 speakersModel 中的對(duì)應(yīng) speaker 標(biāo)記為選中。
接下來,在prepare(for:sender:)的 vc.isCurrentUser = false: 之后添加下面的代碼:
vc.speaker = speakersModel?.selectedSpeaker
這里講 selectedSpeaker 傳遞給了 * CardViewController* 來顯示。
確保你的本地服務(wù)還在運(yùn)行當(dāng)中,build & run Xcode。你會(huì)看到 app 已經(jīng)集成了用戶名片,同時(shí)顯示了演講者的信息。

你已經(jīng)成功地用Swift的客戶端和Python的服務(wù)端,構(gòu)建好了一個(gè)應(yīng)用程序??蛻舳撕头?wù)端同時(shí)使用了由 proto 文件創(chuàng)建的模型。如果你需要修改模型,只需要簡單地運(yùn)行編譯器并重新生成,就能立刻得到兩端的模型文件!
總結(jié)
你可以從 這里下載到完成的工程。
在這篇教程中,你已經(jīng)學(xué)習(xí)到了 protocol buffer 的基本特征, 怎樣定義一個(gè) .proto 文件并通過編譯器生成 Swift 文件。還學(xué)習(xí)了如何使用Flask 創(chuàng)建一個(gè)簡單的本地服務(wù)器,并使用這個(gè)服務(wù)發(fā)送 protocol buffer 的二進(jìn)制數(shù)據(jù)給客戶端,以及如何輕松地去反序列化數(shù)據(jù)。
protocol buffers 還有更多的特性,比如說在 message 中定義映射和處理向后兼容。如果你對(duì)這些感興趣,可以查看 Google 的文檔。
最后值得一提的是,Remote Procedure Calls這個(gè)項(xiàng)目使用了 protocol buffers 并且看起來非常不錯(cuò),訪問GRPC了解更多吧。