Sử dụng JavaScript trong iOS project
Bài đăng này đã không được cập nhật trong 3 năm
I. Giới thiệu
Thông thường, JavaScript được các lập trình viên sử dụng nhiều trong phát triển web, đây là ngôn ngữ được sử dụng rất nhiều trong các trang web mà gần như trang web nào cũng dùng đến. Tuy nhiên, trong lập trình mobile (và cụ thể hơn là lập trình iOS), JavaScript hầu như không được sử dụng. Đa phần các lập trình viên iOS chưa bao giờ sử dụng JavaScript trong ứng dụng của họ.
Tại sao lại sử dụng JavaScript làm gì? tôi chỉ cần code bằng Swift thôi, thêm code JavaScript vào làm gì cho rắc rối? Đúng là việc sử dụng JavaScript trong iOS project không thể nào đơn giản như viết code Swift(hoặc Objective-C) trực tiếp. Tuy nhiên trong một số trường hợp, sử dụng JavaScript cũng là một cách hay, như một số trường hợp sau:
- Bạn đã từng code web, đã từng quen thuộc với code JavaScript, hay thậm chí bạn đang code cả mobile và web, và bạn muốn code JavaScript trong iOS project
- Bạn có 1 vấn đề, và bạn đã tìm được một thư viện có thể giải quyết vấn đề của bạn, nhưng đen là thư viện này lại được viết bằng JavaScript, không lẽ giờ lại ngồi đọc source code, rồi code lại nguyên cả thư viện bằng Swift. Mà chắc gì code mình code lại đã chạy ngon mà code
- Bạn muốn app của bạn có thể điều khiển app từ server bằng cách thêm code vào app. Thông thường, khi chúng ta build app, tất cả code của chúng ta sẽ được biên soạn để tạo nên ứng dụng, 1 file ipa và chúng ta không thể sửa code. Để điều khiển app từ server, chúng ta phải code sẵn các đoạn code đại loại như biến a nhận giá trị true/false từ server, nếu true thực hiện đoạn A, nếu false thì không thực hiện. Sử dụng JavaScript chúng ta có thể thêm luôn cả đoạn code chúng ta muốn.
- Đơn giản là bạn tò mò, muốn tìm hiểu, muốn sử dụng JavaScript cho biết
Trong bài này, tôi sẽ giới thiệu đến các bạn về cách sử dụng JavaScript trong project iOS bằng project demo đơn giản.
II. Sử dụng JavaScript trong iOS Project
1. Tạo demo project
Đầu tiên, chúng ta mở Xcode, tạo một project, đặt tên, để ngôn ngữ lập trình là Swift và bấm create để tạo project. Tiếp theo, chúng ta cần tạo 1 file để chứa code JavaScript. Các bạn chọn File -> New -> File -> Empty, đặt tên file jssource.js. Sau khi tạo file jssource.js, project navigator của chúng ta sẽ được như sau:
2. Sử dụng JavaScript code trong iOS
a. Sử dụng biến của JavaScript trong Swift
Trong bài viết này, chúng ta sẽ chỉ tìm hiểu cách sử dụng JavaScript trong iOS, nên sample code đều sẽ rất đơn giản. Đầu tiên, chúng ta mở file jssource.js, thêm đoạn code thần thánh sau:
var helloWorld = "Hello World!";
Trong file .js trên, chúng ta khởi tạo một biến dạng string, với giá trị là "Hello World!". Nhiệm vụ của chúng ta bây giờ là sử dụng code Swift để lấy được biến này ra.
Mở file ViewController.swift, import JavaScriptCore framework như sau:
import JavaScriptCore
JavaScriptCore là framework được thêm vào từ phiên bản iOS 7, giúp chúng ta làm việc với JavaScript code.
Tiếp theo, trong class ViewController, chúng ta thêm property sau:
var jsContext: JSContext!
JSContext là class quan trọng nhất trong JavaScriptCore, lớp này là cầu nối tới môi trường JavaScript, giúp chúng ta có thể chạy native JavaScript.
Tiếp theo, chúng ta thêm hàm sau vào class ViewController:
func initializeJS() {
self.jsContext = JSContext()
// 1
if let jsSourcePath = Bundle.main.path(forResource: "jssource", ofType: "js") {
do {
// 2
let jsSourceContents = try String(contentsOfFile: jsSourcePath)
// 3
self.jsContext.evaluateScript(jsSourceContents)
}
catch {
print(error.localizedDescription)
}
}
}
Trong đoạn code trên:
- 1: lấy đường dẫn đến file jssource.js, để chúng ta có thể lấy nội dung của file này
- 2: load nội dung của jssource.js vào string jsSourceContents
- 3: hàm evaluateScript thêm nội dung code JavaScript từ jsSourceContents vào JavaScript runtime
Vậy là bây giờ chúng ta đã load được nội dung JavaScript từ file jssource.js vào JavaScript runtime, code trong file jssource.js đã có thể chạy. Bước tiếp theo chúng ta sẽ lấy biến helloWorld chúng ta đã tạo bên trên, để lấy ra giá trị của nó. Các bạn thêm hàm sau vào class ViewController:
func helloWorld() {
// 1
if let variableHelloWorld = self.jsContext.objectForKeyedSubscript("helloWorld") {
print(variableHelloWorld.toString())
}
}
Như các bạn đã thấy bên trên, để lấy biến từ JavaScript, chúng ta sử dụng hàm objectForKeyedSubscript() của jsContext instance. Tuy nhiên, giá trị trả về của hàm objectForKeyedSubscript() là dạng JSValue, vì thế chúng ta sử dụng hàm toString() để convert variableHelloWorld từ JSValue về String.
Trong đoạn code trên, các bạn cần chú ý 2 điều sau:
- 1: các biến(và cả hàm) trong JavaScript được gọi trong Swift dưới dạng string (cụ thể trong ví dụ bên trên là "helloWorld"), vì thế chúng ta sẽ rất dễ bị viết sai tên biến, và khi chúng ta viết sai thì compile cũng không thể hỗ trợ chúng ta fix bug trong compile time, bug chỉ sảy ra trong quá trình run time.
- 2: Trong ví dụ bên trên, biến helloWorld trong file JavaScript được khai báo dạng String, nên chúng ta sử dụng hàm toString() để convert. Ngoài hàm toString(), lớp JSValue còn rất nhiều hàm khác để convert loại dữ liệu khác như toNumber(), toDate(), toInt32(), toDouble(),...
Bước tiếp theo, chúng ta viết code sau để gọi 2 hàm chúng ta đã viết bên trên:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
initializeJS()
helloWorld()
}
Build chạy thử project, chúng ta sẽ nhận được kết quả ở màn console log như sau
b. Sử dụng hàm của JavaScript trong Swift
Bên trên, chúng ta đã lấy được biến của JavaScript để sử dụng, tuy nhiên thông thường lấy các biến từ JavaScript không có nhiều ý nghĩa lắm. Bây giờ chúng ta sẽ thực hiện việc có nhiều ý nghĩa hơn: lấy hàm của JavaScript.
Trước tiên, chúng ta mở file jssource.js lên, và thêm hàm sau vào file này:
function biggerNumber(firstNumber, secondNumber) {
if (firstNumber >= secondNumber) {
return firstNumber;
} else {
return secondNumber;
}
}
Hàm bên trên khá là đơn giản với đầu vào là 2 số, và so sánh để trả về số lớn hơn. Tiếp theo, chúng ta quay lại hàm ViewController và thêm hàm sau:
func biggerNumber(firstNumber: Int, SecondNumber: Int) {
// 1
if let functionBiggerNumber = self.jsContext.objectForKeyedSubscript("biggerNumber") {
// 2
if let biggerNumber = functionBiggerNumber.call(withArguments: [firstNumber, SecondNumber]) {
//3
print("bigger number: \(biggerNumber.toInt32())")
}
}
}
Trong đoạn code bên trên:
- 1: Tương tự việc lấy biến từ JavaScript, chúng ta cũng sử dụng hàm objectForKeyedSubscript() để lấy hàm trong JavaScript ra.
- 2: Sau khi lấy được hàm biggerNumber() từ JavaScript, chúng ta sử dụng hàm call(withArguments: ) để gọi hàm này. các tham số của hàm biggerNumber() trong JavaScript sẽ được truyền vào thông qua array được truyền vào tham số withArguments. Ở đây chúng ta truyền vào 2 số Int vì trong file jssource.js, hàm biggerNumber() có tham số đầu vào là 2 số Int.
- 3: Giá trị trả về của hàm biggerNumber() là dạng Int, vì thế chúng ta sử dụng hàm toInt32() để lấy ra kết quả trả về từ hàm biggerNumber()
Bây giờ chúng ta cần gọi hàm biggerNumber(firstNumber: SecondNumber: ) bằng cách viết thêm hàm viewDidAppear(_ animated: ) như sau:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
initializeJS()
helloWorld()
biggerNumber(firstNumber: 3, SecondNumber: 6)
}
Build chạy project, chúng ta sẽ có kết quả output từ console log như hình sau:
Vậy là chúng ta đã có thể sử dụng biến và hàm của JavaScript để sử dụng trong Swift. Trong phần tiếp theo, tôi sẽ giới thiệu cách dùng Swift trong JavaScript.
3. Sử dụng Swift code trong JavaScript
Bây giờ, chúng ta xét trường hợp, chúng ta có gọi 1 hàm JavaScript từ trong Swift, và trong hàm của JavaScript này, chúng ta cần gọi 1 hàm khác của bên Swift, chúng ta sẽ cần biết cách sử dụng Swift code trong JavaScript để có thể giải quyết vấn đề này. Lằng nhằng thật, chúng ta đi vào code demo cho dễ hình dung vậy.
Đầu tiên, trong class ViewController, chúng ta tạo property là closure sau:
let randomStringHandler: @convention(block) (String) -> Void = { randomString in
}
Closure bên trên chính là block code mà hàm trong JavaScript sẽ gọi. Hãy chú ý vào kiểu của tham số trong closure bên trên, chúng ta đang để là String, đây là thứ chúng ta mong muốn được trả về khi gọi từ hàm trong JavaScript, randomString chính là giá trị mà hàm của JavaScript thực sự trả về.
Tiếp theo, vẫn trong class ViewController, chúng ta thêm hàm sau:
func generateRandomString() {
// 1
let randomStringObject = unsafeBitCast(self.randomStringHandler, to: AnyObject.self)
// 2
self.jsContext.setObject(randomStringObject, forKeyedSubscript: "handleRandomString" as (NSCopying & NSObjectProtocol)!)
// 3
_ = self.jsContext.evaluateScript("handleRandomString")
// 4
if let functionGenerateRandomString = self.jsContext.objectForKeyedSubscript("generateRandomString") {
// 5
_ = functionGenerateRandomString.call(withArguments: nil)
}
}
Trong đoạn code trên:
- 1: chúng ta chuyển kiểu của property randomStringHandler về kiểu AnyObject, bởi vì chúng ta cần kiểu định dạng này để sử dụng trong code bên dưới.
- 2: gán randomStringObject vào jsContext, chú ý ở đây randomStringObject chính là tên hàm chúng ta sẽ gọi trong code JavaScript
- 3: hàm evaluateScript thêm nội dung code closure của chúng ta vào JavaScript runtime
- 4, 5: như bên trên chúng ta đã tìm hiểu, chúng ta gọi hàm generateRandomString() của file JavaScript
Tiếp theo, chúng ta cần viết thêm hàm generateRandomString() trong file JavaScript. mở file jssource.js và thêm đoạn code sau:
function generateRandomString() {
var randomString = Math.random().toString(36);
// 1
handleRandomString(randomString);
}
Đoạn code bên trên cũng khá đơn giản, nhưng chúng ta cần để ý vào hàm handleRandomString(). Hàm này không hề có trong JavaScript, cũng không hề được chúng ta viết thêm trong file jssource.js, mà hàm này được chúng ta thêm vào trong đoạn code Swift bên trên.
Cuối cùng, chúng ta implement cho closure chúng ta đã khai báo bên trên, để xử lý chuỗi randomString được sinh ra. Quay trở lại class ViewController và thêm code sau:
let randomStringHandler: @convention(block) (String) -> Void = { randomString in
print("generated random string: " + randomString)
}
Túm lại flow chạy của các hàm trên là như sau: từ hàm generateRandomString() của class ViewController gọi đến hàm generateRandomString() của file jssource.js, hàm generateRandomString() của file jssource.js lại gọi đến closure randomStringHandler của class ViewController (chúng ta có thứ tự Swift -> JavaScript -> Swift)
Cuối cùng, thêm dòng code sau vào hàm viewDidAppear(_ animated: ):
generateRandomString()
Build chạy thử project, chúng ta được kết quả trong console log như hình sau:
III. Kết luận
Trên đây, tôi đã giới thiệu đến các bạn việc sử dụng JavaScript trong dự án iOS (ngôn ngữ Swift). Hi vọng bài viết này giúp ích cho các bạn trong việc tìm hiểu cách sử dụng JavaScript trong iOS.
Cuối cùng, xin cảm ơn các bạn đã theo dõi bài viết này!!!
All rights reserved