07/09/2018, 17:22

Swift - Closure: Bài 2: CallBack/ Completion (Part 2)

Swift - Closure: Bài 2: CallBack/ Completion (Part 2) 3. Closure with arguments Dữ liệu của ta thường là các kiểu của Networking service, không phải kiểu đơn giản load file local json như bài 1, mà là nhận API, rồi lấy data từ đó, mặc dù nói chung vẫn trả về kiểu json tương tự. Chúng ta ...

Swift - Closure: Bài 2: CallBack/ Completion (Part 2)

3. Closure with arguments
Dữ liệu của ta thường là các kiểu của Networking service, không phải kiểu đơn giản load file local json như bài 1, mà là nhận API, rồi lấy data từ đó, mặc dù nói chung vẫn trả về kiểu json tương tự.
Chúng ta bắt đầu pass data ở đây thông qua completion của ta. Ta có thể cho thêm arguments vào bên trong completion closure. Và ta thêm như sau:

  func getPeople(completion: (Bool, Any?, Error?) -> Void) {
    guard let path = Bundle.main.path(forResource: "someJson", ofType: "json") else { return }
    let url = URL(fileURLWithPath: path)
    do {
      let data = try Data(contentsOf: url)
      let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
      guard let array = json as? [[String: Any]] else { return }
      var names = [String]()
      for user in array {
        guard let name = user["name"] as? String else { continue }
        names.append(name)
      }
//      print(names)
      completion(true, names, nil)
    } catch {
      print(error)
      completion(false, nil, error)
    }
  }

Để ý dòng code đầu, getPeople(completion: (Bool, Any?, Error?) -> Void), biến đầu tiên sẽ là Bool, biến thứ hai sẽ là Any? và cuối cùng là Error? - cũng là 1 optional. Tức là khi bạn gọi completion, bạn phải truyền giá trị vào cho mỗi chú, bắt buộc. VÌ không thể gọi 1 function mà không truyền giá trị tương ứng.

Trong đoạn chỗ do, ta chắc chắn khi nó load phần này thì nó đã thành công, và ta sẽ đặt true, và thông tin ta cần pass đến nơi mà gọi function này là mảng names, cho nên ta đặt names, và đương nhiên như đã nói ở trên, phần này không có error, nên ta trả về nil.

Ta làm tương tự cho phần dưới, trong catch block, nó sẽ false và ta chẳng có data nào muốn chuyển trong phần nào cả, cái thông tin ta cần lấy ở đây chính là error mà được gen automatically bởi catch block nào. Bây giờ comment getPeople đang có hiện tại và type getPeople, bạn sẽ thấy như sau:
alt text

Bấm ok, thì nó sẽ ra code bên trong với các argument name, ta rename lại chúng và sử dụng chúng, đặt tên gì cũng được hết, nên ta đặt như sau:

getPeople { (success, response, error) in

    }

Thực ra bạn đặt gì cũng được nhưng đây là general practice, nên làm theo, h ta cần làm là nếu success == true thì nhận/ xử lý data, không thì xem error là gì

getPeople { (success, response, error) in
      if success {
        guard let names = response as? [String] else { return }
        print(names)
      } else {
        if let error = error {
          print(error)
        }
      }

Nhìn dễ hiểu hơn đúng không mọi người, ta sử dụng những values ở đây dựa trên cái mà được chuyển đến.
Đối với success, the response chính là names array mà ta vừa chuyển đến, là String array, nhưng mà ta rõ ràng ta vẫn không biết vì ta ném nó vào completion handler, do đó nó là Any Object, muốn dùng thì phải down cast nó xuống.
Đối với error thì đó là error từ khối catch block mà ta đã có được. Lưu ý ở đây cả hai phần response, error đều đang là optional nên ta unwrap nó bằng cách chaining. Bạn có thể nil-coalescing hoặc force.

Như đã nói, phần getPeople này sẽ nằm ở phần khác chứ không phải ngay chính VC này, nên tất cả những thông tin này sẽ được xử lý và ném qua completion handler

0