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. tp5入门

    runtime目录里的文件是临时文件,可随时删除 在tp5里,命名空间对应了文件的所在目录,app命名空间通常代表了文件的起始目录为application,而think命名空间则代表了文件的起始目录为 ...

  2. torch.utils.data.DataLoader对象中的迭代操作

    关于迭代器等概念参考:https://www.cnblogs.com/zf-blog/p/10613533.html 关于pytorch中的DataLoader类参考:https://blog.csd ...

  3. WPF 窗口去除顶部边框(正宗无边框)

    最近在做一个大屏展示视频图片的项目,功能并不复杂,半天的工作量吧,一开始同事采用的Unity3D进行开发,但是里面要播放4K视频,Unity 的短板就是视频的播放了,今晚就要交付了,我一早就来公司,决 ...

  4. 递归遍历所有xml的节点及子节点

    import java.io.File; import java.util.List; import org.dom4j.Attribute; import org.dom4j.Document; i ...

  5. 此主机支持Intel VT-x,但Intel VT-x处于禁用状态

    原因:未开启虚拟化技术 解决方法:https://www.cnblogs.com/jiefu/p/10711955.html

  6. 爬虫-requests

    一.爬虫系列之第1章-requests模块 爬虫简介 概述 近年来,随着网络应用的逐渐扩展和深入,如何高效的获取网上数据成为了无数公司和个人的追求,在大数据时代,谁掌握了更多的数据,谁就可以获得更高的 ...

  7. Javascript我学之二函数定义

    本文是金旭亮老师网易云课堂的课程笔记,记录下来,以供备忘 函数 几个要点:                 a).函数是javascript中的一等公民 (重要性)                 b ...

  8. Kubernetes 学习1 k8s架构概述

    一.概述 1.意思:舵手,飞行员 2.特点 a.自动装箱,自我修复,水平扩展,服务发现和负载均衡,自动发布和回滚. b.密钥和配置管理,存储编排,批量处理执行. 二.架构术语 1.集群 master( ...

  9. 2018-2019-1 20189201 《LInux内核原理与分析》第八周作业

    只有在天足够黑的时候你才能看到星星. BY WAY GK 加油 一.书本第七章知识总结[可执行程序工作原理] 1. ELF目标文件格式 ELF全称Executable and Linkable For ...

  10. Vue H5 项目模板

    使用了 mint-ui sass vue fastclick vue router 一个项目的初始化状态,一个新项目,陆陆续续花了2天时间搭起来的. 里面有mint-ui的基本用法 tabbar 还有 ...