Làm việc với Model và Dictionary trong ObjC và Swift (phần 2)

  • 01/02/2017
  • 734
Cỡ chữ: Giảm (A-) Mặc định (A) Tăng (A+) Nghe
  • Model tự init data dựa trên JSON mà không phải tự viết hàm initWithDictionary cho mỗi model mới khai báo dựa trên thư viện objc-runtime.
  • Model cho phép add các target cũng như cài đặt callback để tự động trigger events mỗi khi một thuộc tính của Model thay đổi giá trị bằng cách viết thư viện key-value observing. Vấn đề ở đây là nó sẽ không crash và tối ưu hoá hơn khi sử dụng cái mặc định của Apple.
  • Model lấy ý tưởng tương tự như một ActiveRecord trong Ruby on Rails.

Hôm nay mình sẽ nói rõ hơn làm thế nào để làm được điều này bằng thư viện objc-runtime.

Model tự init data dựa trên JSON (Dictionary)

Để init data được cho một model tao có 2 cách tiếp cận:

  • Cách dễ nhất là lặp qua hết các keys của Dictionary và lấy value sau đó set cho model.
  • Biết được model đó có bao nhiêu thuộc tính cũng như kiểu dữ liệu của từng thuộc tính rồi tiến hành lấy dữ liệu tương ứng từ Dictionary để set cho từng thuộc tính.

Cách tiếp cận ban đầu có lợi thế là dễ làm nhưng nó dễ gặp nhiều vấn đề như: lặp qua các key dư thừa không có trong model (giả sử dictionary có 5 key nhưng model chỉ có 3 thuộc tính). Không khớp kiểu dữ liệu giữa dictinonary và model (giả sử price trong dictionary là kiểu int trong khi của model là kiểu float). Cụ thể ta có ví dụ như sau:

Dictionary

Model

Ta thấy giả sử nếu viết một hàm init data mà lặp qua kết các keys của Dictionary để set giá trị cho model thì sẽ dư thừa key city, country cho mỗi lần init data. Hơn nữa kiểu dữ liệu về age sẽ không khớp (không thể xác định được age trong dictionary là kiểu int hay kiểu float). Việc lặp qua hết các keys của Dictionary để set value cho thuộc tính model còn gặp một vấn đề nữa là nếu không handle exception thì chương trình sẽ bị crash khi key đó không có trong model ví dụ key city và country

Với cách tiếp cận thứ hai là căn cứ vào thuộc tính của model để set giá trị có vẻ như là tốt hơn. Nhưng vấn đề là làm thế nào để lấy được danh sách thuộc tính cũng như kiểu dữ liệu của từng thuộc tính. Sử dụng thư viện objc-runtime. Các bước như sau:

Lấy danh sách thuộc tính -> lấy danh sách kiểu dữ liệu -> Lấy giá trị ứng với thuộc tính trong Dictionary -> Căn cứ vào kiểu dữ liệu ứng với thuộc tính tiến hành init dữ liệu cho thuộc tính.

Cùng tham gia sự kiện mobile vào ngày 23/02/2017
  • Lấy danh sách thuộc tính:
  • Sau khi lấy ra được thuộc tính và kiểu dữ liệu ta có thể tiến hành lấy giá trị và set giá trị cho model.

Toàn bộ source code bạn có thể xem ở đây: model

Tạo property reaction khi property thay đổi giá trị

Đôi khi trong lập trình, để dễ dàng hơn, ta muốn khi giá trị một thuộc tính của model thay đổi thì nó sẽ tiến hành gọi một hàm nào đó (callback). Để làm được điều này, chúng ta sử dụng tính năng key-value observing của ObjC cung cấp. Giả sử ta gọi hàm callback đó là một Action.

  • Tạo Object class cho Action:
  • Tiến hành đăng kí một event cho một thuộc tính của model
  • Hàm đăng ký key-observer
  • Cài đặt hàm thực thi khi nhận được notification
  • Khi giá trị của model bị thay đổi giả sử trong đoạn code sau:

Sau dòng lệnh gán giá trị mới cho thuộc tính name trong model user, thì model user sẽ tiến hành gọi hàm renderNameLabel. Model cung cấp một cơ chế tự động remove key-observer nên khi sử dụng chỉ cần add target, không cần quan tâm tới việc remove key-observer khi dealloc model. Điều này giảm thiểu việc crash khi lập trình.

Toàn bộ source code bạn có thể xem ở đây

Cùng tham gia sự kiện mobile vào ngày 23/02/2017

Model lấy ý tưởng tương tự như một ActiveRecord trong Ruby on Rails

Trong model ở đây, mình chỉ lấy ý tưởng từ phần active record về việc auto mapping giữa tên, kiểu dữ liệu của thuộc tính với dữ liệu trong JSON (Dictionary). Trong khi làm việc với các app có lấy dữ liệu từ mạng về, các bạn rất hay gặp trường hợp cần tải dữ liệu của một đối tượng về mà chỉ biết id của đối tượng đó. Ví dụ khởi tạo đối tượng User có id bằng 1.

Hoặc bất đồng bộ

Các công việc cần làm là ta sử dụng một private networking cho class model để tiến hành lấy dữ liệu từ server và kết hợp với phần ở trên để init dữ liệu. Với ý tưởng này, ta có thể đóng gói được phần code làm việc với server gói gọn chỉ trong phần model base mà không cần phải viết lại cho mỗi lần tạo thêm model mới. Từ đó lượng code sinh ra ít hơn và dễ quản lý hơn.

Trong phần model (mình đã update thành opensource) mình đã hiện thực tất cả các phần ở trên, việc sử dụng cũng cực kì đơn giản, chỉ việc kết thừa từ class SModel là có tất cả các tiện ích kể trên. Đồng thời SModel cũng cung cấp thêm nhiều hàm tiện ích khác như mặc định gái trị default cho Model khi giá trị tron Dictionary bị null, convert ngược lại từ class model thành Dictionary cho việc gửi parameters hoặc lưu thành file JSON. Cung cấp hàm callback bằng block, thêm hàm callback khi fetchInBackground.

Techtalk via huypham