[Swift] Làm quen với Realm trong lập trình ứng dụng iOS

[Swift] Làm quen với Realm trong lập trình ứng dụng iOS

1. Giới thiệu:

Các bạn khi lập trình ứng dụng mobile cho iOS lưu trữ database, chắc không lạ gì với cơ sở dữ liệu Sqlite và Core Data. Mỗi cái có các ưu điểm và khuyết điểm riêng. Trong bài viết này mình không đi sâu vào giới thiệu sự khác nhau giữa các cơ sở dữ liệu này. Mình sẽ giới thiệu về Realm  như một lựa chọn thay thế cho Sqlite và Core Data, giới thiệu về cách cài đặt và sử dụng trong ứng dụng cho các bạn khi lần đầu làm quen với cơ sở dữ liệu này.

 

2. Cài đặt:

Có 3 cách cài đặt Realm

– Cài trực tiếp từ file framework

Bạn có thể tải realm framework qua đường link sau: Link

Sau khi giải nén bạn sẽ được 2 file RealmSwift.framework và Realm.framework

Bạn hãy kéo thả 2 file này vào Project → Target của dự án →  General → Embedded Binaries (nhớ bấm vào Copy items if needed)→ Finish

Thêm mới Run Script Phase→ Project →Target của dự án →  Build Phases → New Run Script Phase và paste đoạn script này vào

– Cài thông qua CocoaPods

Cách này yêu cầu máy tính của bạn đã cài đặt CocoaPods (Bạn có thể xem tại đây)

Thêm đoạn code sau vào file Podfile

Sau đó chạy lệnh để cài đặt Realm

Sử dụng file .xcworkspace được tạo bởi CocoaPods để làm việc. (Chú ý không sử dụng file mặc định .xcodeproj)

– Cài thông qua Carthage

Cách này cũng yêu cầu máy tính của bạn đã cài đặt Carthage (Bạn có thể xem tại đây)

Thêm dòng này vào file Cartfile của bạn

Sau đó chạy lệnh

Khi chạy xong trong thư mục build của Carthage sẽ xuất hiện 2 file là RealmSwift.framework và Realm.framework tại đường dẫn Carthage/Build/

Bạn hãy kéo thả 2 file này vào Project → Target của dự án →  General → Linked Frameworks and Libraries (nhớ bấm vào Copy items if needed)→ Finish

 

3. Các thao tác với Realm:

a. Định nghĩa đối tượng lưu trữ dữ liệu trong Realm.

Đối tượng Customer được kế thừa từ Object, Object là một đối tượng đặc biệt của Realm. Mọi đối tượng khi thao tác trên Realm đều phải kế thừa từ Object này.

Realm hỗ trợ hầu hết các kiểu dữ liệu như Bool, Int, Int8, Int16, Int32, Int64, Float, String, Date, Data.

Một lưu ý nhỏ là Realm chỉ hỗ trợ String, Date và Data có thể khai báo là Optional.

Như ví dụ trên thì createdDate là một giá trị Optional.

Nếu như bạn khai báo

thì sẽ bị lỗi khi biên dịch.

Để hỗ trợ Optional Int ta phải khai báo như sau:

Và khi gán giá trị cho RealmOptional ta gán thông qua thuộc tính value của RealmOptional.

Ngoài ra Realm còn hỗ trợ khai báo Primary key và Index thông qua việc khai báo 2 hàm override của đối tượng Realm Object. Với việc khai báo này khi truy vấn cơ sở dữ liệu từ Realm, kết quả sẽ được xuất ra nhanh hơn khi làm việc với cơ sở dữ liệu vài chục nghìn records trở lên.

Realm cũng hỗ trợ kiểu List để lưu giữ giá trị mảng. Chú ý đối tượng lưu trữ trong List cũng phải là kiểu đối tượng được kế thừa từ Realm Object.

Khi thêm giá trị vào List ta chỉ việc append giá trị vào list thông qua method append.

b. Cấu hình cho Realm

Ta tạo mới 1 class để quản lý các business liên quan tới truy cập cơ sở dữ liệu.

 

Như khai báo ở trên ta tạo 1 class DatabaseManager dạng Singletone (Chỉ tạo 1 lần và có thể truy cập ở mọi nơi thông qua biến instance khai báo ở trên) để quản lý toàn bộ database.

Trong hàm khởi tạo, ta gán thuộc tính schemeVersion (chú ý giá trị được khai báo tăng dần, sẽ nói ở phần Migration) làm config chung cho toàn bộ đối tượng của Realm trong dự án.

Tạo mới class Customer Data Access để xử lý toàn bộ chức năng truy vấn cơ sở dữ liệu của customer.

c. Truy cập dữ liệu trong Realm

  • Lấy customer đầu tiên trong database

Đoạn code này giống như ta sử dụng 1 đối tượng truy cập cơ sở dữ liệu (ở đây là Realm), quét toàn bộ record table Customer và trả về giá trị đầu tiên.

  • Filter, Sort kết quả

Để filter giá trị cần lấy ta tạo mới 1 mệnh đề sử dụng NSPredicate và filter với predicate đó.

Hoặc để sort kết quả

Một lưu ý nhỏ, trong Realm mọi đối tượng đều được lưu trữ dưới dạng là lazy object, dù cho trả ra bao nhiêu đối tượng đi nữa thì bộ nhớ cũng sẽ không được tăng lên trừ khi ta trực tiếp lấy nó ra.

Ví dụ ở đoạn code này sẽ trả ra toàn bộ record của customer.

Giả sử như database của chúng ta có 5000 records của customer thì giá trị của allCustomer đang chứa 5000 records. Nhưng nó chỉ là 1 biến reference tới 5000 records đó chứ không phải là đối tượng được lưu trong bộ nhớ ram. Chỉ khi chúng ta lấy trực tiếp nó ra thì nó mới được lưu vào ram (chiếm bộ nhớ)

  • Thêm mới một record

  • Cập nhật nội dung của record

Như ở trên khi thêm mới hoặc cập nhật thông tin 1 đối tượng Realm Object, ta phải kiểm tra đối tượng Realm này có đang ở trên cùng 1 transaction write hay không?

Vì trong Realm khi ta thêm mới, chỉnh sửa, hoặc xoá bắt buộc phải viết ở trong 1 transaction.

d. Migrations trong Realm như thế nào?

Ta cùng xem xét ví dụ sau:

Với ví dụ trên ta để ý rằng, đối tượng customer đã thay đổi thuộc tính customerName thành thuộc tính customerFullName, hoặc ta có thể hiểu thuộc tính customerName đã bị xoá bỏ và customerFullName đã được thêm vào.

Do đó với đoạn code trên, khi khởi chạy ứng dụng thì sẽ bị lỗi crash thông báo rằng properties của Customer đã bị thay đổi. Vậy để giải quyết vấn đề này ta sẽ làm như thế nào.

Ta cùng quay lại chỗ config của Realm ở trên. Ta chỉ cần sửa schemaVersion lớn hơn giá trị cũ thì Realm sẽ tự detect ra properties nào bị xoá và được thêm vào để chỉnh sửa lại thông tin của cơ sở dữ liệu. Do đó khi khởi chạy ứng dụng ta sẽ không bị crash vì lỗi migration nữa.

e. Notifications trong Realm.

Realm cung cấp sẵn cho ta một đối tượng là NotificationToken để có thể lắng nghe các thay đổi trong cơ sở dữ liệu như :

  • Khi nào được khởi tạo xong?
  • Khi nào có dữ liệu mới thêm vào?
  • Khi nào có dữ liệu được chỉnh sửa?
  • Khi nào có dữ liệu bị xoá đi?

Ta cùng tham khảo đoạn code dưới

Ở ví dụ trên biến notificationToken phải được khai báo toàn cục, và nên invalidate nó khi không sử dụng tới nữa.

Có một chú ý là trong trường hợp update xoá record khỏi cơ sở dữ liệu RealmCollectionChange mặc định sẽ trả về giá trị mới nhất của toàn bộ records trong database, có nghĩa là nó sẽ không trả về toàn bộ records trước khi delete mà chỉ trả về toàn bộ records sau khi đã deleted. Vì vậy nếu cần lấy giá trị trước khi deleted ta cần phải lưu giữ nó lại thông qua biến holdMessages ở trên.

f. Xem nội dung cơ sở dữ liệu của Realm như thế nào?

Khi chúng ta xây dựng xong ứng dụng điều chúng ta muốn biết nhất là cơ sở dữ liệu hiện có đang lưu trữ đúng giá trị ta cần hay không? Realm đã cung cấp sẵn cho chúng ta Realm Studio để có thể quản lý dễ dàng cơ sở dữ liệu. Với Realm Studio ta có thể xem cấu trúc của database, chỉnh sửa trực tiếp dữ liệu của database (Thêm, Sửa, Xoá).

Realm Studio

Công cụ này hỗ trợ cả Mac, Windows và Linux bạn có thể download tại đây.

4. Các điểm cần chú ý

  • Không chạy nhiều thể hiện của Realm trên nhiều thread.  (Realm có cơ chế tự kiểm tra khi đang được truy cập trên cùng 1 thread hay không, nên khi muốn chạy multithread bắt buộc phải truy cập thông qua ThreadSafeReference)
  • Không đọc Realm object từ thread này rồi chỉnh sửa trên thread khác.
  • Phải chỉnh sửa dữ liệu trên 1 write transaction của Realm instance.

5. Tổng kết:

Như mình đã giới thiệu qua ở trên. Thao tác với Realm cực kỳ đơn giản và dễ dàng sử dụng.

Vì được phát triển từ C++ nên các vấn đề liên quan tới performance được tối ưu tối đa phù hợp cho các dự án vừa và lớn liên quan tới cơ sở dữ liệu.

Nếu bạn thích thú với Realm có thể áp dụng ngay vào dự án của mình để nhận thấy sự khác biệt.

 

Link tham khảo:

https://realm.io/docs/swift/latest/

Leave a Reply