12/08/2018, 10:50

Tìm hiểu về Watchkit: tạo table và request network

Như đã giới thiệu trong bài viết trước, watchkit là một framework mới của iOS được Apple giới thiệu cùng với sự ra mắt của xcode 6.2. Watchkit được sử dụng để lập trình viên phát triển ứng dụng trên Apple watch, chiếc đồng hồ thông minh được Apple giới thiệu trong cuối năm 2014. Trong bài viết ...

Như đã giới thiệu trong bài viết trước, watchkit là một framework mới của iOS được Apple giới thiệu cùng với sự ra mắt của xcode 6.2.

Watchkit được sử dụng để lập trình viên phát triển ứng dụng trên Apple watch, chiếc đồng hồ thông minh được Apple giới thiệu trong cuối năm 2014.

Trong bài viết trước, tôi đã giới thiệu cấu tạo của 1 ứng dụng trên Apple watch gồm 2 phần:

  • User interface resource: đây là phần app chạy trên Apple Watch. Trên Apple Watch, phần này chỉ xử lý phần giao diện và tương tác người dùng (các bạn có thể coi Apple Watch như một màn hình thứ 2 của iPhone, chỉ gồm giao diện để tương tác với người dùng, mọi xử lý logic đều được xử lý trên iPhone)

  • Watchkit extension: đây là phần app chạy ngầm(background) trên iPhone. Trên iPhone, các xử lý logic, dữ liệu, mạng, xử lý response từ người dùng,... từ Apple Watch được xử lý, trả về Apple Watch và hiển thị trên màn hình Apple Watch cho người dùng

Watchkit thật ra chỉ là một extension của iOS chứ không phải một ứng dụng thật sự. Tuy nhiên, đối với người dùng thông thường, họ không biết được rằng ứng dụng của chúng ta đang chạy trên iPhone chứ không phải trên Apple watch của họ.

Bởi vì watchkit chỉ là 1 extension của iOS, được chạy ngầm trong background của iPhone, do vậy, ứng dụng được viết bằng Watchkit của chúng ta có thể bị iOS kill nếu ứng dụng chạy trên background quá lâu, hoặc khi iOS cần tài nguyên phần cứng để chạy một ứng dụng đang chạy khác.

Trong bài viết này, tôi xin giới thiệu với các bạn cách tạo một ứng dụng đơn giản trên Apple watch sử dụng table view và giao tiếp mạng.

a. chuẩn bị

Do bài viết này chỉ tập chung vào việc làm sao để tạo table view và cách giao tiếp mạng trong Watchkit, nên tôi xin phép được sử dụng demo code hướng dấn từ http://www.raywenderlich.com, các bạn có thể vào đây để xem hướng dẫn cho app demo này từ trang gốc.

Quay lại với bước chuẩn bị, đầu tiên chúng ta download starter project tại đây.

Trong starter project, tác giả đã thêm sẵn Watchkit extension vào trong project(các bạn có thể đọc lại bài viết này của tôi nếu các bạn chưa biết cách thêm watchkit extension vào project).

Ngoài ra, tác giả cũng đã tạo CoinKit framework sử dụng để gửi các request mạng đến trang cryptocoincharts.info.

Mở starter project trong xcode, tại file CoinsInterfaceController.swift trong group CoinTracker WatchKit Extension, import CoinKit framework ở trên:

import CoinKit

thêm các property cho CoinsInterfaceController.swift

var coins = [Coin]()

let coinHelper = CoinHelper()

b. Tạo table view trên Apple Watch

Trong Watchkit, việc sử dụng table view đơn giản hơn sử dụng table view trên iOS. Bởi vì trong watchkit, chúng ta không cần quan tâm đến việc tạo delegate, datasource cho table view.

Đầu tiên, chúng ta thêm outlet sau vào CoinsInterfaceController.swift

@IBOutlet weak var coinTable: WKInterfaceTable!

Tiếp theo, mở Interface.storyboard, add thêm table object vào trong giao diện của Apple watch, Chúng ta sẽ thấy 1 table với mỗi 1 row ứng với 1 group. Nối table vừa tạo với outlet coinTable chúng ta vừa khai báo ở trên.

Screen Shot 2015-07-27 at 12.00.10 AM.png

Kế đó, thêm 2 label vào trong group, đặt tên lần lượt cho các label là Coin name và price, để layout trong group là vertical, để size là size to fit content và sắp xếp các label như hình sau:

Screen Shot 2015-07-27 at 12.06.53 AM.png

Sau đó, chúng ta tạo thêm file CoinRow.swift trong group CoinTracker WatchKit Extension: chọn New FileiOSSourceSwift đặt tên là CoinRow và tạo file mới.

Thêm đoạn code sau vào file CoinRow.swift vừa tạo:

import WatchKit
class CoinRow: NSObject {
	@IBOutlet weak var titleLabel: WKInterfaceLabel!
	@IBOutlet weak var detailLabel: WKInterfaceLabel!
}

Không giống như table view trên iOS với các cột thường được subclass từ UITableViewCell, trên Apple watch, mỗi cột trong trường hợp này là subclass của NSObject class.

Tiếp theo, vẫn trong Interface.storyboard, chúng ta đổi class của Table Row Controller thành CoinRow.swift, set identifier cho Table Row Controller là CoinRow. Rồi lần lượt nối các outlet titleLabel, detailLabel trong file CoinRow.swift với các label Coin name và price trong Interface.storyboard.

Mở file CoinsInterfaceController.swift và thêm function sau:

func reloadTable() {
	// 1
	coinTable.setNumberOfRows(10, withRowType: "CoinRow")
	for (index, coin) in enumerate(coins) {
        // 2
		if let row = coinTable.rowControllerAtIndex(index) as? CoinRow {
        	// 3
			row.titleLabel.setText(coin.name)
			row.detailLabel.setText("(coin.price)")
		}
	}
}

Trong đoạn code trên:

  • 1: hàm setNumberOfRows set số cột của table là 10 với identifier là "CoinRow" (chúng ta đã set identier cho Table Row Controller ở trên)
  • 2: hàm rowControllerAtIndex lấy ra cột của table tại vị trí index
  • 3: gán titleLabel và detailLabel cho cột

Tiếp theo, thêm đoạn code sau vào cuối hàm awakeWithContext:

coins = coinHelper.cachedPrices()
reloadTable()

chạy thử app, chúng ta sẽ có 1 table view với 10 cột như hình sau:

Screen Shot 2015-07-27 at 12.52.44 AM.png

vậy là chúng ta đã tạo được table view trên apple watch. bước tiếp theo, chúng ta sẽ cho Watchkit extension tương tác với app chính trên iPhone để gửi và nhận request.

c. Tương tác giữa iOS app và Watchkit

Từ Watchkit tới iOS

Đầu tiên, trong hàm reloadTable() của CoinsInterfaceController.swift, thay đoạn code sau:

coinTable.setNumberOfRows(10, withRowType: "CoinRow")

thành đoạn code sau:

if coinTable.numberOfRows != coins.count {
	coinTable.setNumberOfRows(coins.count, withRowType: "CoinRow")
}

Thêm đoạn code sau vào cuối hàm awakeWithContext của CoinsInterfaceController.swift:

WKInterfaceController.openParentApplication(["request": "refreshData"],
 reply: { (replyInfo, error) -> Void in
  // TODO: process reply data
  NSLog("Reply: (replyInfo)")
})

Trong đoạn code trên, hàm openParentApplication là 1 class method của WKInterfaceController, hàm này được sử dụng để Watchkit extension gửi thông báo đến iPhone, yêu cầu iPhone khởi chạy app chính. Param đầu, dictionary ["request": "refreshData"] là biến truyền đến ứng dụng chính trên iOS, closure ở trên được gọi khi app chính đã khởi động xong.

Từ iOS tới Watchkit

Trong group CoinTracker, mở file AppDelegate.swift lên và thêm hàm sau:

func application(application: UIApplication,
     handleWatchKitExtensionRequest userInfo: [NSObject : AnyObject]?,
     reply: (([NSObject : AnyObject]!) -> Void)!) {

	// 1
	if let userInfo = userInfo, request = userInfo["request"] as? String {
		if request == "refreshData" {
			// 2
			let coinHelper = CoinHelper()
			let coins = coinHelper.requestPriceSynchronous()

			// 3
			reply(["coinData": NSKeyedArchiver.archivedDataWithRootObject(coins)])
			return
		}
	}

	// 4
	reply([:])
}

Trong đoạn code trên:

    1. Ứng dụng trên iOS kiểm tra param truyền tới từ Watchkit extension
    1. Ứng dụng trên iOS gửi request lên server để lấy về dữ liệu
    1. Sau khi dữ liệu được lấy về, ứng dụng chính trên iOS sẽ trả về cho watchkit thông qua closure reply
    1. Trường hợp request lên server có lỗi, ứng dụng sẽ trả về cho Apple watch rỗng

Chúng ta cùng chạy thử project, nếu không có lỗi gì sảy ra, thì khi nhìn vào log trong console, ứng dụng của chúng ta sẽ show log kiểu như hình sau:

Screen Shot 2015-07-27 at 1.14.33 AM.png

Dữ liệu được trả về ở đây là dạng NSData. Vậy là qua bước này, ứng dụng của chúng ta đã có thể gửi thông báo từ Watchkit extension đến iOS, để iOS mở ứng dụng chính, request download dữ liệu, và trả ngược lại dữ liệu về extension.

Hiển thị dữ liệu trả về trên table view

Quay lại với file CoinsInterfaceController.swift, trong hàm awakeWithContext, chúng ta thay đoạn NSLog() để show log từ closure trả về bằng đoạn code sau:

if let coinData = replyInfo["coinData"] as? NSData {
	if let coins = NSKeyedUnarchiver.unarchiveObjectWithData(coinData) as? [Coin] {
		self.coinHelper.cachePriceData(coins)
		self.coins = coins
		self.reloadTable()
	}
}

Chạy lại ứng dụng, chúng ta sẽ có được kết quả trên Apple watch như hình sau: Screen Shot 2015-07-27 at 1.26.44 AM.png

Vậy là dữ liệu của chúng ta cuối cùng đã có thể truyền về Apple watch và hiển thị trên table view của Apple watch. Ứng dụng demo của tôi xin dừng lại ở đây

Qua bài viết này, tôi đã giới thiệu đến các bạn một ít thông tin về Watchkit, một framework của Apple để lập trình viên tạo ứng dụng trên Apple watch.

Đồng thời, tôi cũng đã giới thiệu đến các bạn một ứng dụng demo, trong ứng dụng đó, chúng ta đã tạo một table view trên Apple watch, viết hàm để Watchkit extension truyền thông điệp tới iOS mở ứng dụng chính, tạo request để lấy dữ liệu thông qua API từ server và trả về cho watchkit exentsion xử lý, hiển thị dữ liệu trên Watchkit app.

Nguyên nhân của việc gửi thông điệp gọi mở ứng dụng chính trên iOS, như tôi đã nói ở trên, do app extension chỉ được chạy trên background theard, không đảm bảo khi chạy các tác vụ nặng hay mất nhiều thời gian.

Cuối cùng, người viết xin cảm ơn các bạn đã theo dõi bài viết này. Ứng dụng demo này của tôi chỉ nghiên cứu về việc tạo table và gửi request qua Watchkit, Các bạn có thể tham khảo tutorial gốc thông qua link sau.

Xin cảm ơn !!!

0