gRPC is an universal remote procedure call framework developed by Google that has been gaining interests among many software developers that were developing microservices in recent years because its open source, language neutral, compact binary size, HTTP/2 support, and cross platform compatibility. According to Google:

gRPC is a language-neutral, platform-neutral remote procedure call (RPC) framework and toolset developed at Google. It lets you define a service using Protocol Buffers, a particularly powerful binary serialization toolset and language. It then lets you generate idiomatic client and server stubs from your service definition in a variety of languages

In a nutshell, gRPC server application distributes sets of service methods that can be directly call by the gRPC clients across different machines and platforms with the same parameter and return type. gRPC uses Protocol Buffer binary serialization for the request and response message format between the server and client. It is more compact than JSON and converts nicely to the native language data type using the Protocol Buffer compiler. The compiler accepts proto file that is declared using the Interface Definition Language and compiles it to the target language specified such as Swift or Java. This compact binary format is very well suited for connecting mobile application and web clients.

In this article, we are going to build a iOS Swift application that connects to the gRPC Server that runs locally. The server provides NoteService RPC Service that we can use to get list of notes, create new note, and delete note (CRUD) using iOS App we will build. The full source code for the project is also available at the project GitHub repository below.

https://github.com/alfianlosari/ios-grpc-note-crud-app

The are some prerequisites software that you need to install in your machine to follow along this article:

  1. node.js: Our gRPC Server we will use is built using the node.js gRPC library.
  2. Xcode 9.4 with Swift 4.1

What we will build

  1. Setup and Run gRPC Server Locally
  2. Overview of the Notes.proto file gRPC Service
  3. Setup Xcode Project and Cocoapod Dependencies
  4. Compile Proto File with Swift Protocol Buffer Compiler
  5. Swift Data Repository for gRPC Service
  6. List Notes
  7. Create New Note
  8. Delete Existing Note

Setup and Run gRPC Server Locally

The first step we are going to do is to clone the gRPC Server i have created from the GitHub Repository below.

https://github.com/alfianlosari/node-grpc-server-note-crud

I also wrote a Medium article about building the gRPC Server using node.js if you are interested on how the server was built. You can read the detail if you want by clicking the link below.

Building gRPC Service Server Note CRUD API with node.js

Open Terminal and go to your preferred working directory, then type the command below to clone the project into your machine, install the dependencies for the project, and start the server locally.

$ git clone https://github.com/alfianlosari/node-grpc-server-note-crud.git
$ cd node-grpc-server-note-crud
$ npm install
$ npm start
Server running at http://127.0.0.1:50051

Overview of the Notes.proto file gRPC Service

Inside the gRPC Server project directory there is a file with the name of notes.proto. This is the file where we declare our Protocol Buffer Messages and gRPC Service. It has 4 models which declared as message with their respective fields. Note that the NoteList Message has a repeated keyword in notes field, repeated means the type of the data is List/Array. It also declares the NoteService Service which provides 5 methods with parameter and return type to perform CRUD of Note. We will compile this notes.proto into Swift Data Type for our iOS App Client later using Swift Proto Buffer Compiler.

syntax = "proto3";

service NoteService {
rpc List (Empty) returns (NoteList) {}
rpc Get (NoteRequestId) returns (Note) {}
rpc Insert (Note) returns (Note) {}
rpc Update (Note) returns (Note) {}
rpc Delete (NoteRequestId) returns (Empty) {}
} message Empty {} message Note {
string id = 1;
string title = 2;
string content = 3;
} message NoteList {
repeated Note notes = 1;
} message NoteRequestId {
string id = 1;
}

Setup Xcode Project and Install Cocoapod Dependencies

Now, we are going to create iOS App using Xcode. Open Xcode and create new project with Single View App template. Enter the product name and uncheck all the checkboxes.

Close Xcode and Open the terminal and navigate to the just created Xcode Project working directory. Initialize Cocoapod using pod init, open Podfile using your favorite Text Editor.

$ pod init
$ vim Podfile

Update the Podfile dependencies following the one below:

# platform :ios, '9.0'
target 'grpc_note' do
use_frameworks!
pod 'SwiftGRPC'
end

Save and close the Podfile. then run pod install to install the Cocoapod dependencies.

$ pod install

After the dependencies has been installed, open the project by clicking file with .xcworkspace extension. To be able to call our gRPC service locally, we need to allow insecure HTTP connection by adding key attributes to the info.plist file like the one below:

Compile Proto File with Swift Protocol Buffer Compiler

The next step is to compile the notes.proto file from the gRPC server folder that we had cloned from the GitHub repository earlier using Protocol Buffer and Swift Protobuf Compiler into Swift file.

First we need to download and install Google official Protocol Buffer Compiler. Type the command below into the Terminal to download the ProtocolBuffers v3.51 and install it to the /usr/local/bin $PATH location inside your machine.

$ curl -LOk https://github.com/protocolbuffers/protobuf/releases/download/v3.5.1/protoc-3.5.1-osx-x86_64.zip
$ unzip protoc-3.5.1-osx-x86_64.zip -d proto_buffer && cd proto_buffer
$ sudo cp bin/protoc /usr/local/bin
$ sudo cp -R include/google/protobuf/ /usr/local/include/google/protobuf
$ protoc --version

We also need to download and install Swift Protobuf Compiler 1.0.3 from from Apple GitHub Repository by cloning it from the repository, perform Makefile build, and install to the /usr/local/bin $PATH. Follow the command below inside the Terminal:

$ git clone https://github.com/grpc/grpc-swift.git
$ cd grpc-swift
$ git checkout tags/1.0.0
$ make
$ sudo cp protoc-gen-swift protoc-gen-swiftgrpc /usr/local/bin

Now we need to compile the notes.proto file into Swift file using the Swift Protocol Buffer Compiler we just installed. Inside the terminal, go to the gRPC Server project directory from the first step and run the following command in terminal:

$ protoc notes.proto \
--swift_out=. \
--swiftgrpc_out=Client=true,Server=false:.
$ ls | grep swift

There are 2 files that the compiler will create inside the directory, notes.pb.swift and notes.grpc.swift. Now we need to copy the files to our Xcode project. Make sure to check Copy Items if Needed.

  

Now build the Project to make sure the project build successfully. You can take a peek inside the Swift Files we just copied. We can see that the Protocol Buffer and gRPC Compiler generates Struct for our Message and Class for the NoteService gRPC client that we can use to call our gRPC server. We will start build our iOS App in the next steps!.

Swift Data Repository for gRPC Service

We will use a Data Repository Class as a centralized networking coordinator Singleton object that provides interface for calling the RPC CRUD methods.

Create a file called DataRepository.swift. For implementation, we use the shared Singleton pattern that exposes shared static properties to access the object. We also instantiate the client variable using the NoteServiceClient Class generated from the Swift gRPC Compiler passing the address of our server localhost and secure as false. We will add additional CRUD method function as we get along the article beginning from the List Notes.

DataRepository.swift

import Foundation
import SwiftGRPC
class DataRepository { static let shared = DataRepository()
private init() {}
private let client = NoteServiceServiceClient.init(address: "127.0.0.1:50051", secure: false)
}

List Notes

Our main screen is List Notes Screen that display list of notes in UITableView. We will use the DataRepository to get the Notes from the gRPC Server, so let’s add the listNotes function to the DataRepository that accepts a completion handler for notes and CallResult. Inside the function is invokes the client list function passing Empty Request as the parameter, in the completion handler, it provides 2 optional arguments, notes and result. The result is a CallResult object indicating whether theres is an error. For simplicity we just invoke our completion handler passing the notes array.

DataRepository.swift

class DataRepository {

    ....
func listNotes(completion: @escaping([Note]?, CallResult?) -> Void) {
_ = try? client.list(Empty(), completion: { (notes, result) in
DispatchQueue.main.async {
completion(notes?.notes, result)
}
})
}
}

Next, we will build the UI. In main.storyboard, delete the existing UIViewController the drag the UITableViewController from Object Library. Select Editor > Embed in Navigation Controller. Add 1 prototype cell with Subtitle style and set the identifier name to Cell. finally set the NavigationController as the initial ViewController.

  

  

Let’s create the NoteListViewController which is a UITableViewController subclass. We store the DataRepository and array containing the Note we will get from the server as instances variables. We create a refresh function that triggers the fetching of notes from the DataRepository. In viewDidLoad we add UIRefreshControl to the TableView with the selector of refresh so it will be invoked when user perform pull to refresh. Also we invoke refresh when the view is loaded for the first time.

NoteListViewController.swift

import UIKit
class NoteListViewController: UITableViewController {
let dataRepository = DataRepository.shared
var notes = [Note]() {
didSet {
tableView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad() refreshControl = UIRefreshControl(frame: .zero)
refreshControl?.addTarget(self, action: #selector(refresh), for: .valueChanged)
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell") refresh()
}
@objc func refresh() {
dataRepository.listNotes {[weak self] (notes, result) in
self?.refreshControl?.endRefreshing()
self?.notes = notes ?? []
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return notes.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let note = notes[indexPath.row]
cell.textLabel?.text = note.title
cell.detailTextLabel?.text = note.content
return cell
}
}

  

At last, make sure to assign the NoteListViewController as the class inside TableView in main.storyboard. Make sure the gRPC is already running locally and build the project to see list of notes fetched from the server is displaying inside the TableView.

Create New Note

Next, lets’ add insertNote method to our DataRepository. The insertNote method accepts Note as the parameter and a completion handler closure that will be invoked after the call to service is completed. Inside we invoke the client insert method passing the note, the completion handler passes 2 arguments, createdNote and CallResult. We just call our main thread passing the createdNote and result to completion closure.

We will also add custom initializer to the Note struct using Extension, so we can construct new Note object easier passing the title and content.

DataRepository.swift

class DataRepository {

    ...
func insertNote(note: Note, completion: @escaping(Note?, CallResult?) -> Void) {
_ = try? client.insert(note, completion: { (createdNote, result) in
DispatchQueue.main.async {
completion(createdNote, result)
}
})
}
}
extension Note {
init(title: String, content: String) {
self.title = title
self.content = content
}
}

To add note in our UI, we will display UIAlertController to the user when they tap on the Navigation Bar Button Item, then we provide textfields to enter the title and content inside the Note List Screen. After user fills the textfields and tap OK Button, we get the title and content String from the textfields, then create a Note object using the custom initializer we created before passing the title and content. Finally we invoke the DataRepository insert passing the Note and completion closure that will invoke refresh method to refresh the data.

NoteListViewController.swift

class NoteListViewController: UITableViewController {
....
func viewDidLoad() {
....
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(add))
....
}
@objc func add() {
let alertController = UIAlertController(title: "Add Note", message: "Fill the fields", preferredStyle: .alert)
alertController.addTextField { $0.placeholder = "Title" }
alertController.addTextField { $0.placeholder = "Content" }
alertController.addAction(UIAlertAction(title: "Save", style: .default, handler: { (_) in
let titleTextField = alertController.textFields![0]
let contentTextField = alertController.textFields![1]
guard let title = titleTextField.text, !title.isEmpty,
let content = contentTextField.text, !content.isEmpty
else {
return
}
let note = Note(title: title, content: content)
self.dataRepository.insertNote(note: note, completion: {[weak self] (createdNote, _) in
self?.refresh()
})
}))
alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(alertController, animated: true, completion: nil)
}
....
}

Build and run the project, then tap on the add button on the right navigation bar. Then fill the textfields and press ok to see the note is added to the list!.

Delete Existing Note

At last for the sake of completeness, we will add the delete existing note method to the DataRepository. The delete note method accepts noteId String as the parameter and a completion handler closure that will be invoked after the call to service is completed. We also will create custom initializer for the NoteRequestId struct that accepts id as the parameter and assign it to the id property.

Inside the delete method we invoke the client delete method passing the NoteRequestID we initialized using the id passed from the function. Inside the completion handler we check if there is no error by checking the success response is exists. Then we invoke the completion handler passing true if it is exists and false if it is not exists.

DataRepository.swift

class DataRepository {
....
func delete(noteId: String, completion: @escaping(Bool) -> ()) {
_ = try? client.delete(NoteRequestId(id: noteId), completion: { (success, result) in
DispatchQueue.main.async {
if let _ = success {
completion(true)
} else {
completion(false)
}
}
})
} }
...
extension NoteRequestId { init(id: String) {
self.id = id
}
}

Inside our Note List Screen, we will trigger the deletion when user performs swipe to delete interaction to the TableViewCell. We get note using the indexPath.row from the notes array, then invoke the dataRepository delete method passing the note id. At last, inside the completion handler we refresh the data if the completion is success.

class NoteListViewController: UITableViewController {
...
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
guard editingStyle == .delete else {
return
}
let note = notes[indexPath.row]
dataRepository.delete(noteId: note.id) {[weak self](success) in
if success {
self?.refresh()
}
}
}
}

Build and run the project, then perform swipe to delete in one of the tableview cell to trigger the deletion!.

  

Conclusion

We finally finished building our gRPC iOS Client App that has the features to display list of notes, display detail of note, create new note, and delete existing note. Using gRPC in iOS App is quite simple and easy, we don’t even have to care and implement the manual JSON decoding to Swift object. In real life scenario, there have already been many production Google iOS Client Library SDK that use gRPC and Protocol Buffer under hood to communicate between client and server. As a homework, you can add Get Note Detail gRPC method in a separate Note Detail Screen. Keep doing the best and Happy Lifelong Learning .

  

another example

https://hackernoon.com/grpc-bff-for-swift-ios-app-efdd52df7ce2  

  

Building gRPC Client iOS Swift Note Taking App的更多相关文章

  1. 用Swift语言做App开发之单元测试

    作为一个有质量保障的应用程序,当然少不了单元测试:Swift开发的App也亦如此,此文将以一个简单的实例来介绍Swift中的单元测试. 这里我们使用XCode模版自带的XCTest框架,此框架包含了一 ...

  2. gRPC Client的负载均衡器

    一.gRPC是什么? gRPC是一个高性能.通用的开源RPC框架,其由Google主要面向移动应用开发并基于HTTP/2协议标准而设计,基于ProtoBuf(Protocol Buffers)序列化协 ...

  3. iOS Swift WisdomScanKit二维码扫码SDK,自定义全屏拍照SDK,系统相册图片浏览,编辑SDK

    iOS Swift WisdomScanKit 是一款强大的集二维码扫码,自定义全屏拍照,系统相册图片编辑多选和系统相册图片浏览功能于一身的 Framework SDK [1]前言:    今天给大家 ...

  4. iOS - GitHub干货分享(APP引导页的高度集成 - DHGuidePageHUD - ②)

    距上一篇博客"APP引导页的高度集成 - DHGuidePageHUD - ①"的发布有一段时间了, 后来又在SDK中补充了一些新的内容进去但是一直没来得及跟大家分享, 今天来跟大 ...

  5. 关于IOS的证书、App ID、设备、Provisioning Profile详述

    首先,打开developer.apple.com ,在iOS Dev Center打开Certificates, Indentifiers & Profiles认识一下基本结构.列表就包含了开 ...

  6. iOS开发UI篇—APP主流UI框架结构

    iOS开发UI篇—APP主流UI框架结构 一.简单示例 说明:使用APP主流UI框架结构完成简单的界面搭建 搭建页面效果:                                二.搭建过程和 ...

  7. ele.me在IOS浏览器端启动APP的技巧分析

    ele.me在IOS浏览器端启动APP的技巧分析 巧妙利用后台重定向,在schemes启动时提示用户打开,启动不了APP时能够及时跳转至下载页面. 避免报错页面的出现以及用户还没来的及选择就跳转到下载 ...

  8. iOS swift的xcworkspace多项目管理(架构思想)

    iOS  swift的xcworkspace多项目管理(架构思想) 技术说明: 今天在这里分享 swift下的 xcworkspace多项目管理(架构思想),能为我们在开发中带来哪些便捷?能为我们对整 ...

  9. iOS 跳转到 App Store 下载评分页面

    html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,bi ...

随机推荐

  1. C#学习笔记-XML的读写(一)

    需要解析的配置XML <?xml version="1.0" encoding="utf-8" ?> <configurations> ...

  2. Sublime text3 连接sftp/ftp(远程服务器)

    1.按下Ctrl + Shift + P调出命令面板2.在输入框中输入Sftp,按回车下载3.建一个新的文件夹放到左边的项目栏中4.右击文件夹,选中SFTP/FTP,点击Map to Remote5. ...

  3. 【easy】561. Array Partition I

    Given an array of 2n integers, your task is to group these integers into n pairs of integer, say (a1 ...

  4. Mac ---- markdown 转 html\word\pdf

    在Mac上,有一个软件,叫iA writer,是一个文字编辑器,可以进行md到word的转换,但它是收费的,RMB68元. 如果只是临时用一下,不想购买,你可以使用pandoc. 在mac下,使用方法 ...

  5. linux redis 主从复制

    在从服务的redis.conf 添加 slaveof 主服务器 端口 查看reids进程和端口,都是存在的.只是ip地址是127.0.0.1而不是0.0.0.0,只是本机能使用; 查找redis的配置 ...

  6. jQuery的选择器中的通配符[id^='code']或[name^='code']及jquery选择器总结

    1.选择器 (1)通配符: $("input[id^='code']");//id属性以code开始的所有input标签 $("input[id$='code']&quo ...

  7. vue入门知识点

    最近入坑vue 做一点小的记录 有不对的 辛苦指出 会第一时间更改上新 0.利用vue-cli构建项目新建一个目标文件夹 ,全局安装vue-cli脚手架工具 (全局安装一次即可) npm instal ...

  8. Java 中的内部类

    前言 在第一次把Java 编程思想中的内部类这一章撸完后,有点印象.大概知道了什么时内部类,局部内部类,匿名内部类,嵌套内部类.随着时间的推移,自己慢慢的就忘记了,总感觉自己思考的东西不多,于是 看了 ...

  9. bzoj5397 circular 随机化(

    题目大意 给定一个环,环上有一些线段,试选出最多的线段 题解: 提醒:这可能是一篇非常欢乐的题解 我们考虑倍长环,然后断环为链 我们考虑枚举开头的线段,然后做一次贪心 这样子的复杂度根据实现的不同是\ ...

  10. 07_ for 练习 _ sumOfOdd

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...