Protocol Buffers 在 iOS 中的使用

因?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è)腳本做了這些事:

  1. 安裝 Flask 以運(yùn)行 Python 本地服務(wù)。
  2. 從 Starter/protobuf-3.1.0 目錄下生成 protocol buffer 編譯器。
  3. 安裝 protocol buffer 的 Python 模塊,這樣服務(wù)端可以使用 Protobuf 庫。
  4. 將 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.

  1. Contact 模型用于描述名片信息。在 app 中會(huì)被顯示在 badges 頁。
  2. 每一個(gè) contact 應(yīng)該分類,這樣才能區(qū)別出是訪客還是演講者。
  3. proto 文件中的每一條 messageenum 必須指派一個(gè)增量且唯一的數(shù)字標(biāo)簽。這些數(shù)字用來用于區(qū)分信息二進(jìn)制格式,這很重要。訪問reserved fields可以了解更多關(guān)于標(biāo)簽的信息。
  4. 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)求:

  1. shared 是一個(gè)發(fā)起網(wǎng)絡(luò)請(qǐng)求的單例。
  2. getCurrentUser(_:) 方法通過 /currentUser 路徑發(fā)起了獲取用戶信息的網(wǎng)絡(luò)請(qǐng)求,后臺(tái)會(huì)返回一個(gè)硬編碼的用戶信息。
  3. if let 獲取了數(shù)據(jù)。
  4. 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ù),并用來配置名片:

  1. fetchCurrentUser() 請(qǐng)求服務(wù)器去獲取當(dāng)前用戶的信息,并使用 * contact* 來配置 * CardViewController*。
  2. 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了解更多吧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容