MVVM Swift

Xin chào! Trong bài hôm trước chúng ta đã viết xong lớp base để xử lý realm database rồi. Và bài hôm nay chúng ta sẽ tiến hành viết các service kế thừa từ lớp này để sử dụng realm database 1 cách linh hoạt.

Lớp RealmGithubService của chúng ta sẽ có nhiều vụ sau:

  • Lưu trữ kết quả từ server trả về, cụ thể model GithubSearchResponse
  • Vì với mỗi keyword sẽ có 1 response khác nhau, đồng thời mỗi keyword này tùy vào số page gửi lên mà 1 kết quả khác nhau. Do vậy chúng ta cần lưu response này với khóa chính kết hợp giữa keyword và page
  • Lấy response đã lưu dựa vào keyword đã search và page tương ứng
  • Xóa hay update response
  • Xóa toàn bộ response

Từ những phân tích trên, chúng ta tiến hành tạo 1 protocol như sau:

Tôi đã đặt tên sát với từng ý mà tôi đã phân tích ở trên. Tuy nhiên để model GithubSearchResponse tích hoạt realm thì chúng ta cần sửa đổi nó. Hãy cùng xem lớp này có gì nhé:

Lớp GithubSearchItem

Đầu tiên là lớp GithubSearchItem:

Dòng 49 tôi sử dụng @objcMembers để chỉ định rằng toàn bộ class này có thể truy cập được từ objective-C. Lý do mà tôi làm vậy vì realm được code từ C++, nên nó cần cho phép objective-C truy cập. Thông thường, C++ không thể truy cập trực tiếp vào swift, mà phải thông qua Objective-C. Cũng như bên java muốn truy cập được C++ thì cần JNI vậy 😝 Sau này bạn nhận ra 1 điều là bạn cũng có thể truy cập React native từ swift thông qua Objective-C.

Tôi muốn class GithubSearchItem vừa có thể dùng để nhận kết quả từ API, vừa có thể dùng nó như realm. Tham lam thật, nhưng chúng ta sẽ làm được điều đó bằng cách chỉnh sửa làm sao nó có thể hoạt động đồng thời cả với realm và codable.

  • dynamic var: Các cột trong bảng của realm bắt đầu bằng từ khóa này. Mỗi 1 thuộc tính là 1 cột. Ở đây tôi tạo ra 4 cột để lưu các thuộc tính này vào bảng của realm, gồm id, name, htmlURL, itemDescription.
  • override class func primaryKey: Hàm trả về khóa chính cho bảng. Một bảng có thể có khóa chính hoặc không nên hàm trả về kiểu optional. Tuy nhiên tôi muốn bảng này phải có khóa chính để sau này cập nhật. Trong SQL, khóa chính là tồn tại để định danh từng dòng trong bảng, duy nhất và khác nhau giữa các dòng. Realm cũng hoạt động tương tự. Ở đây ta thấy id có các tính chất như vậy nên ta chọn làm khóa chính.
  • required init(from decoder: Decoder) throws: Hàm dịch response của API thành object codable. Hàm này có mục đích là để mỗi khi decode xong json -> object, thì tôi tạo luôn realm cho object này. Chính là ở dòng này:
Tạo realm sau khi decode xong

Do vậy bạn có thể dùng cả codable và Object của realm. Xịn chưa 👌🥳

  • extension cho codable tương ứng với mỗi thuộc tính và key response tương ứng.

Tiếp theo chúng ta sẽ xem xét class GithubSearchResponse, chứa class GithubSearchItem trên:

class GithubSearchResponse
  • Tương tự, class này cũng chứa các thuộc tính có thể thao tác với realm nên ta define nó là @objcMembers
  • Ở đây, vì nó chứa list GithubSearchItem. Chúng ta bắt buộc phải sử dụng RealmSwift.List vì realm không hỗ trợ array thông thường của swift.
  • Ở đây tôi custom 1 khóa chính tên là keyword. Lý do là class này chưa có khóa chính, và vì với mỗi GithubSearchResponse trả về. Do đó tôi sẽ nghĩ ngay nó là khóa chính. Thứ 2 khóa chính này phụ thuộc vào page gửi lên, do vậy khóa chính sẽ là gộp của keyword và page.
  • Các extension và hàm init tương tự class GithubSearchItem

Vậy là bạn đã hoàn thành xong lớp GithubSearchResponse đảm bảo có thể lưu vào realm database. Việc của chúng ta tiếp theo là viết lớp RealmGithubService.😎

Định nghĩa lớp RealmGithubService
  • Như tôi đã trình bày ở bài trước, thì mọi class muốn kế thừa từ lớp RealmManager, đều phải khóa chính uid để có thể cập nhật hay xóa được. Do vậy bạn sẽ thấy đoạn code sau:
  • Tiếp theo, lớp RealmGithubService kế thừa các hàm của RealmGithubServiceProtocol, chúng ta sẽ định nghĩa các hàm này theo logic của app.
  • saveRepositoryResponse: Hàm này lưu response vào database. Trước khi lưu bạn cần gán keyword để làm khóa chính cho nó:
repoList.keyword = keyword
  • getRepositoryResponse: Hàm này sẽ tiến hành query toàn bộ model trong realm database, sau đó tìm model nào có keyword bằng keyword truyền vào, lấy thằng đầu tiên(first) và trả về.
  • deleteRepositoryResponse: Hàm xóa respone theo keyword. Tôi lấy model có khóa chính là keyword xong gọi hàm xóa. Rất đơn giản!
  • deleteAllRepositoryResponses: Xóa toàn bộ model GithubSearchResponse trong database
  • Ở đây tôi tạo class singleton. Nó đảm bảo trong 1 thời điểm chỉ có duy nhất 1 lớp có thể truy cập tới RealmGithubService.

Mọi thứ đã gần xong, giờ bạn hãy làm 1 cốc cà phê, ra ban công ngắm nhìn phố phường rồi tiếp tục sau nhé.

Chỉnh sửa GithubViewModel

Bây giờ chúng ta hãy tiến hành chỉnh sửa view model này. Hãy xem xét hàm requestRepositories đã có.

  • Mỗi khi tiến hành request API, chúng ta cần kiểm tra xem kết quả với từ khóa đó đã tồn tại trong database hay chưa bằng dòng code sau:

Ở đây bạn sẽ dùng hàm RealmGithubService.shared.getRepositoryResponse(with: keyword) để lấy response tương ứng với keyword gồm language + page. Nếu như nó đã tồn tại thì tiến hành các logic bình thường như sau khi API đã trả về kết quả. Còn nếu không, chúng ta sẽ tiến hành request API như bình thường. Tôi có dùng hàm  print(“from local”) để đánh dấu việc lấy data từ databse, print(“from network”) để đánh dấu lấy data từ server.

Khi request các bạn sẽ thấy log ở Xcode như sau:

Thật tuyệt vời, bạn hãy thử tắt luôn mạng internet để check cho chắc cú là nó đã lấy data từ realm nha. Tôi chỉ đùa vậy thôi! 😄

Okey, finally vậy là mọi thứ đã xong. Giờ đây bạn có thể tạo 1 ứng dụng cho phép sử dụng offline như facebook, không cần kết nối internet vẫn có thể thao tác với dữ liệu cũ, để tăng tính trải nghiệm người dùng, làm cho ứng dụng của bạn chuyên nghiệp hơn. Ứng dụng của chúng ta còn đơn giản, chưa nhiều logic phức tạp. Xong chắc hẳn tôi nghĩ bạn cũng đã hiểu được phần nào các làm 1 ứng dụng offline rồi phải hông? Và các bạn thấy được các tiện ích mà realm mang lại. Xin chào và hẹn gặp lại!😘😍

Code Toàn Bug

Code nhiều bug nhiều!

3 thoughts on “Lập trình IOS: Triển khai MVVM cho project swift(phần 5): Tạo ứng dụng offline bằng realm database(tiếp theo)

  1. Bài viết khá rối. Bạn nên cung cấp sơ đồ các khối xử lý và tương tác giữa các khối xử lý trước. Sau đó đi vào giải quyết từng xử lý sau.

    Người đọc sẽ nhìn vào thiết kế trước và xem cách bạn code theo thiết kế như thế nào ạ.

Leave a Reply

Your email address will not be published. Required fields are marked *