12/08/2018, 13:51

Swift 3.0 - Một vài thay đổi đáng chú ý [Part 2]

Trong phần 1 của bài viết này "Swift 3.0 - Một vài thay đổi đáng chú ý [Part 1]" , mình đã giới thiệu đến các bạn một ít thông tin về swift 3.0 và một vài thay đổi lớn, các features bị khai tử trên Swift 3.0. Trong phần 2 này, mình xin đề cập đến một vài features lớn sẽ được bổ xung vào Swift 3.0. ...

Trong phần 1 của bài viết này "Swift 3.0 - Một vài thay đổi đáng chú ý [Part 1]" , mình đã giới thiệu đến các bạn một ít thông tin về swift 3.0 và một vài thay đổi lớn, các features bị khai tử trên Swift 3.0. Trong phần 2 này, mình xin đề cập đến một vài features lớn sẽ được bổ xung vào Swift 3.0. Việc bổ xung thêm các features mới này vào trong ngôn ngữ sẽ góp phần làm Swift ngày càng hiện đại hơn, thân thiện hơn và dễ sử dụng hơn với lập trình viên.

a. key-path trong Key-Value Coding

Trong ngôn ngữ Objective-C, các class đều được thừa kế từ class NSObject nên việc sử dụng key-path là rất phổ biến. Tuy nhiên, trong Swift, nhiều class mới được viết lại mà không thừa kế từ NSObject (Array, Dictionary,...), nên key-path không còn được sử dụng nhiều như trước. Tuy nhiên, vẫn còn rất nhiều class trong Swift thừa kế từ class NSObject (đến class phổ biến như UIView còn thừa kế từ NSObject) nên việc thay đổi này trên Swift 3.0 vẫn rất giá trị.

Tương tự như việc viết Selector trong phần trước, việc viết key-path trên Swift 3.0 cũng tương tự, chúng ta không viết key-path bằng string trên Swift 3.0, bởi cách viết này không phát hiện được lỗi trong compile time mà lỗi chỉ sảy ra trong run time.

Ví dụ, trong các bản Swift trước, khi sử dụng key-path, chúng ta viết như sau:

class Phone : NSObject {
    var name: String = ""
    init(name: String) {
        self.name = name
        super.init()
    }
}

let iphone = Phone(name: "iPhone")
let phoneName = (iphone.valueForKeyPath("name"))

Trong ví dụ bên trên, chúng ta gọi hàm valueForKeyPath() để lấy giá trị của property name trong class Phone của instance iphone. Tuy nhiên, giả sử lập trình viên gõ nhầm valueForKeyPath("name") -> valueForKeyPath("naem") thì app của chúng ta khi build lên vẫn chạy bình thường, chỉ bị crash khi chạy đến đoạn code này.

Trên Swift 3.0, việc gọi hàm này được đổi lại như sau:

let iphone = Phone(name: "iPhone")
let phoneName = iphone.value(forKeyPath: #keyPath(Phone.name))

Như các bạn thấy, việc gọi hàm như thế này đảm bảo property "name" đã tồn tại, và chúng ta cũng tránh bị gõ sai property này, tương tự việc thay đổi cách gọi của Selector.

b. Loại bỏ tiền tố NS của các class trong framework Foundation

Lại thêm 1 thay đổi kinh hoàng nữa. Trong khi lập trình iOS, hẳn là rất nhiều lần chúng ta phải làm việc với các class của Foundation framework - một framework cơ bản của iOS. Chúng ta đã rất quen thuộc với các đoạn code kiểu như sau:

let filePath = NSBundle.mainBundle().pathForResource("DeviceList", ofType: "json")
let url = NSURL(fileURLWithPath: filePath!)
let data = NSData(contentsOfURL: url)
let json = try! NSJSONSerialization.JSONObjectWithData(data!, options: [])
print(json)

Hãy tưởng tượng, một ngày đẹp trời, bạn update lên Xcode 8, mở project ra và phát hiện đoạn code này của chúng ta báo lỗi đỏ lòm ở tất cả các dòng, và cả project cũng đỏ lòm vì bug, compiler báo hàng nghìn bug, chắc ngày hết đẹp luôn (yaoming)

Trên Swift 3.0, để đơn giản hóa ngôn ngữ, các tiền tố NS đều được loại bỏ. Đoạn code trên được viết lại như sau:

let filePath = Bundle.main().pathForResource("DeviceList", ofType: "json")
let url = URL(fileURLWithPath: filePath!)
let data = try! Data(contentsOf: url)
let json = try! JSONSerialization.jsonObject(with: data)
print(json)

Nguyên nhân của việc xóa bỏa tiền tố NS của Foundation framework, các bạc có thể đọc thêm ở đây

c. Số Pi mới

Trước đây, để lấy giá trị số Pi, chúng ta sử dụng M_PI dạng Double. Giờ đây trên Swift 3.0, chúng ta có thể sử dụng số Pi của cả Double, Float và CGFloat như sau:

Float.pi     // Float
Double.pi    // Double
CGFloat.pi   // CGFloat

Đây chỉ là một thay đổi nhỏ nhưng rất hữu ích trên Swfit 3.0 mới. Trước đây, khi chúng ta làm việc với số Pi, có thể chúng ta sẽ phải convert qua lại giữa Float->Double->CGFloat. Giờ đây chúng ta có thể sử dụng số Pi theo type mà mình muốn. Hơn nữa, cách viết mới cũng làm cho ngôn ngữ có vẻ hiện đại hơn, dễ đọc hơn.

d. Grand Central Dispatch (CGD)

Grand Central Dispatch được sử dụng để xử lý dữ liệu, tính toán trong các thread khác nhau, tránh block main thread, điển hình là các tác vụ liên quan đến mạng. Tuy nhiên, CGD được viết bằng ngôn ngữ C, việc sử dụng CGD không đơn giản đối với những người mới code.

Ví dụ, để tạo 1 queue mới và thực thi code không đồng bộ trong thread, chúng ta viết như sau:

let queue = dispatch_queue_create("New Queue", nil)
dispatch_async(queue) {
  print("Do task here")
}

Trong Swift 30 mới, chứng ta viết như sau:

let queue = DispatchQueue(label:New Queue)
queue.async {
  print(Do task here”)
}

Như chúng ta thấy, đoạn code mới trong Swift 3.0 dễ đọc, dễ hiểu hơn rất nhiều. Chúng ta tạo queue và thực thi code giống như gọi các class bình thường khác.

Để tìm hiểu thêm việc thay đổi syntax của CGD, các bạn có thể tham khảo tại đây

e. Core Graphics framework

Core Graphics framework là một framework rất mạnh của iOS. Lập trình viên có thể sử dụng Core Graphics để rend các view với custom sharp mà bình thường không thể vẽ với các API của UIView thông thường.

Cũng giống với CGD, các API của Core Graphics framework cũng được sử dụng dạng C-style. Và cũng giống CGD, việc này gây khó khăn cho dev, đặc biệt là những người mới, do các API hay sử dụng trong Core Graphics còn nhiều hơn các API của CGD. Trong Swift 3.0, các hàm trong Core Graphiccs cũng được thay đổi theo hướng hiện đại hơn, dễ đọc, dễ hiểu, và dễ sử dụng hơn.

Ví dụ, trong các phiên bản Swift trước đây, chúng ta viết code sử dụng Core Graphics như sau:

class View: UIView {
  let frame = CGRect(x: 0, y: 0, awidth: 100, height: 100)
  override func drawRect(rect: CGRect) {
    let context = UIGraphicsGetCurrentContext()
    let blue = UIColor.blueColor().CGColor
    CGContextSetFillColorWithColor(context, blue)
    let red = UIColor.redColor().CGColor
    CGContextSetStrokeColorWithColor(context, red)
    CGContextSetLineWidth(context, 10)
    CGContextAddRect(context, frame)
    CGContextDrawPath(context, .FillStroke)
  }
}

let aView = View(frame: frame)

Trong đoạn code trên, chúng ta override hàm drawRect để có thể custom hình dạng của UIView (Các bạn chú ý Apple không khuyến khích viết code vào hàm này, chỉ nên viết vào khi chúng ta cần custom hình dạng của view vì hàm này có thể làm app của chúng ta bị chậm). Trong hàm drawRect, chúng ta dùng các hàm của Core Graphics framework để vẽ nền, vẽ đường thẳng,… Các hàm này đều khó đọc, khó viết, gây khó khăn cho người mới sử dụng Core Graphics.

Trong Swift 3.0, đoạn code bên trên được viết lại khác hoàn toàn như sau:

class View: UIView {

  let frame = CGRect(x: 0, y: 0, awidth: 100, height: 100)
 
  override func draw(_ rect: CGRect) {
    guard let context = UIGraphicsGetCurrentContext() else {
      return
    }
    
    let blue = UIColor.blue().cgColor
    context.setFillColor(blue)
    let red = UIColor.red().cgColor
    context.setStrokeColor(red)
    context.setLineWidth(10)
    context.addRect(frame)
    context.drawPath(using: .fillStroke)
  }
}
let aView = View(frame: frame)

Như các bạn thấy, các hàm của Core Graphics framework được viết lại hiện đại hơn, hướng đối tượng hơn, dễ đọc, dễ hiểu, dễ viết hơn nhiều.

f. Naming convention: danh từ vs động từ

Trước đây, các hàm trong Swift thường được đặt tên bằng động từ. Tuy nhiên, trong phiên bản Swift mới này, các hàm trong Swift được chia ra thành 2 loại:

  • Hàm trả về giá trị nhất định: các hàm này khi gọi đến sẽ trả về kết quả, trong Swift 3.0, các hàm kiểu này được đặt tên bằng danh từ
  • Hàm không trả về giá trị: các hàm này khi gọi đến sẽ không trả về giá trị nào cả, và được đặt tên bằng động từ trong Swift 3.0

Ví dụ, trong Swift 2.2 chúng ta có hàm sort() như sau:

var array = [4, 9, 6, 1, 5, 3]
let sortedArray = array.sort()
print(sortedArray)

Hàm sort() bên trên trả về giá trị là mảng array đã được sắp xếp, vì thế trong Swift 3.0, hàm này được đặt tên bằng danh từ bằng cách thêm đuôi -ed vào động từ. Hàm này trên Swift 3.0 được đổi tên như sau:

var array = [4, 9, 6, 1, 5, 3]
let sortedArray = array.sorted()
print(sortedArray)

Trong Swift 2.2, chúng ta có một hàm khác để sắp sếp array: sortInPlace(). Hàm này sắp xếp array và không trả về giá trị nào cả mà chỉ có tác dụng sắp xếp array:

var array = [4, 9, 6, 1, 5, 3]
array.sortInPlace()
print(array)

Do hàm này không trả về giá trị nào cả, nên trong Swift 3.0 hàm này được đặt tên bằng động từ. Người ta đổi tên hàm sortInPlace() thành sort():

var array = [4, 9, 6, 1, 5, 3]
array.sort()
print(array)

Như các bạn thấy, vì naming convention mới mà hàm sort() thì đổi tên thành sorted(), còn hàm sortInPlace() thì đổi tên thành hàm sort() T_T

Trên đây chỉ là một vài thay đổi đáng chú ý của phiên bản Swift 3.0 mới, ngoài ra bản Swift này còn rất nhiều thay đổi khác mà chúng ta cần phải làm việc với nó nhiều hơn để khám phá thêm các thay đổi này. Cũng như lần nâng cấp từ Swift 1.2 -> Swift 2.0, lần nâng cấp từ Swift 2.2 -> Swift 3.0 này hứa hẹn mang đến cho lập trình viên chúng ta nhiều việc phải làm (update code), kèm theo đó là những thay đổi đáng giá: ngôn ngữ hiện đại hơn, dễ code hơn,… Chỉ còn một vài ngày nữa là Swift 3.0 chính thức ra mắt, hi vọng bài viết này của tôi sẽ có ích cho các bạn trong việc tìm hiểu về Swift mới. Các bạn có thể tham khảo thêm phần 1 của bài viết này tại đây.

Cuối cùng, xin cảm ơn các bạn đã theo dõi bài viết này !!!

0