- vừa được xem lúc

Build gRPC client iOS đơn giản

0 0 24

Người đăng: Thanh Nguyen

Theo Viblo Asia

Introduction

Trong bài viết trước, chúng ta đã cùng nhau tìm hiểu về gRPC và cách để build một gRPC server bằng node.js với các chức năng CRUD đơn giản:

https://viblo.asia/p/build-crud-server-don-gian-voi-grpc-va-nodejs-maGK70rxZj2

Trong bài viết này, chúng ta sẽ tiếp tục build một gRPC client app bằng iOS để kết nối đến server gRPC local ở bài viết trên. Source code của gRPC server có thể download tại:

https://github.com/oNguyenXuanThanh/crud-grpc-nodejs

Setup and run gRPC server locally

Đầu tiên, chúng ta cần deploy gRPC server ở local. Mở terminal lên, cd đến thư mục chứa source code và chạy lệnh node index.js:

cd /Users/thanhfnx/Desktop/StudyReport/gRPC-Server
node index.js Server running at http://127.0.0.1:50051
(node:3296) DeprecationWarning: grpc.load: Use the @grpc/proto-loader module with grpc.loadPackageDefinition instead
(Use `node --trace-deprecation ...` to show where the warning was created)

Setup Xcode project and install Cocoapod dependencies

Sau khi run được server gRPC, bước tiếp theo, chúng ta sẽ bắt đầu xây dựng gRPC client iOS. Mở Xcode lên và tạo mới một project tên là gRPC Client như sau:

Tiếp theo, mở tab mới trên Terminal, cd đến thư mục của project iOS vừa tạo và chạy lệnh pod init.

File Podfile sẽ được tự động sinh ra, dùng một text editor nào đó mở file này lên và thêm pod SwiftGRPC:

# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0' target 'gRPC Client' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! # Pods for gRPC Client pod 'SwiftGRPC' end

Sau đó, chạy lệnh pod install để install các Cocoapod dependency.

pod install
Analyzing dependencies
Downloading dependencies
Installing BoringSSL-GRPC (0.0.4)
Installing SwiftGRPC (0.11.0)
Installing SwiftProtobuf (1.7.0)
Installing gRPC-Core (1.24.2)
Generating Pods project
Integrating client project
Pod installation complete! There is 1 dependency from the Podfile and 4 total pods installed.

Sau khi pod install xong, mở project iOS vừa tạo bằng file .xcworkspace. Bởi vì server gRPC của chúng ta chạy ở localhost nên cần phải thay đổi setting, cho phép truy cập HTTP bằng cách thêm key-value sau và file Info.plist.

	<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict>

Compile proto file

Đến với bước tiếp theo, chúng ta cần compile file pets.proto ở thư mục gRPC server sang file Swift, sử dụng Protocol Buffer và Swift Protobuf Compiler.

Đầu tiên, hãy tải về và cài đặt Proto Buffer Compiler chính thức của Google bằng cách gõ lệnh sau trong Terminal:

brew install grpc-swift

Tiếp theo, chúng ta cần tải và cài đặt Swift Protobuf Compiler.

brew install swift-protobuf

Sau khi cài đặt xong, chạy lệnh sau để bắt đầu compile:

protoc pets.proto \ --swift_out=. \ --plugin=./.build/debug/protoc-gen-grpc-swift \ --swiftgrpc_out=Client=true,Server=false:.

Kết quả là chúng ta sẽ được 2 file pets.grpc.swiftpets.pb.swift. Kéo thả 2 file này vào thư mục của project iOS trong Xcode và đảm bảo tích option Copy Items if needed.

Swift data repository for gRPC service

Tiếp tục, chúng ta sẽ tạo một class mới có tên là DataRepository, sử dụng một instance singletion để thực hiện CRUD qua gRPC method.

Tạo mới Swift file DataRepository và thêm đoạn code sau:

import Foundation
import SwiftGRPC class DataRepository { // Singletion instance static let shared = DataRepository() // Client instance với address của gRPC local server private let client = PetServiceServiceClient(address: "127.0.0.1:50051", secure: false) private init() {}
}

List pets

Để call gRPC method get list, trong DataRepository, tạo một method mới như sau:

 func getPets(completion: @escaping ([Pet]?, CallResult?) -> Void) { // Vì gRPC get list pet không cần parameter đầu vào nên chỉ cần khời tạo // Empty message let emptyParameter = Empty() // Call gRPC method và trả về list pet trên main thread _ = try? client.list(emptyParameter, completion: { petList, result in DispatchQueue.main.async { completion(petList?.pets, result) } }) }

Để test thử, đơn giản nhất, trong Main.storyboard, tạo mới button Get list và set method handle touch up inside như sau:

 @IBAction private func getListButtonTapped(_ sender: Any) { DataRepository.shared.getPets { pets, result in if let result = result { print("Call result: \(result)") } print("Fetched pets: \(pets ?? [])") } }

Kết quả thu được:

Call result: successful, status ok: OK
resultData: 55 bytes
initialMetadata: [:]
trailingMetadata: [:]
Fetched pets: [gRPC_Client.Pet:
id: "1"
name: "Alaska"
description: "Description 1"
, gRPC_Client.Pet:
id: "2"
name: "Husky"
description: "Description 2"
]

Create new pet

Trước tiên, tạo mới extension cho struct Pet, thêm method init từ các kiểu dữ liệu đơn giản:

extension Pet { init(name: String, description: String) { self.name = name self.description_p = description }
}

Trong DataRepository, implement method addPet:

 func addPet(_ pet: Pet, completion: @escaping (Pet?, CallResult?) -> Void) { _ = try? client.insert(pet, completion: { createdPet, result in DispatchQueue.main.async { completion(createdPet, result) } }) }

Và cuối cùng, thêm button Add new với handler như sau:

 @IBAction private func addNewButtonTapped(_ sender: Any) { let alertController = UIAlertController(title: "Add new pet", message: nil, preferredStyle: .alert) alertController.addTextField(configurationHandler: { $0.placeholder = "Pet name" }) alertController.addTextField(configurationHandler: { $0.placeholder = "Pet description" }) alertController.addAction(UIAlertAction(title: "Save", style: .default, handler: { _ in let nameTextField = alertController.textFields![0] let descriptionTextField = alertController.textFields![0] guard let name = nameTextField.text, !name.isEmpty, let description = descriptionTextField.text, !description.isEmpty else { return } let newPet = Pet(name: name, description: description) DataRepository.shared.addPet(newPet) { insertedPet, result in if let result = result { print("Call result: \(result)") } else { print("Call result: nil") } if let insertedPet = insertedPet { print("Inserted pet: \(insertedPet)") } else { print("Inserted pet: nil") } } })) alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) present(alertController, animated: true, completion: nil) }

Kết quả sau khi thêm mới thành công:

Call result: successful, status ok: OK
resultData: 60 bytes
initialMetadata: [:]
trailingMetadata: [:]
Inserted pet: gRPC_Client.Pet:
id: "d919ffa0-6a5b-11eb-8a35-ac3965b004a8"
name: "Chihuahua"
description: "Chihuahua"

Delete existing pet

Cuối cùng, method để xóa một record pet đã tồn tại được implement như sau:

extension PetRequestId { init(id: String) { self.id = id }
}
 func delete(petId: String, completion: @escaping (Bool) -> Void) { _ = try? client.delete(PetRequestId(id: petId), completion: { pet, result in DispatchQueue.main.async { completion(pet != nil) } }) }

Handler cho button delete:

 @IBAction private func deleteButtonTapped(_ sender: Any) { let alertController = UIAlertController(title: "Delete pet by ID", message: nil, preferredStyle: .alert) alertController.addTextField(configurationHandler: { $0.placeholder = "Pet id" }) alertController.addAction(UIAlertAction(title: "Delete", style: .default, handler: { _ in let petIdTextField = alertController.textFields![0] guard let petId = petIdTextField.text, !petId.isEmpty else { return } DataRepository.shared.delete(petId: petId) { success in print(success ? "Delete successfully" : "Delete failed") } })) alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) present(alertController, animated: true, completion: nil) }

Conclusion

Trên đây chỉ là một ví dụ đơn giản cho việc implement gRPC client iOS dưới dạng in ra màn hình console kết quả. Trong thực tế chúng ta cần phải kết hợp với xây dựng UI/UX cụ thể hơn để có thể vận dụng nhưng ưu điểm của gRPC vào một project hoàn chỉnh.

Final project: https://github.com/oNguyenXuanThanh/crud-grpc-ios

Source article: https://www.alfianlosari.com/posts/building-grpc-client-swift-note-taking-app/

Bình luận

Bài viết tương tự

- vừa được xem lúc

Học Flutter từ cơ bản đến nâng cao. Phần 1: Làm quen cô nàng Flutter

Lời mở đầu. Gần đây, Flutter nổi lên và được Google PR như một xu thế của lập trình di động vậy.

0 0 254

- vừa được xem lúc

Học Flutter từ cơ bản đến nâng cao. Phần 3: Lột trần cô nàng Flutter, BuildContext là gì?

Lời mở đầu. Màn làm quen cô nàng FLutter ở Phần 1 đã gieo rắc vào đầu chúng ta quá nhiều điều bí ẩn về nàng Flutter.

0 0 189

- vừa được xem lúc

Swift: Tạo custom phép toán tử (Operator) của riêng bạn!

Swift cho phép bạn tạo các toán tử có thể tùy chỉnh của riêng bạn. Điều này đặc biệt hữu ích khi bạn xử lý các loại dữ liệu của riêng mình. Operator Types in Swift. Có năm loại toán tử chính trong Swift.

0 0 45

- vừa được xem lúc

Chương 6 Protocol oriented programming.

Cuốn sách này là về lập trình hướng protocol. Khi Apple thông báo swift 2 ở WWDC 2015.

0 0 28

- vừa được xem lúc

Ví dụ về UIActivityViewController

Trên iOS, UIActivityViewController cung cấp giao diện thống nhất để người dùng chia sẻ và thực hiện các hành động trên văn bản, hình ảnh, URL và các mục khác trong ứng dụng. let string = "Hello, world!". let url = URL(string: "https://nshipster.com").

0 0 45

- vừa được xem lúc

Quản lý self và cancellable trong Combine.

. . Công việc quản lý bộ nhớ memory management thường trở nên phức tạp khi chúng ta thực hiện các tác vụ bất đồng bộ asynchronous vì chúng ta thường phải lưu giữ một số object nằm ngoài scope mà object được define trong khi vẫn phải đảm bảo được việc giải phóng object đó được thực hiện đúng quy trìn

0 0 28