Xin chào tất cả các bạn!

Hôm nay VietDevelopers sẽ giới thiệu những cái mới trong Swift 3 để các bạn tiện theo dõi nhé.

Apple đã tích hợp Swift 3 vào phiên bản Xcode 8 beta tại sự kiện WWDC năm nay và sẽ tung ra bản chính thức vào vài tháng tới. Đây là phiên bản open source (mã nguồn mở) đầu tiên và hoạt động trên cả hai hệ điều hành Mac OS X và Linux. Nếu bạn theo dõi Swift Evolution trên Github từ tháng 12 năm ngoái bạn sẽ nhận ra rằng nó có rất nhiều sự thay đổi. Chúng tôi chắc rằng nếu bạn biên dịch code của bạn trên Xcode 8, code của bạn sẽ bị phá vỡ.

Sự thay đổi ở Swift 3 có thể nhóm thành hai mục chính sau đây:

  • Xóa những tính năng đã lỗi thời trong Swift 2.2
  • Các vấn đề hiện đại hóa ngôn ngữ

Hãy bắt đầu với mục thứ nhất, thật dễ hiểu khi bạn đã gặp phải trường hợp những cảnh báo lỗi thời ngay trong Xcode 7.3.

Toán tử ++ và --

Toán tử tăng và giảm được thừa kế từ ngôn ngữ lập trình C và chức năng của chúng rất đơn giản - đó là cộng hoặc trừ đi 1 đơn vị của biến:

var i = 0
i++
++i
i--
--i

Tuy nhiên, mọi thứ trở nên phức tạp khi nó quyết định nên chọn toán tử nào. Mỗi toán từ có thể luôn đi kèm với hai phần tiền tố và hậu tố.

Điều này khiến cho các lập trình viên mới bối rối, vì vậy Apple quyết định bỏ chúng đi và thay vào đó là hai toán tử += (đối với toán tử tăng) và -= (đối với toán tử giảm):

var i = 0
i += 1
i -= 1

Tất nhiên bạn vẫn có thể sử dụng toán tử (+) và (-):

i = i + 1
i = i - 1

Không còn vòng lặp kiểu C

Cách sử dụng toán tử tăng và giảm phổ biến nhất ở ngôn ngữ C là trong vòng lặp (loop). Việc xóa bỏ các toán tử đó có nghĩa là chức năng này cũng thành vô nghĩa, do đó bạn sẽ không thể thực hiện một vòng lặp trong khoảng như kiểu C nữa, thay vào đó là vòng lặp for-in.

Nếu bạn đã có chút kiến thức về lập trình, bạn chắc hẳn sẽ viết một vòng lặp in ra số từ 1 đến 10 như sau:

for (i = 1; i <= 10; i++) {
  print(i)
}

Trong Swift 3, bạn không được phép làm như vậy nữa. Thay vì sử dụng toán tử tăng, bạn sử dụng ba dấu chấm (...) để hiện khoảng:

for i in 1...10 {
  print(i)
}

Ngoài ra, bạn cũng có thể sử dụng vòng lặp for-each với cú pháp sau:

(1...10).forEach {
  print($0)
}

Bỏ đi từ khóa var trong tham số của hàm

Các tham số trong của hàm thường được khai báo như những hằng số, do vậy bạn sẽ không thể thay đổi giá trị của chúng trong thân hàm được. Tuy nhiên, có một vài trường hợp khai báo biến sẽ trở nên hữu ích. Trong Swift 2, để khai báo biến trong hàm như một tham số chúng ta cần phải sử dụng từ khóa var. Một khi tham số này được khai báo với từ khóa var, nó sẽ tạo một bản lưu cục bộ giá trị của tham số, do đó bạn có thể thay đổi giá trị của biến đó ở trong thân hàm.

func gcd(var a: Int, var b: Int) -> Int {
  if (a == b) {
    return a
  }
  repeat {
    if (a > b) {
      a = a - b
    } else {
      b = b - a
    }
  } while (a != b)
  return a
}

Thuật toán rất đơn giản: nếu cả hai số thực sự bằng nhau, thì trả về một trong hai. Ngược lại, so sánh hai số, lấy số lớn hơn trừ số nhỏ hơn rồi gán cho số lớn hơn cho đến khi hai số trở nên bằng nhau và trả về một trong hai số. Như bạn thấy, bằng cách tạo ra hai biến a và b, chúng ta có thể thay đổi giá trị của chúng trong hàm.

Swift 3 không còn cho phép lập trình viên gán các tham số trong hàm như các biến - điều khiến cho các lập trình viên lẫn lộn bởi varinout. Do vậy, ở phiên bản cuối cùng của Swift, nó sẽ sẽ bỏ từ khóa var trong các tham số của hàm.

Vì vậy, các viết hàm gcd ở Swift 3 sẽ có chút khác. Bạn sẽ cần lưu giá trị của các tham số của hàm vào các biến cục bộ:

func gcd(a: Int, b: Int) -> Int {
  if (a == b) {
    return a
  }
  var c = a
  var d = b
  repeat {
    if (c > d) {
      c = c - d
    } else {
      d = d - c
    }
  } while (c != d)
  return c
}

Tính nhất quán trong đặt nhãn cho các tham số của hàm

Các tham số của hàm được liệt kê như các tuples (bộ dữ liệu), vì vậy bạn có thể sử dụng chúng để gọi hàm với điều kiện cấu trúc của tuple trùng với cấu trúc mà hàm đã khai báo. Lấy hàm gcd() làm ví dụ. Bạn có thể gọi nó như sau:

gcd(8, b: 12)

Hay thậm chí gọi hàm như cách sau:

let number = (8, b: 12)
gcd(number)

Như bạn đã thấy, bạn không cần gọi tên nhãn ở tham số thứ nhất trong Swift 2. Tuy nhiên, bạn lại phải gọi tên nhãn của tham số thứ nhất (và các tham số còn lại) khi gọi hàm.

Cú pháp này khiến cho lập trình viên mới bối rối, vì vậy trong Swift 3, hàm sẽ được gọi như sau:

gcd(a: 8, b: 12)

Bạn phải gọi tường minh tên nhãn của tham số thứ nhất. Nếu bạn không làm vậy, Xcode 8 sẽ báo một lỗi.

Phản ứng đầu tiên của bạn về sự thay đổi này là: "Trời ơi, mình lại phải thay đổi một mớ code cũ ư?". Bạn đúng rồi đấy. Bạn sẽ phải thay đổi nhiều thứ. Do vậy Apple cung cấp một cách để bỏ qua nhãn của tham số thứ nhất khi gọi một hàm. Bạn có thể thêm dấu gạch dưới _ đối với tham số thứ nhất như ví dụ sau:

func gcd(_ a: Int, b: Int) -> Int {
...
}

Với cách làm này, bạn có thể gọi hàm sử dụng cách cũ - mà không cần gọi tên nhãn cho tham số đầu tiên. Điều này khiến bạn có thể tương thích code của bạn từ Swift 2 sang Swift 3 một cách dễ dàng hơn.

Selector như kiểu String sẽ không còn

Hãy tạo một button (nút) và làm gì đó khi người dùng tap vào nó - chỉ được sử dụng Playground, không sử dụng Interface Builder:

// 1
import UIKit
import XCPlayground
// 2
class Responder: NSObject {
  func tap() {
    print("Button pressed")
  }
}
let responder = Responder()
// 3
let button = UIButton(type: .System)
button.setTitle("Button", forState: .Normal)
button.addTarget(responder, action: "tap", forControlEvents: .TouchUpInside)
button.sizeToFit()
button.center = CGPoint(x: 50, y: 25)
// 4
let frame = CGRect(x: 0, y: 0, width: 100, height: 50)
let view = UIView(frame: frame)
view.addSubview(button)
XCPlaygroundPage.currentPage.liveView = view

Có rất nhiều thứ xảy ra ở đây, do đó hãy chia ra thành nhiều bước nhỏ:

  1. Import 2 frameworks là UIKit và XCPlayground - bạn cần chúng để tạo một button và hiển thị nó ngay trên Playground. Lưu ý: Bạn nên bật chế độ hỗ trợ trình biên soạn (assistant editor) ở trong Xcode để tương tác được với button: Chọn View -> Assistant Editor -> Show Assistant Editor
  2. Định nghĩa phương thức tap và được gọi khi người dùng nhấn vào nút button, và phản hồi về một đối tượng (responder object) cho button target - lớp cha là NSObject, bởi vì selector chỉ hoạt động với các phương thức Objective-C
  3. Định nghĩa button và thiết lập các thuộc tính của nó
  4. Định nghĩa view và frame tương ứng của nó, và thêm button vào trong nó rồi hiển thị nó trong assistant editor

Hãy chú ý ở câu lệnh:

button.addTarget(responder, action: "tap", forControlEvents: .TouchUpInside)

Giá trị ở tham số action (hay còn gọi là button selector) là một chuỗi: "tap". Nếu bạn gõ sai, code của bạn sẽ được biên dịch như sẽ crash (sụp đổ) trong thời gian chạy (compile time), với lỗi là: không tìm thấy phương thức tương ứng.

Để giải quyết vấn đề này, Swift 3 thay thế chuỗi selector bằng từ khóa #selector(). Điều này cho phép trình biên dịch (compiler) phát hiện vấn đề sớm khi bạn không điền vào tên phương thức đúng.

button.addTarget(responder, action: #selector(Responder.tap), for: .touchUpInside)

Đó là tất cả cho phần 1 - xóa một vài tính năng. Bây giờ hãy chuyển sang phần 2 - vấn đề hiện đại hóa ngôn ngữ.

Key-path như các chuỗi

Tính năng này giống với phần trước, tuy nhiên nó được áp dụng cho key-value coding (KVC) và key-value observing (KVO):

class Person: NSObject {
  var name: String = ""
  init(name: String) {
    self.name = name
  }
}
let me = Person(name: "Cosmin")
me.valueForKeyPath("name")

Ban tạo một class có key-value coding phù hợp, tạo định danh của tôi bằng hàm khởi tạo và sử dụng key-path tương ứng để xác định tên của tôi. Một lần nữa, nếu bạn sai, mọi thứ sẽ vỡ tung và bạn sẽ không hề vui chút nào! 🙁

May thay, điều này sẽ không còn xảy ra ở Swift 3 nữa. Chuỗi key-path được thay thế bằng biểu thức #keyPath():

class Person: NSObject {
  var name: String = ""
  init(name: String) {
    self.name = name
  }
}
let me = Person(name: "Cosmin")
me.value(forKeyPath: #keyPath(Person.name))

Bỏ tiền tố NS cho các kiểu cơ sở (Foundation Types)

Một ví dụ về phân tích JSON (JSON parsing):

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

Bạn sử dụng các lớp cơ sở (Foundation classes) để kết nối file và trích xuất dữ liệu JSON bằng một cách phù hợp: NSBundle -> NSURL -> NSData -> NSJSONSerialization. Các tiền tố NS sẽ bị bỏ ở trong Swift 3:

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

Bài được dịch từ blog Appcoda.com. Vui lòng ghi nguồn bản dịch thuộc về VietDevelopers.com.

Lập trình iOS - Lập trình iOS cơ bản - Lập trình Swift - Lập trình Swift 3 - Học lập trình iOS - Lập trình di động - Học lập trình - Vietdevelopers - Vietdevelopers.com