Chúng ta hãy cùng nhớ lại ví dụ bài trước để hiểu làm thế nào chúng ta đọc dữ liệu từ một Firebase database. Để đọc được các bài viết đã post, chúng ta có thể làm như sau:

OBJ-C

// Url là đường dẫn của database của chúng ta

Firebase *ref = [[Firebase alloc] initWithUrl: @"https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts"];

// Chúng ta phải truyền vào Event và một block, block này sẽ được callback khi dữ liệu thay đổi

[ref observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {

    NSLog(@"%@", snapshot.value);

} withCancelBlock:^(NSError *error) {

    NSLog(@"%@", error.description);

}];

SWIFT

// Url là đường dẫn của database của chúng ta

var ref = Firebase(url:"https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts")

// Chúng ta phải truyền vào Event và một block, block này sẽ được callback khi dữ liệu thay đổi

ref.observeEventType(.Value, withBlock: { snapshot in

    print(snapshot.value)

}, withCancelBlock: { error in

    print(error.description)

})

Nếu chúng ta chạy đoạn mã này, chúng ta sẽ thấy một đối tượng chứa tất cả các bài mà chúng ta đã post sau đó in ra ở console log.Cái block trả về sẽ được gọi bất kỳ lúc nào dữ liệu được thêm vào database mà chúng ta ánh xa đến.

Function callback này sẽ trả lại một biến có kiểu FDataSnapshot,  nó là miêu ta cho dữ liệu của database. Việc truy xuất giá trị của snapshot trả về dữ liệu là kiểu id. Nếu không có dữ liệu thì giá trị sẽ là NSNull.

Điều cần chú ý là chúng ta phải chỉ rõ ra sự kiên FEventType. Chúng ta sử dụng sự kiện Value trong ứng dụng trên, khi sử dụng sự kiện này nó sẽ đọc toàn bộ nội dung của một Firebase database.Với FeventType nó là kiểu enum gốm 5 giá trị.

Các kiểu sự kiện

Value:Sự kiện Value được sử dụng để đọc một snapshot tính của nội dung tại một đường dẫn, như việc chúng tồn tại ở một thời điểm khi sự kiện đọc xảy ra. Nó được kích hoạt ngay lập tức với dữ liệu được khởi tạo và lặp lại quá trình khởi tạo mỗi khi dữ liệu thay đổi. Sự kiện này  gọi lại và được truyền cho một snapshot chưa tất cả dữ liệu tại vị trí đó, bao gồm các nhánh con. Trong đoạn code ví dụ trên việc quan sát kiểu sự kiện là Value trả về tất cả các blog đã post trong ứng dụng. Mỗi khi một blog mới được post được thêm, function sẽ trả về tất cả các bài viết.

Child Added:Sự kiện Child Added là một kiểu đặc biệt được sử dụng khi muốn lấy một danh sách của các item trong Firebase database của bạn. Không giống như Value nó trả về toàn bộ nội dung tại đường dẫn đó, với ChildAdded nó được kích hoạt ngay lập tức cho mỗi nhánh con và làm lại điều đó khi có một nhánh con được thêm vào đường dẫn đó. Sự kiện callback này sẽ trả về một snapshot chứa dữ liệu của nhánh con.

Nếu chúng ta muôn chỉ nhận dữ liệu của các bài blog không bao gồm các trường ngoài(như là key của nhánh con), chúng ta có thể sử dụng ChildAdded:

OBJ-C


Firebase *ref = [[Firebase alloc] initWithUrl: @"https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts"];

// Nhận những bài viết mới khi chúng được thêm vào database

[ref observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {

  NSLog(@"%@", snapshot.value[@"author"]);

  NSLog(@"%@", snapshot.value[@"title"]);

}];

SWIFT

// Get a reference to our posts

var ref = Firebase(url:"https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts")

// Nhận những bài viết mới khi chúng được thêm vào database

ref.observeEventType(.ChildAdded, withBlock: { snapshot in

    print(snapshot.value.objectForKey("author"))

    print(snapshot.value.objectForKey("title"))

})

Trong ví dụ này snapshot sẽ chưa một đôi tượng là blog. Chúng ta có thể truy vấn thuộc tính "author" của bài blog đó hoặc là "title" bằng cách gọi thuộc tính value và truyền vào các key “author” và  “title”.

Child Changed:Sự kiện ChildChanged được kích hoạt mỗi khi một nhánh con được chỉnh sửa và bao gồm các thành phần ở trong nhanh con đó. Nó thường được kết hợp với ChildAdded và ChildRemoved để trả về mỗi khi có sử hay đổi của một danh sách các item. Biến snapshot được trả về khi callback chưa dữ liệu đã cập nhật của nhánh con.

OBJ-C

Firebase *ref = [[Firebase alloc] initWithUrl: @"https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts"];
//Lấy dữ liệu từ dữ liệu vừa thay đổi(ví dụ khi thay đổi bài blog thì nó sẽ trả về dữ liệu bài blog đó) 

[ref observeEventType:FEventTypeChildChanged withBlock:^(FDataSnapshot *snapshot) {

  NSLog(@"The updated post title is %@", snapshot.value[@"title"]);

}];

SWIFT

var ref = Firebase(url:"https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts")

//Lấy dữ liệu từ dữ liệu vừa thay đổi(ví dụ khi thay đổi bài blog thì nó sẽ trả về dữ liệu bài blog đó) 

ref.observeEventType(.ChildChanged, withBlock: { snapshot in

    let title = snapshot.value.objectForKey("title") as? String

    print("The updated post title is \(title)")

})

Child Removed:Sự kiện ChildRemoved được kích hoạt khi một nhánh con bị xoá. Nó thường được sử dụng kết hợp với các sự kiện ChildAdded và ChildChanged. Biến snapshot được trả về chứa dữ liệu của nhanh con bị xoá.

Child Moved:Sự kiện ChildMove được sử dụng khi làm việc với dữ liệu được sắp xếp, chúng ta sẽ tham khảo ở phần tiếp theo.

 

Event Guarantees

Một số cách quy tắc quan trọng khi sử dụng các sự kiện

  • Các sự kiện luôn luôn được kích hoạt khi trạng thái cục bộ thay đổi.
  • Các sự kiện sẽ luôn luôn phản ánh đúng trạng thái của dữ liệu thận trí bạn gặp một số các vấn đề về mạng...
  • Việc ghi từ một client sẽ luôn luôn được ghi tới máy chủ và những người dùng khác sẽ được ghi theo trật tự.
  • Sự kiện FEventTypeValue luôn luôn được kích hoạt  sau để được đảm bảo có dữ liệu cập nhật từ  mọi sự kiện khác trước khi snapshot được lấy.

Ví dụ dưới FEventTypeValue sẽ luôn được kích hoạt sau khi FeventTypeChildAdded được kích hoạt.

OBJ-C

Firebase *ref = [[Firebase alloc] initWithUrl: @"https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts"];

__block NSInteger count = 0;

[ref observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {

    count++;

    NSLog(@"added -> %@", snapshot.value);

}];

[ref observeSingleEventOfType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {

    NSLog(@"initial data loaded! %d", count == snapshot.childrenCount);

}];

SWIFT

var ref = Firebase(url:"https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts")

var count:UInt = 0

ref.observeEventType(.ChildAdded, withBlock: { snapshot in

    count++

    print("added -> \(snapshot.value)")

})
ref.observeEventType(.Value, withBlock: { snapshot in

  print("initial data loaded! \(count == snapshot.childrenCount)")

})

 

Detaching Block

Trường hợp chúng ta đóng một ViewController không có nghĩa là observers dừng lại việc đồng bộ hoá dữ liệu. Nếu một observer không được xoá nó sẽ tiếp tục đồng bộ hoá dữ liệu ở mộ nhớ cục bộ. Khi một observer không cần nó sẽ được xoá bằng method removeObserverWithHandle(Khi bạn khai báo một observer để lắng nghe dữ liệu kể cả khi bạn sang view khác nếu dữ liệu trên database của bạn thay đổi nó vẫn sẽ callback lại nó sẽ gây ra hiện tượng unrecognized selector ).

Khi thêm một callback block đến một tham chiếu một biến FirebaseHandle sẽ được trả về chúng ta có thể sử dụng chúng để xoá.

OBJ-C

FirebaseHandle handle = [ref observeEventType:FEventTypeValue withBlock:^(FDatasnapshot* snapshot) {

    NSLog(@"Snapshot value: %@", snapshot.value)

}];

[ref removeObserverWithHandle:handle];

SWIFT

var handle = ref.observeEventType(.Value, withBlock: { snapshot in

    print("Snapshot value: \(snapshot.value)")

})

ref.removeObserverWithHandle(handle)

Nếu bạn muốn xoá toàn bộ callbacks tại lớp hiện tại bạn vị trí tham chiếu hiện tại bạn có thể sử dụng:

OBJ-C

[ref removeAllObservers];

SWIFT

ref.removeAllObservers()

Reading Data Once

Trong một số trường hợp nếu bạn chỉ muốn đọc dữ liệu một lần thì phương thức dưới đây sẽ làm điều đó đơn giản hơn thay vì việc cứ phải tạo ra rồi remove luôn.

OBJ-C

[ref observeSingleEventOfType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {

    // do some stuff once

}];

SWIFT

ref.observeSingleEventOfType(.Value, withBlock: { snapshot in

    // do some stuff once

})

Checking for NSNull

Nếu không tồn tại dữ liệu tại đường dẫn, FDataSnapshot sẽ trả về NSNull Chúng ta có thể kiểm tra NSNull bằng cú pháp sau:

OBJ-C

[ref observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {

 if (snapshot.value == [NSNull null]) {

    // The value is null

  }

}];

SWIFT

ref.observeEventType(.Value, withBlock: { snap in

if snap.value is NSNull {

    // The value is null

  }

})

Querying Data

Chúng ta có thể nhận dữa liệu bằng nhiều cách sử dụng các truy vấn khác nhau. Để tạo một truy vấn, bạn phải xác định được dữ liệu bạn muốn lấy và sau đó sử dụng một trong các ordering funcstion sau: queryOrderedByChild:, queryOrderedByKey:, queryOrderedByvalue, hoặc queryOrderedBypriority: . Bạn có thể kết hợp các 5 method sau để tạo thành những query phức tạp hơn: queryLimitedToFirst:, queryLimitedToLast:, queryStartingAtValue:, queryEndingAtValue:, và queryEqualToValue:.

Chúng ta sẽ sử dụng dữ liệu dưới đây để tạo các truy vấn:

https://dinosaur-facts.firebaseio.com/dinosaurs

Ordering using a child key

Chúng ta có thể sắp xếp nhánh bằng một key có có trong nhánh con đó và điền key đó vào queryOrderedByChild: . Ví dụ để đọc tất cả các con khủng long được sắp xêm bới cân nặng, chúng ta làm như sau:

OBJ-C

Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"];

[[ref queryOrderedByChild:@"height"]

    observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
    NSLog(@"%@ was %@ meters tall", snapshot.key, snapshot.value[@"height"]);

}];

SWIFT

let ref = Firebase(url:"https://dinosaur-facts.firebaseio.com/dinosaurs")

ref.queryOrderedByChild("height").observeEventType(.ChildAdded, withBlock: { snapshot in

    if let height = snapshot.value["height"] as? Double {

        print("\(snapshot.key) was \(height) meters tall")

    }

})

Mọi nhánh mà không chứa key mà chúng ta search cũng như có giá trị NULL thì sẽ được đưa lên đầu.

Hơn nữa các truy vấn có thế được sắp xếp bằng các phần tử của nhánh con, hơn là chỉ sắp xếp ở chính nhánh con đó. Nó thật hữu dụng nếu bạn có dữ liệu lồng nhau như ở dưới đây:

{

  "lambeosaurus": {

    "dimensions": {

      "height" : 2.1,

      "length" : 12.5,

      "weight": 5000

    }

  },

  "stegosaurus": {

    "dimensions": {

      "height" : 4,

      "length" : 9,

      "weight" : 2500

    }

  }

}

Để truy vấn height chúng ta sử dụng đường dẫn đầy đủ để chỉ đến trường đó thay vì dùng một key như bình thường:

OBJ-C

Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"];

[[ref queryOrderedByChild:@"dimensions/height"]

    observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
    NSLog(@"%@ was %@ meters tall", snapshot.key, snapshot.value[@"height"]);

}];

SWIFT

let ref = Firebase(url:"https://dinosaur-facts.firebaseio.com/dinosaurs")

ref.queryOrderedByChild("dimensions/height").observeEventType(.ChildAdded, withBlock: { snapshot in

    if let height = snapshot.value["height"] as? Double {

        print("\(snapshot.key) was \(height) meters tall")

    }

})

Các truy vấn có thể chỉ sắp xếp bới một key trong cùng một thời điểm. Việc gọi queryOrderedByChild: nhiều lần trong cùng một thời gian có thể gây ra lỗi các bạn có thể đọc thêm ở đây:Read the documentation

Ordering by key name

Hơn nữa chúng ta có thể sắp xếp các nhanh bởi các key sử dụng queryOrderedByKey: , ở ví dụ dưới đây chúng ta sẽ đọc tất cả các con khủng long theo bảng chữ cái:

OBJ-C

Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"];

[[ref queryOrderedByKey]

    observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {

    NSLog(@"%@ was %@", snapshot.key, snapshot.value[@"height"]);

}];

SWIFT

let ref = Firebase(url:"https://dinosaur-facts.firebaseio.com/dinosaurs")

ref.queryOrderedByKey().observeEventType(.ChildAdded, withBlock: { snapshot in

    if let height = snapshot.value["height"] as? Double {

        print("\(snapshot.key) was \(height)")

    }

})

Ordering by value

Chúng ta có thể sắp xếp các nhánh bằng giá trị của các key sử dụng queryOrderedByValue.

OBJ-C

Firebase *scoresRef = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/scores"];

[[scoresRef queryOrderedByValue] observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {

    NSLog(@"The %@ dinosaur's score is %@", snapshot.key, snapshot.value);

}];

SWIFT

let scoresRef = Firebase(url:"https://dinosaur-facts.firebaseio.com/scores")

scoresRef.queryOrderedByValue().observeEventType(.ChildAdded, withBlock: { snapshot in

    if let score = snapshot.value as? Int {

        print("The \(snapshot.key) dinosaur's score is \(score)")

    }

})

Ordering by priority

Chúng ta có thế sắp xếp nhánh theo độ ưu tiên bằng cách gọi queryOrderByPriority. Kết quả sẽ là những đối tượng có mức độ ưu tiên thấp sẽ ở trên đầu.

Complex Queries

Limit Queries

Với queryLimitedToFirst: và queryLimitedTolast: được sử dụng để set một số nguyên của con để đồng bộ khi callback. Ví dụ nếu chúng ta set queryLimitedToFirst:2 thì nó sẽ lấy 2 dòng đầu tiên nghĩa là lấy 2 giá trị min ngược lại nếu queryLimitedToLast:2 thì nó sẽ lấy 2 dòng cuối cùng nghĩa là lấy 2 đối tượng max. Cả 2 methods này chỉ gọi it hơn số lần mà chúng ta set khi dữ liệu trên database là không đủ ví dụ ta có 5 đối tượng mà ta set nó là 10 thì nó chỉ chạy 5 lần thôi. Hơn nữa cả 2 method này sẽ được gọi lại khi mà chúng ta thêm hoặc xoá dữ liệu từ database và sẽ sắp xếp lại.

Range Queries

Sử dụng queryStartingAtValue:, queryEndingAtValue:, và queryEqualToValue: cho phép chúng ta tuỳ chọn bắt đầu và điểm kết thúc của các query. Ví dụ, nếu chúng ta muốn tìm tât cả các con khủng long tôi thiểu cao 3m, chúng ta có thể viết đoạn đã như sau:

OBJ-C

Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"];

[[[ref queryOrderedByChild:@"height"] queryStartingAtValue:@3]

    observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {

    NSLog(@"%@", snapshot.key);

}];

SWIFT

let ref = Firebase(url:"https://dinosaur-facts.firebaseio.com/dinosaurs")

ref.queryOrderedByChild("height").queryStartingAtValue(3)

   .observeEventType(.ChildAdded, withBlock: { snapshot in

   print(snapshot.key)

})

Chúng ta có thể sử dụng queryEndingAtValue: để tìm tất cả các con khủng long ở trước “pterodacatyl” theo thứ tự được điền vào:

OBJ-C

Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"];

[[[ref queryOrderedByKey] queryEndingAtValue:@"pterodactyl"]

    observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
    NSLog(@"%@", snapshot.key);

}];

SWIFT

let ref = Firebase(url:"https://dinosaur-facts.firebaseio.com/dinosaurs")

ref.queryOrderedByKey().queryEndingAtValue("pterodactyl")

   .observeEventType(.ChildAdded, withBlock: { snapshot in
    print(snapshot.key)

})

Chúng ta cũng có thể kết hợp queryStartAtValue: và queryEndingAtValue: để query theo khoảng nào đó. Ở ví dụ dưới đây sẽ tìm tất cả các con khủng long có tên bắt đầu là chứ “b”:

OBJ-C

Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"];

[[[[ref queryOrderedByKey] queryStartingAtValue:@"b"] queryEndingAtValue:@"b\uf8ff"]

    observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {

    NSLog(@"%@", snapshot.key);

}];

SWIFT

let ref = Firebase(url:"https://dinosaur-facts.firebaseio.com/dinosaurs")

ref.queryOrderedByKey().queryStartingAtValue("b").queryEndingAtValue("b\u{f8ff}")

   .observeEventType(.ChildAdded, withBlock: { snapshot in

   print(snapshot.key)

})

Ở ví dụ trên các bạn có thể thấy có các ký tự f8ff nó là một khoảng ở trong Unicode. Bởi nó đứng sau tất cả các ký tự trong bảng Unicode nên truy vấn chúng ta sẽ trả về tất cả giá trị a và b nhưng ta lại có query bắt đầu bằng ký tự “b” nên chỉ trả về những con khủng long bắt đầu là chữ “b”.

Với method queryEqualToValue:  cho phép chúng ta sử dụg để tìm kiếm chính xác, như với trường hợp query theo khoảng, nó sẽ tìm mỗi nhánh con rồi sau đó tìm tới giá trị phù hợp với điều kiện. Ví dụ chúng ta có thể sử dụng để tim tất cả các con khủng long cao 25m:

OBJ-C

Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"];

[[[ref queryOrderedByChild:@"height"] queryEqualToValue:@25]

    observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
    NSLog(@"%@", snapshot.key);

}];

SWIFT

let ref = Firebase(url:"https://dinosaur-facts.firebaseio.com/dinosaurs")

ref.queryOrderedByChild("height").queryEqualToValue(25)

   .observeEventType(.ChildAdded, withBlock: { snapshot in
   print(snapshot.key)

})

 

Putting it all together

Chúng ta có thể kết hợp tất cả những ký thuật ở trên để tạo những query phức tạp hơn. Ví dụ, chúng ta có thể tìm tên của con khủng long mà thấp hơn Stegosaurus:

OBJ-C

Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"];

[[[ref childByAppendingPath:@"stegosaurus"] childByAppendingPath:@"height"]

 observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *stegosaurusHeightSnapshot) {
 
//Lấy ra chiều cao stegosaurus

NSNumber *favoriteDinoHeight = stegosaurusHeightSnapshot.value;

//nếu có giá trị thì sẽ lấy ra chiều cao nhỏ hơn chiều cao vừa lấy

     FQuery *queryRef = [[[ref queryOrderedByChild:@"height"] queryEndingAtValue:favoriteDinoHeight] queryLimitedToLast:2];

     [queryRef observeSingleEventOfType:FEventTypeValue withBlock:^(FDataSnapshot *querySnapshot) {

//kiểm tra nếu có 2 giá trị nghĩa là có khủng long thấp hơn

         if (querySnapshot.childrenCount == 2) {

             for (FDataSnapshot* child in querySnapshot.children) {

                 NSLog(@"The dinosaur just shorter than the stegasaurus is %@", child.key);
                 break;
             }

         } else {

             NSLog(@"The stegosaurus is the shortest dino");

         }

     }];

 }];

SWIFT

let ref = Firebase(url:"https://dinosaur-facts.firebaseio.com/dinosaurs")

ref.childByAppendingPath("stegosaurus").childByAppendingPath("height")

    .observeEventType(.Value, withBlock: { stegosaurusHeightSnapshot in

      //Lấy ra chiều cao stegosaurus

        if let favoriteDinoHeight = stegosaurusHeightSnapshot.value as? Double {

//nếu có giá trị thì sẽ lấy ra chiều cao nhỏ hơn chiều cao vừa lấy được  2 giá trị trên cùng

            let queryRef = ref.queryOrderedByChild("height").queryEndingAtValue(favoriteDinoHeight).queryLimitedToLast(2)

            queryRef.observeSingleEventOfType(.Value, withBlock: { querySnapshot in

//kiểm tra nếu có 2 giá trị nghĩa là có khủng long thấp hơn

                if querySnapshot.childrenCount == 2 {

                    let child: FDataSnapshot = querySnapshot.children.nextObject() as FDataSnapshot

                    println("The dinosaur just shorter than the stegasaurus is \(child.key)");

                } else {

                    println("The stegosaurus is the shortest dino");

                }

            })

        }

    })

Nguồn bài viết:https://www.firebase.com

Demo ứng dụng chơi cờ online sử dụng Firebase:

Tham khảo:

Hiện nay tại TechMaster đang có 3 khóa học về lập trình iOS, một khóa dạy bằng ngôn ngữ Objective-C, một khóa bằng ngôn ngữ Swift và một khoá Swift - Kết nối REST Web Service. Hình thức học tập rất linh hoạt cho bạn lựa chọn và sẽ có mức học phí khác nhau tùy theo bạn chọn học online, offline hoặc FlipLearning (kết hợp giữa online và offline). Ngoài ra bạn cũng có thể tham gia thực tập toàn thời gian tại TechMaster để rút ngắn thời gian học và nhanh chóng đi kiếm việc làm.