iOS/Swift: Lưu dữ liệu sử dụng Property List

by DaoNM2
1.4K views

Lời mở đầu

Cách lưu dữ liệu sử dụng file Property List khá phổ biến trong lập trình ứng dụng iOS, nó thường được dùng để lưu các dữ liệu nhỏ và không cần tính bảo mật cao. Bài viết này mình sẽ chia sẻ với các bạn một số cách để thao tác với Property List.

Info.plist

Tất cả các project khi bạn mới tạo XCode sẽ tự tạo ra một file Info.plist, nó làm nhiệm vụ lưu lại các thông tin dự án của bạn. Từ project name, version cho đến các setting cũng như mô tả API của Apple.

Các thông tin trong file Info.plist sẽ được các API của Apple truy cập vào để lấy thông tin hiển thị lên ứng dụng. (VD: Hiển thị message cho pop up xin quyền truy cập Camera …) cũng như việc xác nhận thông tin của Apple khi bạn Submit ứng dụng lên App Store Connect. Vì vậy file này chỉ nên lưu những thông tin cài đặt của dự án. Nếu bạn muốn lưu dữ liệu dạng này ta có thể tạo một file mới và lưu vào đó.

Tạo file Property List mới

Bước 1: Chuột phải vào thư mục bạn muốn lưu file -> New File…

Bước 2: Đánh ô tìm kiếm phía trên với keyword “Property List” -> Next -> Đặt tên file

Vậy là bạn đã tạo thành công file Property List. Giờ bạn có thể mở file và điền thông tin mình muốn lưu vào file đó.

Tuy nhiên nếu sử dụng XCode thì chúng ta chỉ có thể lưu được các dữ liệu như:

  • String
  • Number
  • Bool
  • Data
  • Date
  • Array
  • Dictionary

Chúng ta cũng có thể lưu kiểu custom object vào file Property List nhưng phải sử dụng code để encode dữ liệu cần lưu sang Data rồi khi sử dụng thì chúng ta decode nó về dữ liệu ban đầu.

Tạo file Property List bằng code

Chúng ta có thể tạo file Property List một cách đơn giản hơn bằng cách như sau:

    func save(key: String, value: Any) {
        let myData: NSDictionary = [key: value]
        let fileManager = FileManager.default
        // Đường dẫn lưu file và tên file của bạn
        let path = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("MyInfo3.plist")
        // Viết vào file nếu file không tồn tại nó sẽ tự tạo file mới.
        myData.write(to: path, atomically: true)
        print(path)// in ra console để thấy đường dẫn lưu file
    }

Truy cập vào file Property List bằng code

Lấy data từ file Property list

    func getMyPlist(key: String) -> Any? {
        // Lấy ra đường dẫn vào file Property List của bạn
        let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("MyInfo.plist")
        // Sử dụng NSDIctionary để lấy nội dung từ URL
        guard let myDict = NSDictionary(contentsOf: path) else { return nil }
        print(path)
        // Trả về giá trị theo key trong Dictionary
        return myDict[key]
    }

Lưu dữ liệu vào file Property List

    func saveMyPlist(key: String, value: Any) {
        let fileManager = FileManager.default
        // Tạo đường dẫn file
        let path = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("MyInfo.plist")
        guard let myDict = NSMutableDictionary(contentsOf: path) else {
            // nếu file chưa tồn tại chúng ta sẽ ghi vào file với bộ key/value đầu tiên.
            let myData: NSDictionary = [key: value]
            myData.write(to: path, atomically: true)
            return
        }
        // nếu file đã tồn tại chúng ta sẽ update dữ liệu
        myDict[key] = value
        myDict.write(to: path, atomically: true)
        print(path)
    }

Xóa một key trong Property List

    func removeMyPlist(key: String) {
        let fileManager = FileManager.default
        // Tạo đường dẫn file
        let path = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("MyInfo.plist")
        guard let myDict = NSMutableDictionary(contentsOf: path) else { return }
        // nếu file đã tồn tại chúng ta sẽ update dữ liệu
        myDict.removeObject(forKey: key)
        myDict.write(to: path, atomically: true)
    }

Để tương tác dễ hơn với plist bắt buộc chúng ta phải tạo ra các func để quản lý các file đó. Nhưng nếu các bạn không muốn làm những thứ lằng nhằng đó thì chúng ta có thể chuyển qua dùng một file Property List có sẵn mà Apple đã cung cấp đó là sử dụng UserDefault

UserDefault

UserDefault là một singleton class nhằm mục đích giúp các nhà phát triển có thể dễ dàng lưu dữ liệu vào file Plist hơn. Mặc định khi bạn sử dụng UserDefault nó sẽ tạo ra một file Plist trong dự án. Từ đó ta có thể thêm, sửa, xóa các key nằm trong file Plist đó dựa trên các hàm mà UserDefault cung cấp.

UserDefault tương tác trên Property List nên nó cũng phải tuân thủ theo quy tắc của file đó. Vì vậy thông thường chung ta chỉ có thể gán được các giá trị mà file Plist cho phép.

Để lưu/lấy giá trị bằng UserDefault ta làm như sau:

        let udf = UserDefaults.standard//Tạo instance
        udf.set("11", forKey: "1")// gán giá trị 11 cho key "1"
        udf.synchronize()//nó block thread đang gọi cho đến khi giá trị được gán hoàn tất
        print(udf.value(forKey: "1"))// lấy dữ liệu của key 1 và in ra console

Như vậy với UserDefault chúng ta có thể làm việc với file Plist một cách đơn giản. Tuy nhiên, đôi khi chúng ta cũng phải lưu lại kiểu dữ liệu mà Plist không hỗ trợ. Lúc này chúng ta cần phải encode nó về Data để có thể lưu được dạng này.

Lưu ý

Chúng ta không nên lưu những dữ liệu quá nặng trong Plist, vì file này sẽ tiêu tốn bộ nhớ RAM khi bạn chạy ứng dụng.

Lưu custom object

Tạo mới class

class Employee: NSObject, NSCoding {
    let name: String
    let dob: String
    // hàm khởi tạo
    init(name: String, dob: String) {
        self.name = name
        self.dob = dob
    }
    // init với NSCoder bước này để decode
    required convenience init(coder aDecoder: NSCoder) {
        let name = aDecoder.decodeObject(forKey: "name") as! String
        let dob = aDecoder.decodeObject(forKey: "dob") as! String
        self.init(name: name, dob: dob)
    }
    // hàm này để encode
    func encode(with aCoder: NSCoder) {
        aCoder.encode(name, forKey: "name")
        aCoder.encode(dob, forKey: "dob")
    }
}

Lưu Employee vào UserDefault sử dụng NSKeyedArchiver

        let employee = Employee(name: "Dino", dob: "19/09/1999")
        let udf = UserDefaults.standard
        do {
            let encodeData = try NSKeyedArchiver.archivedData(withRootObject: employee, requiringSecureCoding: false)
            udf.set(encodeData, forKey: "emp")
            udf.synchronize()
        } catch {
            print(error)
        }

Lấy ra Employee từ UserDefault

let decode = udf.data(forKey: "emp")
let decodeEmp = NSKeyedUnarchiver.unarchiveObject(with: decode!) as? Employee

Tổng kêt

Mình hi vọng bài viết này có thể giúp các bạn sử dụng Property List tốt hơn. Chúc các bạn thành công.

Leave a Comment

* By using this form you agree with the storage and handling of your data by this website.