Tạo SiriKit Extension đơn giản trên iOS 10

Introduction.

Siri - trợ lý ảo nổi tiếng do Apple phát triển và được giới thiệu lần đầu vào năm 2011. Tuy nhiên đến tận WWDC 2016, Apple mới cho phép các developer tích hợp Siri vào các app tự phát triển thông qua SiriKit. Trong bài viết này, chúng ta sẽ tìm hiểu cách tạo và tích hợp một SiriKit extension đơn giản trong iOS 10.

Supported Domains.

SiriKit hỗ trợ một số domain nhất định, một domain thể hiện một nhóm các chức năng cụ thể. Mỗi domain lại bao gồm nhiều intent, mỗi intent đảm nhiệm một chức năng tương ứng mà người dùng có thể tương tác được với Siri. Hiện tại SiriKit hỗ trợ 8 domain:

  • List.
  • QR Codes.
  • Ride Booking.
  • Messaging.
  • Photo Search.
  • Payments.
  • VoIP Calling.
  • Workouts.
  • Climate and radio.

Nếu chức năng bạn muốn làm không nằm trong các domain trên thì cũng đừng vội từ bỏ. Vì SiriKit rất "powerful" và Apple sẽ tiếp tục phát triển và hỗ trợ thêm nhiều chức năng mới trong tương lai.

SiriKit Extension Architecture.

Một SiriKit extension được cấu tạo từ 2 loại extension: Intents (required) và IntentsUI (optional).

  • Intents extension có nhiệm vụ xử lý, thực hiện các task, các request cụ thể cho người dùng. Ví dụ: bắt đầu một cuộc gọi VoIP, gửi một tin nhắn, gửi yêu cầu thanh toán...
  • IntentsUI là extension optional, không bắt buộc phải có. Chỉ cần khi muốn customize giao diện khi tương tác với Siri, còn không thì giao diện mặc định của Siri sẽ được sử dụng.

Example Project.

Trong project này, ta sẽ build một app thanh toán cực đơn giản bằng giọng nói với Siri.

Initial Setup.

  1. Đăng nhập vào trang Apple developer và setup App ID Wildcard, enable SiriKit, tạo provisioning profile mới...
  2. Mở Xcode lên, tạo mới một project mới và đặt tên là SiriPayment với các thứ đã tạo ở trên.
  3. Enable Siri capability bằng cách vào Target > Capabilities.
  4. Trong Info.plist, thêm key NSSiriUsageDescription với value là string, mô tả mục đích sử dụng Siri khi xin quyền người dùng.
  5. Chọn File > New > Target. Trong cửa sổ Application Extensions, chọn Intents Extension. Bạn có thể chọn thêm Include UI Extension nếu muốn customize giao diện Siri sau này. Mở file Info.plist của target Intents mới tạo lên. Trong dictionary NSExtension thể hiện loại intent mà extension hiện tại sẽ hỗ trợ và loại intent mà bạn không muốn cho người dùng gọi sử dụng khi iPhone đang ở trạng thái khoá màn hình. Ở đây, chúng ta đang làm chức năng thanh toán bằng Siri nên cài đặt INSendPaymentIntent vào IntentsRestrictedWhileLockedIntentsSupported.

Việc set IntentsRestrictedWhileLockedINSendPaymentIntent sẽ yêu cầu thiết bị phải được mở khoá mới có thể sử dụng app để thanh toán qua Siri.

iOS Target.

Để sử dụng Siri, ta phải hỏi người dùng để xin quyền sử dụng sử dụng Siri. Cụ thể là gửi dữ liệu giọng nói đến Apple để phân tích. Trong ViewController, ta chỉ cần import Intents và sử dụng đoạn code sau:

import UIKit
import Intents

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        INPreferences.requestSiriAuthorization { authorizationStatus in
            switch authorizationStatus {
            case .authorized:
                print("Authorized")
            default:
                print("Not Authorized")
            }
        }
    }

}

Trên iPhone, hãy enable Siri lên. Kết quả là khi lần đầu chạy, app sẽ hiển thị dialog xin quyền sử dụng Siri.

Intents Extension.

Quay lại phần Intents extension đã tạo trước đó, ta thấy có một file tên là IntentHandler.swift Chúng ta sẽ sử dụng file này để xử lý các intents nhận được từ Siri. Cụ thể Siri sẽ forward tất cả các intent nhận được đến method handler(for :) kể cả trong trường hợp extension hỗ trợ nhiều domain. Và việc của developer là phân loại, xác định các kiểu intent của class INIntent và xử lý logic cho phù hợp. File IntentHandler.swift mặc định đã chứa các đoạn code example cài đựat việc thực hiện một Messaging intent. Xoá toàn bộ code example mẫu đi ta được.

class IntentHandler: INExtension {
      
    override func handler(for intent: INIntent) -> Any? {
        // This is the default implementation.  If you want different objects to handle different intents,
        // you can override this and return the handler you want for that particular intent.         
        return self
    }
    
}

Mỗi intent sẽ có một associated protocol để đảm bảo các lớp kế thừa phải implement toàn bộ các required method của nó. Hầu hết các protocol trong framework Intents đều có cấu trúc giống nhau. Ở đây, chúng ta sẽ implement protocol INSendPaymentIntentHandling. Protocol này chứa các required method và optional method sau:

  • Required: handle(sendPayment:completion:)
  • Optional: confirm(sendPayment:completion:) resolvePayee(forSendPayment:with:) resolveCurrencyAmount(forSendPayment:with:) resolveNote(forSendPayment:with:) Trong file IntentHandler.swift, tạo một extension cho class IntentHandler để cài đặt required method handle(sendPayment:completion).
extension IntentHandler: INSendPaymentIntentHandling {

    func handle(sendPayment intent: INSendPaymentIntent, completion: @escaping (INSendPaymentIntentResponse) -> Void) {
        // Kiểm tra các giá trị người nhận tiền và số tiền đã valid hay chưa.
        // Nếu chưa thì trả về INSendPaymentIntentResponse với code .unspecified qua completion
        guard let payee = intent.payee, let amount = intent.currencyAmount else {
            return completion(INSendPaymentIntentResponse(code: .unspecified, userActivity: nil))
        }
        // Thực hiện logic thanh toán
        print("Sending \(amount) payment to \(payee)!")
        completion(INSendPaymentIntentResponse(code: .success, userActivity: nil))
    }

}

Đoạn code trên chỉ mô phỏng logic xử lý thanh toán demo rất đơn giản. Chỉ kiểm tra payeecurrencyAmount có valid hay không rồi thực hiện thanh toàn bằng việc print log.

Test Extension.

Chọn Intents scheme và chạy thử. Khi Xcode hiển thị menu chọn app để run thì chọn app SiriPayment mà ta đang thử. Giao diện Siri hiện lên trên iPhone, hãy cố gắng nói câu "Send $10 to Andy via SiriPayment". Nếu không tự tin với khả năng nói tiếng Anh hoặc Siri không nhận diện được câu bạn nới thì có thể dùng chức năng tap to edit của Siri cũng được. Trường hợp cả payeecurrencyAmount đều valid. Lúc này trên màn hình log của Xcode sẽ hiện chi tiết Payment Intent nhận được như sau:

Sending <INCurrencyAmount: 0x100b2f7c0> {
    amount = 10;
    currencyCode = USD;
} payment to <INPerson: 0x100b35360> {
    contactIdentifier = "<null>";
    customIdentifier = "<null>";
    displayName = Andy;
    image = "<null>";
    nameComponents = "<NSPersonNameComponents: 0x100b2dc40> {givenName = ANDY, familyName = (null), middleName = (null), namePrefix = (null), nameSuffix = (null), nickname = (null) phoneticRepresentation = (null) }";
    personHandle = "<null>";
    relationship = "<null>";
    siriMatches = "<null>";
}!

Bạn cũng có thể test trường hợp invalid để method handle(sendPayment:completion) đã cài đặt ở trên rơi vào fail case. Lúc này kết quả log Xcode sẽ là:

Sending <INCurrencyAmount: 0x100b33f70> {
    amount = 1000;
    currencyCode = USD;
} payment to <INPerson: 0x100b2d6e0> {
    contactIdentifier = "<null>";
    customIdentifier = "<null>";
    displayName = "<null>";
    image = "<null>";
    nameComponents = "<null>";
    personHandle = "<null>";
    relationship = aunt;
    siriMatches = "<null>";
}!

Conclusion.

Kết lại, khả năng tích hợp Siri với các iOS app là rất đa dạng và Siri rất thú vị. Trên đây chỉ là một ví dụ demo cực kỳ đơn giản cho việc tạo và test Siri extension. Còn rất nhiều thứ hay ho về Siri mà bạn có thể nghiên cứu, mày mò đấy.