ProtocolBuffer for Swift

參考:http://www.reibang.com/p/8c6c009bc500

http://blog.csdn.net/kmyhy/article/details/70243065

第一步挽拔、配置環(huán)境(跟著github 上面配置就行了)

第二步橙喘、把項(xiàng)目從gitHub拉下來(以下都是根據(jù)這個(gè)github的內(nèi)容配置的):https://github.com/alexeyxo/protobuf-swift (使用終端操作)

以下兩個(gè)方法供使用:

$ git clone git@github.com:alexeyxo/protobuf-swift.git

$ git clone https://github.com/alexeyxo/protobuf-swift.git

第三步停团、執(zhí)行 ./scripts/build.sh 文件? (使用終端操作)

使用終端cd到protobuf-swift目錄下暇检,然后直接在終端輸入以下命令執(zhí)行(貌似這是一個(gè)腳本竹勉,反正正常執(zhí)行會(huì)生成一大坨文件)

$ ./scripts/build.sh

第四步棋弥、集成protobuf-swift ?(手動(dòng)拖進(jìn)項(xiàng)目媒咳, 或者pod管理)

1.直接拖拽進(jìn)你的工程中凶杖,然后添加編譯文件:target--> build phases? -> Link binary with libraries? (注意: 拖進(jìn)去的/ProtocolBuffers.xcodeproj文件瓦糕, 在你的工程里不會(huì)存在真實(shí)的文件夾底洗, 這里拖進(jìn)去只是一個(gè)引用)

2.然后記得 pod 一下 或者把Source文件夾拖進(jìn)工程:protobuf-swift/Source ? ?(和 使用第三方框架一樣原理)

$ pod 'ProtocolBuffers-Swift'

$ pod install

第五步、用你自己定義的 .proto 文件生成一個(gè) .swift 文件咕娄,.proto 文件使用來寫protobuf代碼的 (使用終端操作)

1.創(chuàng)建(touch)一個(gè) .proto 文件 亥揖,然后 protobuf 代碼就全部寫在這個(gè) .ptoto 文件里面

$ touch ?Test.proto

例如代碼:

syntax = "proto3";

message Test {

string sessionId = 1;

string name = 2;

string authorites = 3;

}

2.cd到你的 .proto 文件位置, 然后 使用終端 編譯成 swift文件

$ protoc? Test.proto --swift_out="./"

3. 生成的 swift文件就可以直接拖進(jìn)你的項(xiàng)目工程中了

第六步圣勒、祝你好運(yùn)费变。






對(duì)于大部分需要后臺(tái)支持的 App 來說,轉(zhuǎn)化和存儲(chǔ)數(shù)據(jù)是非常重要的工作圣贸。和 web service 交互時(shí)挚歧,程序員常常需要發(fā)送或接收 JSON/XML 數(shù)據(jù),創(chuàng)建數(shù)據(jù)結(jié)構(gòu)并傳遞它們吁峻。

盡管已經(jīng)有許多序列化/反序列化框架和 APIs滑负,但這里有一個(gè)維護(hù)性的問題,比如版本管理以及當(dāng)后臺(tái)模型改變后需要對(duì)對(duì)象解析器進(jìn)行修改用含。

如果你想創(chuàng)建全新的矮慕、健壯的后端和前端服務(wù),請(qǐng)嘗試 protocol buffers耕餐,它是一個(gè)和語言無關(guān)的序列化結(jié)構(gòu)數(shù)據(jù)的方法凡傅,由 Google 所開發(fā)。許多時(shí)候肠缔,它都要比別的常用方法比如 JSON夏跷、XML 要更靈活高效哼转。

它有一個(gè)重要的特點(diǎn),只需要定義一次數(shù)據(jù)結(jié)構(gòu)槽华,編譯器就能夠生成多種語言的代碼——包括Swift! 它所產(chǎn)生的類文件能夠毫不費(fèi)力地對(duì)對(duì)象進(jìn)行讀和諧壹蔓。

在本教程中,你將啟動(dòng)一個(gè)Python服務(wù)器猫态,并集成一個(gè)已有iOSAPP 中的數(shù)據(jù)佣蓉。然后,我們將介紹如何使用 protocol buffer亲雪,如何配置環(huán)境勇凭,如何用 protocol buffers 傳遞數(shù)據(jù)。

你是不是仍然覺得沒有必要使用 protocol buffers义辕?請(qǐng)看下面虾标。

本教程假設(shè)你具備一定的 iOS 和 Swift 基本技能,了解基本的服務(wù)端編程灌砖,知道怎么使用終端璧函。

同時(shí),確認(rèn)你使用的是最新版的 Xcode 8.2基显。

開始

RWCards 是一個(gè) app蘸吓,允許你查看自己的門票以及活動(dòng)發(fā)言者清單。

首先下載開始項(xiàng)目,打開目錄下面的 Starter撩幽。

請(qǐng)先熟悉一下這三部分內(nèi)容:

客戶端

在 Starter/RWCards 目錄库继,打開 RWCards.xcworkspace 瀏覽一下主要的項(xiàng)目文件,包括:

SpeakersListViewController.swift 負(fù)責(zé)列出發(fā)言者名單摸航。這個(gè)控制器是一個(gè)模板制跟,因?yàn)槲覀冞€沒有創(chuàng)建模型對(duì)象。

SpeakersViewModel.swift 充當(dāng) SpeakersListViewController 的數(shù)據(jù)源酱虎。它包含了發(fā)言者的列表雨膨。

CardViewController.swift 負(fù)責(zé)顯示一個(gè)出席者的圖標(biāo)以及他們的社交信息。

RWService.swift 負(fù)責(zé)集成客戶端和后臺(tái)读串。我們將使用 Alamofire 來調(diào)用后臺(tái)服務(wù)聊记。

Main.storyboard 包含了整個(gè) App 用到的所有 scene。

這個(gè)項(xiàng)目通過 CocoaPods 集成了這兩個(gè)框架:

Swift Protobuf 允許你在你的 Xcode 項(xiàng)目中使用 protocol buffers恢暖。

Almofire 是一個(gè) HTTP 網(wǎng)絡(luò)庫排监,我們用它來訪問服務(wù)器。

注意:本教程中我們將使用 Swift Protobuf 0.9.24 以及 Google 的 Protoc Compiler 3.1.0杰捂。它們都已經(jīng)內(nèi)置在開始項(xiàng)目中了舆床,因此你不需要做什么。

如何使用 Protocol Buffers?

要使用 protocol buffers,首先必須定義一個(gè) .proto 文件挨队。在這個(gè)文件中谷暮,你需要定義一個(gè)消息類型,用于定義你的架構(gòu)或數(shù)據(jù)結(jié)構(gòu)盛垦。這是一個(gè) .proto 文件的例子:

syntax ="proto3";message Contact {? enum ContactType {? ? SPEAKER =0;? ? ATTENDANT =1;? ? VOLUNTEER =2;? }stringfirst_name =1;stringlast_name =2;stringtwitter_name =3;stringemail =4;stringgithub_link =5;? ContactTypetype=6;stringimageName =7;};

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

這里指定了一個(gè) Contact 消息以及它的屬性湿弦。

定義好 .proto 文件后,你只需要將文件傳遞給 protocol buffer 編譯器腾夯,它將根據(jù)你選擇的語言生成數(shù)據(jù)訪問類(在 Swift 中是結(jié)構(gòu))颊埃。然后你就可以通過這個(gè)類/結(jié)構(gòu)了。簡(jiǎn)單吧蝶俱!

https://koenig-media.raywenderlich.com/uploads/2016/12/pb3.png’ width=’600’/>

編譯器對(duì)消息進(jìn)行翻譯班利,將它的值類型映射為指定的語言,并生成相應(yīng)的模型對(duì)象文件跷乐。后面會(huì)詳細(xì)介紹如何定義消息肥败。

在準(zhǔn)備使用 Protocol buffers 之前,你首先應(yīng)當(dāng)明白為什么要在你的項(xiàng)目中使用它愕提。

好處

JSON 和 XML 是程序員存儲(chǔ)和數(shù)據(jù)轉(zhuǎn)換的標(biāo)準(zhǔn)方式,但 protocol buffers 具有這些優(yōu)勢(shì):

更快皿哨,更星城取:根據(jù) Google 所說,protocol buffers 比起 XML 數(shù)據(jù)量小 3-10 倍证膨,速度快 20-100 倍如输。請(qǐng)閱讀 Damien Bod 的這篇文章,其中對(duì)不同主流數(shù)據(jù)格式的讀寫時(shí)間進(jìn)行了比較央勒。

類型安全:protocol buffers 和 Swift 一樣是類型安全的不见。通過 protocol buffer 語言,你必須為每個(gè)屬性指定屬性崔步。

自動(dòng)反序列化:你不再需要編寫千篇一律的解析代碼稳吮。只需要修改你的 proto 文件,重新生成數(shù)據(jù)訪問類井濒。

面向分享:可以通過指定的語言灶似,跨平臺(tái)分享模型,這意味著進(jìn)行跨平臺(tái)時(shí)能夠減少工作瑞你。

局限

Protocol buffers 有優(yōu)點(diǎn)酪惭,也有缺點(diǎn):

時(shí)間和工作量:將已有項(xiàng)目中切換到 protocol buffers 上時(shí),可能會(huì)增加一些成本者甲,因?yàn)檫@種轉(zhuǎn)換需要成本春感。此外,它需要學(xué)習(xí)一種新語法。

人類不可讀:XML 和 JSON 更加具有描述性鲫懒,更容易閱讀纺铭。Protocol buffer 的原始格式不是自描述的。如果沒有 .proto 文件刀疙,你根本無法看懂?dāng)?shù)據(jù)舶赔。

它不是全能的:如果我們想使用樣式表(比如 XSLT),那么最好用 XML谦秧。protocol buffers 并不適合于這種目的竟纳。

語言不支持:編譯器可能不支持你想使用的那種語言。

它并不是什么情況下都能夠使用疚鲤,因此對(duì)于 protocol buffers 仍然有很多爭(zhēng)論锥累!

編譯運(yùn)行 App,看看它是什么樣子集歇。

https://koenig-media.raywenderlich.com/uploads/2016/12/pb2-6.gif’ width=’400’/>

不幸的是桶略,你不能看到任何數(shù)據(jù),因?yàn)閿?shù)據(jù)源還沒有準(zhǔn)備好诲宇。你的任務(wù)是調(diào)用后臺(tái)服務(wù)际歼,用發(fā)言者列表和參與者角標(biāo)來刷新 UI。首先姑蓝,我們來看一下開始項(xiàng)目中提供的這兩部分鹅心。

Protocol Buffer 的 Schema

回到 Finder,進(jìn)入 Starter/ProtoSchema 目錄纺荧,你會(huì)看到如下文件:

contact.proto 通過 protocol buffer 語法描述了一個(gè) contact 的結(jié)構(gòu)旭愧。后面會(huì)詳細(xì)介紹。

protoScript.sh 是一個(gè) bash 腳本宙暇,將通過 protocol buffer 編譯器生成 contact.proto 所定義的 Swift 結(jié)構(gòu)和 Python 類输枯。

后臺(tái)

在 folder Starter/Server 下面有如下文件:

RWServer.py 是一個(gè) Python 服務(wù)器,基于 Flask 構(gòu)建占贫。它有兩個(gè) GET 請(qǐng)求:

/currentUser 返回當(dāng)前參會(huì)者的信息桃熄。

/speakers 返回發(fā)言者的列表。

RWDict.py 包含了 RWServer 會(huì)讀取的一個(gè)發(fā)言者的 dictionary靶剑。

然后是配置 protocol buffer 運(yùn)行環(huán)境蜻拨。在后面,你將安裝 Google protocol buffer 編譯器的運(yùn)行環(huán)境桩引、Swift Protobuf 插件缎讼、以及運(yùn)行 Python 服務(wù)器所需要的 Flask。

安裝環(huán)境

要使用 protocol buffers坑匠,我們需要安裝一些工具和庫血崭。開始項(xiàng)目中已經(jīng)包含了一個(gè) protoInstallation.sh 腳本,它會(huì)為你完成所有的工作。而且更好的是夹纫,它還會(huì)在安裝某個(gè)庫之前檢查其它庫是否安裝了咽瓷。

這個(gè)腳本的執(zhí)行會(huì)花一點(diǎn)時(shí)間,尤其是安裝 Google 的 protocol buffer 庫的時(shí)候舰讹。打開你的終端茅姜,切到開始項(xiàng)目目錄,執(zhí)行命令:

$ ./protoInstallation.sh

注意:執(zhí)行這個(gè)腳本月匣,可能需要你輸入管理員密碼钻洒。

當(dāng)腳本執(zhí)行完成,你可以再次執(zhí)行它锄开,并確認(rèn)你看到如下輸出:

https://koenig-media.raywenderlich.com/uploads/2016/12/Screen-Shot-2016-12-10-at-2.51.38-PM.png’ width=’550’/>

如果你看到上圖的樣子素标,說明腳本執(zhí)行成功。如果腳本沒有執(zhí)行成功萍悴,可能你的管理員密碼輸錯(cuò)了头遭。如果是這樣,請(qǐng)重新運(yùn)行腳本癣诱,對(duì)于已經(jīng)執(zhí)行成功的內(nèi)容计维,它不會(huì)重復(fù)執(zhí)行的。

這段腳本進(jìn)行了如下工作:

安裝 Flask狡刘,以便能夠在本機(jī)運(yùn)行 Python 服務(wù)器享潜。

從 Starter/protobuf-3.1.0 目錄編譯 Google protocol buffer 編譯器。

安裝 Python 的 protocol buffer 模塊嗅蔬,以便服務(wù)器能夠調(diào)用 protobuf 庫。

將 Swift Protobuf 插件拷貝到 /usr/local/bin疾就。這使得 Protobuf 編譯器能夠生成 Swift 結(jié)構(gòu)澜术。

注意:要具體了解這個(gè)腳本是什么意思,請(qǐng)用文本編輯器打開 protoInstallation.sh 查看詳細(xì)命令猬腰。這需要你具備一定的 bash 知識(shí)鸟废。

萬事俱備,讓我們開始使用 protocol buffers 吧姑荷!

定義一個(gè) .proto 文件

.proto 文件用于定義 protocol buffer 消息盒延,所謂消息描述了數(shù)據(jù)的結(jié)構(gòu)。將它傳遞給 protocol buffer 編譯器之后鼠冕,就會(huì)生成數(shù)據(jù)訪問器結(jié)構(gòu)添寺。

注意:在本教程中,我們將使用 proto3懈费,這是最新的 protocol buffer 語言版本计露。要深入了解這種語法以及如何定義 proto3 文件,請(qǐng)看 Google 的官方指南

用任意文本編輯器打開 ProtoSchema/contact.proto票罐。這里叉趣,我們使用現(xiàn)成的 .proto 文件,它已經(jīng)為我們定義好了 Contact 消息和 Speakers 消息:

syntax ="proto3";message Contact {// 1enumContactType {// 2SPEAKER =0;? ? ATTENDANT =1;? ? VOLUNTEER =2;? }stringfirst_name =1;//3stringlast_name =2;stringtwitter_name =3;stringemail =4;stringgithub_link =5;? ContactType type =6;stringimageName =7;};message Speakers {// 4repeated Contact contacts =1;};

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

這個(gè)定義包含了如下內(nèi)容:

Contact 模型描述了某個(gè)人的聯(lián)系信息该押。在 App 中疗杉,這些信息會(huì)顯示在這個(gè)人的徽章下面。

每個(gè) Contact 都有一個(gè)類別蚕礼,以便區(qū)分這個(gè)人是發(fā)言者還是聽眾烟具。

proto 文件中的 message 和 enum 定義中的每個(gè)字段都必須賦一個(gè)遞增的、唯一的 tag 值闻牡。這個(gè) tag 值會(huì)被 message 二進(jìn)制格式中用作唯一標(biāo)識(shí)净赴,以便保持它們的順序。更多關(guān)于 tag 值和字段管理的內(nèi)容罩润,請(qǐng)參考 google 文檔中的reserved fields玖翅。

Speakers 模型是一個(gè)包含了 Contact 對(duì)象的集合。repeated 表示這是一個(gè)對(duì)象的數(shù)組割以。

生成 Swift 結(jié)構(gòu)

當(dāng)我們把 contact.proto 傳遞給 protoc 程序時(shí)金度,proto 文件就生成了 Swift 結(jié)構(gòu)。這些結(jié)構(gòu)將繼承 ProtobufMessage 協(xié)議严沥。protoc 會(huì)為每個(gè) Swift 字段猜极、初始化方法、以及序列化/反序列化方法提供屬性消玄。

注意:關(guān)于 Swfit protobuf API 的更多內(nèi)容跟伏,請(qǐng)參考Apple 的 Protobuf API 文檔

打開終端翩瓜,切到 Starter/ProtoSchema 目錄受扳。在文本編輯器中打開 protoScript.sh :

#!/bin/bashecho'Running ProtoBuf Compiler to convert .proto schema to Swift'protoc --swift_out=. contact.proto //1echo'Running Protobuf Compiler to convert .proto schema to Python'protoc -I=. --python_out=. ./contact.proto //2

1

2

3

4

5

1

2

3

4

5

這個(gè)腳本會(huì)調(diào)用 protoc 兩次——一次生成了 Swift 源文件,一次生成了 Python 文件兔跌。

回到終端勘高,執(zhí)行這個(gè)腳本:

$ ./protoScript.sh

你會(huì)看到:

Running ProtoBuf Compilertoconvert.proto schematoSwiftprotoc-gen-swift: Generating Swiftforcontact.protoRunning Protobuf Compilertoconvert.proto schematoPython

1

2

3

1

2

3

這樣我們就根據(jù) contact.proto 文件生成了 Swift/Python 源文件。

在 ProtoSchema 目錄坟桅,你會(huì)看到兩個(gè)文件华望,一個(gè) Swift 的,一個(gè)是 Python 的仅乓。注意每個(gè)新生成的文件都會(huì)以 .pb.swift 或 .ph.py 文件為后綴名赖舟。pb 后綴表示它是 protocol buffer 生成的。

https://koenig-media.raywenderlich.com/uploads/2016/12/generatedFiles-650x71.png’ width=’700’/>

將 contact.pb.swift 拖到 Xcode 的項(xiàng)目導(dǎo)航器中方灾,并放到 Protocol Buffer Objects 文件夾下建蹄。注意勾選 “Copy items if needed” 選項(xiàng)碌更。通過 Finder 或終端,將 contact_pb2.py 拷貝到 Starter/Server 文件夾洞慎。

大概看一下 contact.pb.swift 和 contact_pb2.py 的內(nèi)容痛单,看看 proto 的消息是如何映射成兩種語言的結(jié)構(gòu)的。

我們已經(jīng)有了模型對(duì)象劲腿,是時(shí)候來使用他了旭绒!

運(yùn)行本地服務(wù)器

示例項(xiàng)目包含了一個(gè)內(nèi)置的 Python 服務(wù)器。這個(gè)服務(wù)器提供兩個(gè) GET 接口:一個(gè)返回聽眾的徽章信息焦人,一個(gè)返回發(fā)言者列表挥吵。

本教程不涉及服務(wù)端代碼。但是需要注意 contact_pb2.py 模型文件花椭。另外如果你有興趣忽匈,可以看一下 RWServer.py。當(dāng)然對(duì)于本教程而言矿辽,這不是必須的丹允。

要開啟服務(wù)器,請(qǐng)打開終端袋倔,切換到 Starter/Server 目錄雕蔽。 執(zhí)行命令:

$ python RWServer.py

你會(huì)看到:

https://koenig-media.raywenderlich.com/uploads/2016/12/startServer.png’ width=’600’/>

測(cè)試 GET 請(qǐng)求

用瀏覽器進(jìn)行 HTTP 請(qǐng)求,就可以看見 protocol buffer 的原始數(shù)據(jù)格式宾娜。

訪問http://127.0.0.1:5000/currentUser批狐,你會(huì)看到:

https://koenig-media.raywenderlich.com/uploads/2016/12/Screen-Shot-2016-12-10-at-11.41.52-AM-650x61.png’ width=’600’/>

試一下另外一個(gè)接口,http://127.0.0.1:5000/speakers:

https://koenig-media.raywenderlich.com/uploads/2016/12/Screen-Shot-2016-12-19-at-10.14.00-PM-650x70.png’ width=’600’/>

注意:你可以保持本地服務(wù)器的運(yùn)行前塔,也可以停止服務(wù)器嚣艇,然后在需要測(cè)試RWCards App 時(shí)再啟動(dòng)它。

我們啟動(dòng)了一個(gè)簡(jiǎn)單服務(wù)器华弓,這個(gè)服務(wù)器使用了 proto 文件中的消息作為我們的模型髓废。太好了!

調(diào)用服務(wù)

讓本地服務(wù)器啟動(dòng)并保持運(yùn)行该抒,讓我們?cè)?App 中調(diào)用它的服務(wù)。在 RWService.swift 中顶燕,將 RWService 類替換為:

class RWService {staticletshared = RWService()// 1leturl ="http://127.0.0.1:5000"privateinit() { }? func getCurrentUser(_ completion: @escaping (Contact?) -> ()) {// 2letpath ="/currentUser"Alamofire.request("\(url)\(path)").responseData { responseinifletdata = response.result.value{// 3letcontact =try? Contact(protobuf: data)// 4completion(contact)? ? ? }? ? ? completion(nil)? ? }? }}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

這個(gè)類負(fù)責(zé)和我們的Python 服務(wù)器進(jìn)行通訊凑保。我們用它實(shí)現(xiàn)了 currentUser 接口調(diào)用。代碼說明如下:

shared 是一個(gè)單實(shí)例涌攻,用于訪問網(wǎng)絡(luò)接口欧引。

getCurrentUser(_:) 方法用于請(qǐng)求 /currentUser 接口,以獲取當(dāng)前用戶數(shù)據(jù)恳谎。這個(gè)用戶在后臺(tái)是以硬編碼的形式定義的芝此。

通過 if let 語句對(duì)響應(yīng)值進(jìn)行解包憋肖。

data 對(duì)象中是 protocol buffer 的二進(jìn)制形式。Contact 構(gòu)造函數(shù)把它作為參數(shù)婚苹,然后對(duì)收到的消息進(jìn)行解碼岸更。

將 protocol buffer 轉(zhuǎn)換成對(duì)象非常簡(jiǎn)單,只需調(diào)用這個(gè)對(duì)象的構(gòu)造函數(shù)并傳入 data膊升。不需要你手動(dòng)解析數(shù)據(jù)怎炊。Swift Protobuf 庫自動(dòng)為你完成一切。

獲得接口數(shù)據(jù)之后廓译,我們要把它顯示出來评肆。

顯示聽眾徽章

打開 CardViewController.swift,在 viewWillAppear(_:) 方法后面添加方法:

func fetchCurrentUser() { //1RWService.shared.getCurrentUser{ contactinif let contact = contact {? ? ? self.configure(contact)? ? }? }}func configure(_ contact: Contact) { //2self.attendeeNameLabel.attributedText= NSAttributedString.attributedString(for: contact.firstName,and: contact.lastName)? self.twitterLabel.text= contact.twitterNameself.emailLabel.text= contact.emailself.githubLabel.text= contact.githubLinkself.profileImageView.image= UIImage(named: contact.imageName)}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

這兩個(gè)方法用于從服務(wù)器抓取數(shù)據(jù)并顯示用戶徽章非区。代碼解釋如下:

fetchCurrentUser() 負(fù)責(zé)請(qǐng)求服務(wù)器瓜挽,抓取當(dāng)前用戶信息,用 contact 對(duì)象刷新 CardViewController征绸。

configure(_:) 需要一個(gè) Contact 參數(shù)久橙,并對(duì)控制器中的 UI 控件進(jìn)行賦值。

稍后我們?cè)僬{(diào)用這兩個(gè)方法歹垫,現(xiàn)在我們需要從 ContactType 枚舉派生出一個(gè)可讀的聽眾類型剥汤。

自定義 Protocol Buffer 類

我們需要用一個(gè)方法將枚舉類型轉(zhuǎn)換成字符串類型,這樣發(fā)言者的徽章會(huì)顯示成 SPEAKER 而不是 0排惨。

這里有一個(gè)問題吭敢。因?yàn)槊慨?dāng)修改消息之后都需要重新生成 .proto 文件,那么我們?nèi)绾卧谀P椭屑尤胱约旱姆椒兀?/p>

Swift 的擴(kuò)展能夠解決這個(gè)問題暮芭。通過擴(kuò)展鹿驼,我們可以向某個(gè)類中添加方法,而不需要修改它用來的代碼辕宏。

創(chuàng)建一個(gè) contact+extension.swift 文件畜晰,將它加到 Protocol Buffer Objects 文件夾中。這個(gè)文件的內(nèi)容編輯如下:

extension Contact {? func contactTypeToString()->String {switchtype {case.speaker:return"SPEAKER"case.attendant:return"ATTENDEE"case.volunteer:return"VOLUNTEER"default:return"UNKNOWN"}? }}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

1

2

3

4

5

6

7

8

9

10

11

12

13

14

contactTypeToString() 方法將 ContactType 類型轉(zhuǎn)換為可顯示的字符串瑞筐。

打開 CardViewController.swift凄鼻,在 configure(_:) 中加入:

self.attendeeTypeLabel.text= contact.contactTypeToString()

1

1

這句代碼將 attendeeTypeLabel 的文本顯示為由 contact 類型轉(zhuǎn)換來的字符串表示。

最后聚假,在 ViewWillAppear 方法的 applyBusinessCardAppearance() 一句后面加入:

ifisCurrentUser {? fetchCurrentUser()}else{// TODO: handle speaker}

1

2

3

4

5

1

2

3

4

5

isCurrentUser 當(dāng)前被硬編碼為 true块蚌,當(dāng)我們需要支持發(fā)言者類型時(shí)我們?cè)賮硇薷乃.?dāng) isCurrentUser 為 true 時(shí)膘格,調(diào)用 fetchCurrentUser()峭范,這會(huì)抓取當(dāng)前用戶信息并顯示到卡片中。

運(yùn)行程序瘪贱,查看聽眾的徽章纱控。

https://koenig-media.raywenderlich.com/uploads/2016/12/Simulator-Screen-Shot-Dec-10-2016-11.00.10-AM.png’ width=’160’/>

顯示發(fā)言者列表

在 My Badge 頁完成之后辆毡,我們需要來完成 Spearkers 頁面。

打開 RWService.swift甜害,添加方法:

func getSpeakers(_ completion:@escaping(Speakers?) -> ()){ // 1letpath= "/speakers"Alamofire.request("\(url)\(path)").responseData{responseinifletdata=response.result.value{ // 2letspeakers=try?Speakers(protobuf: data)// 3completion(speakers)}? }completion(nil)}

1

2

3

4

5

6

7

8

9

10

1

2

3

4

5

6

7

8

9

10

看起來很熟悉吧舶掖?這和 getCurrentUser(_:) 其實(shí)是一樣的,不過它獲取的是發(fā)言者數(shù)據(jù)唾那。發(fā)言者是一個(gè) Contact 對(duì)象數(shù)組访锻,表示所有會(huì)議發(fā)言者。

打開 SpeakersViewModel.swift 闹获,替換文件內(nèi)容為:

classSpeakersViewModel{varspeakers: Speakers!varselectedSpeaker: Contact?? init(speakers: Speakers) {? ? self.speakers = speakers? }? func numberOfRows()->Int {returnspeakers.contacts.count? }? func numberOfSections()->Int {return1}? func getSpeaker(forindexPath: IndexPath)->Contact {returnspeakers.contacts[indexPath.item]? }? func selectSpeaker(forindexPath: IndexPath) {? ? selectedSpeaker = getSpeaker(for: indexPath)? }}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

這個(gè)類用作 SpeakersListViewController 的數(shù)據(jù)源期犬,用于顯示一個(gè)會(huì)議發(fā)言者的列表。speakers 是一個(gè) Contacts 數(shù)組避诽,用 /speakers 接口返回的數(shù)據(jù)填充龟虎。這個(gè)數(shù)據(jù)源為表格的每一行提供一個(gè) Contact 對(duì)象。

view model 準(zhǔn)備好之后沙庐,我們就可以來配置單元格了鲤妥。打開 SpeakerCell.swift 添加方法:

func configure(with contact: Contact) {? profileImageView.image= UIImage(named: contact.imageName)? nameLabel.attributedText= NSAttributedString.attributedString(for: contact.firstName,and: contact.lastName)}

1

2

3

4

1

2

3

4

這個(gè)方法使用一個(gè) Contact 參數(shù),用它來對(duì) cell 的 UIImage 和 UILabel 進(jìn)行賦值拱雏。每個(gè) cell 會(huì)包含一張發(fā)言者的圖片棉安,以及姓名。

然后铸抑,打開 SpeakersListViewController.swift贡耽,在 viewWillAppear(_:) 的父類方法調(diào)用之后添加:

RWService.shared.getSpeakers{ [unowned self] speakersinif let speakers = speakers {? ? self.speakersModel= SpeakersViewModel(speakers: speakers)? ? self.tableView.reloadData()? }}

1

2

3

4

5

6

1

2

3

4

5

6

getSpeakers(_:) 方法負(fù)責(zé)請(qǐng)求并返回一個(gè)發(fā)言者列表。然后用返回的發(fā)言者列表鹊汛,初始化 SpeakersViewModel 對(duì)象蒲赂。然后用抓取到的數(shù)據(jù)刷新表格。

然后需要為表格中的每一行分配一個(gè)發(fā)言者以便顯示刁憋。將 tableView(_:cellForRowAt:) 方法代碼替換為:

letcell = tableView.dequeueReusableCell(withIdentifier:"SpeakerCell",for: indexPath)as! SpeakerCellifletspeaker = speakersModel?.getSpeaker(for: indexPath) {? cell.configure(with: speaker)}returncell

1

2

3

4

5

1

2

3

4

5

getSpeaker(for:) 方法返回指定 indexPath 所對(duì)應(yīng)的 contact 對(duì)象滥嘴。configure(with:) 方法是 SpeakCell 中定義的,作用是設(shè)置單元格的發(fā)言者圖片和姓名至耻。

當(dāng)發(fā)言者列表中的 cell 被點(diǎn)擊若皱,我們要用 CardViewController 顯示所選發(fā)言者信息。打開CardViewController.swift 尘颓,新增如下屬性:

var speaker: Contact?

我們最終會(huì)將所選的發(fā)言者傳遞個(gè)這個(gè)屬性是尖。然后,我們需要顯示發(fā)言者泥耀。將 // TODO: handle speaker 一行替換為:

ifletspeaker = speaker {? configure(speaker)}

1

2

3

1

2

3

這里檢查了 speaker 是否不為空,如果不為空蛔添,調(diào)用 configure() 方法痰催,這個(gè)方法將用指定發(fā)言者信息刷新卡片兜辞。

回到 SpeakersListViewController.swift,傳入選定的發(fā)言者夸溶。首先在 tableView(_:didSelectRowAt:) 方法中逸吵,在 performSegue(withIdentifier:sender:) 之前加入:

speakersModel?.selectSpeaker(for: indexPath)

這句會(huì)在 speakersModel 中記錄用戶所選擇的發(fā)言者。

然后缝裁,在 prepare(for:sender:) 方法中扫皱,在 vc.isCurrentUser = false 一句后面添加:

vc.speaker = speakersModel?.selectedSpeaker

這句將 selectedSpeaker 傳遞給 CardViewController 启昧,以便顯示它族购。

看一下你的本地服務(wù)器是否仍然運(yùn)行,然后編譯運(yùn)行 Xcode〖樱現(xiàn)在粹污,你會(huì)發(fā)現(xiàn) App 已經(jīng)能夠顯示用戶徽章和發(fā)言者列表了段多。

https://koenig-media.raywenderlich.com/uploads/2016/12/pb4-1.gif” width=”300”/>

我們用一個(gè) Python 服務(wù)器和一個(gè) Swift 客戶端打造了一個(gè)端到端應(yīng)用。它們共用由同一個(gè) proto 文件所生成的模型壮吩。如果你想修改模型进苍,只需要運(yùn)行一下編譯器再次生成模型即可,這樣你就可以同時(shí)在服務(wù)端和客戶端使用了鸭叙!

結(jié)束

你可以在這里下載最終完成項(xiàng)目觉啊。

在本教程中,我們學(xué)習(xí)了基本的 protocol buffers 用法沈贝,如何定義 .proto 文件杠人、用編譯器生成 Swift 代碼。我們也學(xué)習(xí)了如何啟動(dòng)一個(gè)簡(jiǎn)單的 Flask 本地服務(wù)器缀程,通過這個(gè)服務(wù)器我們創(chuàng)建了一個(gè)服務(wù)用于發(fā)送 protocol buffer 二進(jìn)制到客戶端搜吧,用 protocol buffers 解析這些數(shù)據(jù)是非常簡(jiǎn)單的!

關(guān)于 protocol buffers 還有很多內(nèi)容杨凑,比如定義消息映射以及向后兼容滤奈。如果你對(duì)這些感興趣,請(qǐng)參考Google 文檔撩满。

還有一件有意思的事情就是 protocol buffers 可以用在遠(yuǎn)程過程調(diào)用蜒程。請(qǐng)參考GRPC

有任何問題和建議伺帘,請(qǐng)?jiān)谙旅媪粞浴?/p>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末昭躺,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子伪嫁,更是在濱河造成了極大的恐慌领炫,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件张咳,死亡現(xiàn)場(chǎng)離奇詭異帝洪,居然都是意外死亡似舵,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門葱峡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來砚哗,“玉大人,你說我怎么就攤上這事砰奕≈虢妫” “怎么了?”我有些...
    開封第一講書人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵军援,是天一觀的道長仅淑。 經(jīng)常有香客問我,道長盖溺,這世上最難降的妖魔是什么漓糙? 我笑而不...
    開封第一講書人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮烘嘱,結(jié)果婚禮上昆禽,老公的妹妹穿的比我還像新娘。我一直安慰自己蝇庭,他們只是感情好醉鳖,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著哮内,像睡著了一般盗棵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上北发,一...
    開封第一講書人閱讀 52,246評(píng)論 1 308
  • 那天纹因,我揣著相機(jī)與錄音,去河邊找鬼琳拨。 笑死瞭恰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的狱庇。 我是一名探鬼主播惊畏,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼密任!你這毒婦竟也來了颜启?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤浪讳,失蹤者是張志新(化名)和其女友劉穎缰盏,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乳规,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年形葬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片暮的。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖淌实,靈堂內(nèi)的尸體忽然破棺而出冻辩,到底是詐尸還是另有隱情,我是刑警寧澤拆祈,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布恨闪,位于F島的核電站,受9級(jí)特大地震影響放坏,放射性物質(zhì)發(fā)生泄漏咙咽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一淤年、第九天 我趴在偏房一處隱蔽的房頂上張望钧敞。 院中可真熱鬧,春花似錦麸粮、人聲如沸溉苛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽愚战。三九已至,卻和暖如春齐遵,著一層夾襖步出監(jiān)牢的瞬間寂玲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來泰國打工梗摇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拓哟,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓留美,卻偏偏與公主長得像彰檬,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子谎砾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容