Các chức năng ngoại tuyến trên các nền tảng của Apple

Ứng dụng Firebase hoạt động ngay cả khi ứng dụng của bạn tạm thời mất kết nối mạng. Ngoài ra, Firebase cung cấp các công cụ để duy trì dữ liệu cục bộ, quản lý sự hiện diện và xử lý độ trễ.

Khả năng lưu trữ cố định ổ đĩa

Các ứng dụng Firebase tự động xử lý các trường hợp gián đoạn mạng tạm thời. Bạn có thể sử dụng dữ liệu đã lưu vào bộ nhớ đệm khi không có kết nối mạng và Firebase sẽ gửi lại mọi lượt ghi khi kết nối mạng được khôi phục.

Khi bạn bật tính năng lưu trữ cố định ổ đĩa, ứng dụng sẽ ghi dữ liệu cục bộ vào thiết bị để ứng dụng có thể duy trì trạng thái khi không có mạng, ngay cả khi người dùng hoặc hệ điều hành khởi động lại ứng dụng.

Bạn có thể bật tính năng lưu trữ cố định ổ đĩa chỉ bằng một dòng mã.

Swift

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
Database.database().isPersistenceEnabled = true

Objective-C

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
[FIRDatabase database].persistenceEnabled = YES;

Hành vi liên tục

Khi bật tính năng lưu trữ cố định, mọi dữ liệu mà ứng dụng Cơ sở dữ liệu theo thời gian thực Firebase sẽ đồng bộ hoá khi có kết nối mạng sẽ tiếp tục được lưu vào ổ đĩa và có thể sử dụng khi không có mạng, ngay cả khi người dùng hoặc hệ điều hành khởi động lại ứng dụng. Điều này có nghĩa là ứng dụng của bạn vẫn hoạt động như hoạt động trực tuyến bằng cách sử dụng dữ liệu cục bộ được lưu trữ trong bộ nhớ đệm. Lệnh gọi lại trình nghe sẽ tiếp tục kích hoạt để cập nhật cục bộ.

Ứng dụng Cơ sở dữ liệu theo thời gian thực Firebase tự động lưu giữ một hàng đợi gồm tất cả thao tác ghi được thực hiện khi ứng dụng của bạn không có kết nối mạng. Khi bật tính năng lưu trữ cố định, hàng đợi này cũng được duy trì vào ổ đĩa để tất cả các lượt ghi của bạn sẽ có sẵn khi người dùng hoặc hệ điều hành khởi động lại ứng dụng. Khi ứng dụng kết nối lại, tất cả các hoạt động sẽ được gửi đến máy chủ Cơ sở dữ liệu theo thời gian thực của Firebase.

Nếu ứng dụng của bạn sử dụng tính năng Xác thực Firebase, thì ứng dụng Cơ sở dữ liệu theo thời gian thực Firebase sẽ duy trì mã xác thực của người dùng khi ứng dụng khởi động lại. Nếu mã thông báo xác thực hết hạn trong khi ứng dụng không kết nối mạng, thì ứng dụng sẽ tạm dừng các hoạt động ghi cho đến khi ứng dụng của bạn xác thực lại người dùng. Nếu không, các thao tác ghi có thể không thành công do quy tắc bảo mật.

Luôn cập nhật dữ liệu

Cơ sở dữ liệu theo thời gian thực của Firebase đồng bộ hoá và lưu trữ một bản sao cục bộ của dữ liệu cho các trình nghe đang hoạt động. Ngoài ra, bạn có thể đồng bộ hoá các vị trí cụ thể.

Swift

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
let scoresRef = Database.database().reference(withPath: "scores")
scoresRef.keepSynced(true)

Objective-C

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
FIRDatabaseReference *scoresRef = [[FIRDatabase database] referenceWithPath:@"scores"];
[scoresRef keepSynced:YES];

Ứng dụng Cơ sở dữ liệu theo thời gian thực Firebase sẽ tự động tải dữ liệu xuống tại các vị trí này và đồng bộ hoá dữ liệu ngay cả khi tệp đối chiếu không có trình nghe đang hoạt động. Bạn có thể tắt tính năng đồng bộ hoá bằng dòng mã sau.

Swift

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
scoresRef.keepSynced(false)

Objective-C

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
[scoresRef keepSynced:NO];

Theo mặc định, 10MB dữ liệu đã đồng bộ hoá trước đó sẽ được lưu vào bộ nhớ đệm. Mức này là đủ đối với hầu hết các ứng dụng. Nếu bộ nhớ đệm vượt quá dung lượng đã định cấu hình, thì Cơ sở dữ liệu theo thời gian thực của Firebase sẽ xoá hoàn toàn những dữ liệu được sử dụng gần đây nhất. Dữ liệu được đồng bộ hoá sẽ không bị xoá hoàn toàn khỏi bộ nhớ đệm.

Truy vấn dữ liệu ngoại tuyến

Cơ sở dữ liệu theo thời gian thực của Firebase lưu trữ dữ liệu được trả về từ một truy vấn để sử dụng khi không có mạng. Đối với những truy vấn được tạo khi không có kết nối mạng, Cơ sở dữ liệu theo thời gian thực của Firebase sẽ tiếp tục hoạt động đối với dữ liệu đã tải trước đó. Nếu dữ liệu bạn yêu cầu chưa tải, thì Cơ sở dữ liệu theo thời gian thực của Firebase sẽ tải dữ liệu từ bộ nhớ đệm cục bộ. Khi kết nối mạng hoạt động trở lại, dữ liệu sẽ tải và phản ánh truy vấn.

Ví dụ: đoạn mã này truy vấn 4 mục cuối cùng trong Cơ sở dữ liệu theo thời gian thực của Firebase cho điểm số

Swift

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
let scoresRef = Database.database().reference(withPath: "scores")
scoresRef.queryOrderedByValue().queryLimited(toLast: 4).observe(.childAdded) { snapshot in
  print("The \(snapshot.key) dinosaur's score is \(snapshot.value ?? "null")")
}

Objective-C

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
FIRDatabaseReference *scoresRef = [[FIRDatabase database] referenceWithPath:@"scores"];
[[[scoresRef queryOrderedByValue] queryLimitedToLast:4]
    observeEventType:FIRDataEventTypeChildAdded withBlock:^(FIRDataSnapshot *snapshot) {
      NSLog(@"The %@ dinosaur's score is %@", snapshot.key, snapshot.value);
    }];

Giả sử người dùng mất kết nối, chuyển sang chế độ ngoại tuyến và khởi động lại ứng dụng. Khi vẫn không có kết nối mạng, ứng dụng sẽ truy vấn 2 mục cuối cùng ở cùng một vị trí. Truy vấn này sẽ trả về thành công 2 mục cuối cùng vì ứng dụng đã tải cả 4 mục trong truy vấn trên.

Swift

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
scoresRef.queryOrderedByValue().queryLimited(toLast: 2).observe(.childAdded) { snapshot in
  print("The \(snapshot.key) dinosaur's score is \(snapshot.value ?? "null")")
}

Objective-C

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
[[[scoresRef queryOrderedByValue] queryLimitedToLast:2]
    observeEventType:FIRDataEventTypeChildAdded withBlock:^(FIRDataSnapshot *snapshot) {
      NSLog(@"The %@ dinosaur's score is %@", snapshot.key, snapshot.value);
    }];

Trong ví dụ trước, ứng dụng Cơ sở dữ liệu theo thời gian thực của Firebase đưa ra các sự kiện "đã thêm con" cho 2 con khủng long có điểm số cao nhất bằng cách sử dụng bộ nhớ đệm cố định. Tuy nhiên, thao tác này sẽ không làm tăng sự kiện "value" vì ứng dụng chưa từng thực thi truy vấn đó khi đang kết nối mạng.

Nếu yêu cầu 6 mục cuối cùng khi không có kết nối mạng, thì ứng dụng sẽ nhận được ngay các sự kiện "đã thêm con" cho 4 mục được lưu vào bộ nhớ đệm. Khi thiết bị có kết nối mạng trở lại, ứng dụng Cơ sở dữ liệu theo thời gian thực của Firebase sẽ đồng bộ hoá với máy chủ và nhận hai sự kiện "child added" (thêm con) cuối cùng và "value" cho ứng dụng.

Xử lý giao dịch ngoại tuyến

Mọi giao dịch được thực hiện khi ứng dụng không có kết nối mạng sẽ được đưa vào hàng đợi. Sau khi ứng dụng kết nối lại mạng, các giao dịch sẽ được gửi đến máy chủ Cơ sở dữ liệu theo thời gian thực.

Quản lý sự hiện diện

Trong các ứng dụng theo thời gian thực, việc phát hiện thời điểm ứng dụng kết nối và ngắt kết nối thường sẽ rất hữu ích. Ví dụ: bạn có thể muốn đánh dấu một người dùng là "đang ngoại tuyến" khi ứng dụng của họ ngắt kết nối.

Ứng dụng Cơ sở dữ liệu Firebase cung cấp các dữ liệu gốc đơn giản mà bạn có thể dùng để ghi vào cơ sở dữ liệu khi ứng dụng ngắt kết nối khỏi các máy chủ Cơ sở dữ liệu Firebase. Các quá trình cập nhật này diễn ra cho dù ứng dụng có ngắt kết nối một cách dễ dàng hay không. Vì vậy, bạn có thể dựa vào ứng dụng này để dọn dẹp dữ liệu ngay cả khi kết nối bị mất hoặc ứng dụng gặp sự cố. Mọi thao tác ghi, bao gồm cả thao tác đặt, cập nhật và xoá, đều có thể thực hiện khi ngắt kết nối.

Dưới đây là một ví dụ đơn giản về cách ghi dữ liệu khi ngắt kết nối bằng cách sử dụng dữ liệu gốc onDisconnect:

Swift

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
let presenceRef = Database.database().reference(withPath: "disconnectmessage");
// Write a string when this client loses connection
presenceRef.onDisconnectSetValue("I disconnected!")

Objective-C

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
FIRDatabaseReference *presenceRef = [[FIRDatabase database] referenceWithPath:@"disconnectmessage"];
// Write a string when this client loses connection
[presenceRef onDisconnectSetValue:@"I disconnected!"];

Cách tính năng onNgắt kết nối hoạt động

Khi bạn thiết lập một thao tác onDisconnect(), thao tác đó sẽ có trên máy chủ Cơ sở dữ liệu theo thời gian thực của Firebase. Máy chủ sẽ kiểm tra tính bảo mật để đảm bảo người dùng có thể thực hiện sự kiện ghi được yêu cầu và thông báo cho ứng dụng của bạn nếu sự kiện đó không hợp lệ. Sau đó, máy chủ sẽ giám sát kết nối. Nếu tại bất kỳ thời điểm nào kết nối hết thời gian kết nối hoặc bị ứng dụng Cơ sở dữ liệu theo thời gian thực chủ động đóng, thì máy chủ sẽ kiểm tra bảo mật lần thứ hai (để đảm bảo thao tác vẫn hợp lệ), sau đó gọi sự kiện.

Ứng dụng của bạn có thể dùng lệnh gọi lại trong thao tác ghi để đảm bảo onDisconnect đã được đính kèm chính xác:

Swift

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
presenceRef.onDisconnectRemoveValue { error, reference in
  if let error = error {
    print("Could not establish onDisconnect event: \(error)")
  }
}

Objective-C

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
[presenceRef onDisconnectRemoveValueWithCompletionBlock:^(NSError *error, FIRDatabaseReference *reference) {
  if (error != nil) {
    NSLog(@"Could not establish onDisconnect event: %@", error);
  }
}];

Bạn cũng có thể huỷ một sự kiện onDisconnect bằng cách gọi .cancel():

Swift

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
presenceRef.onDisconnectSetValue("I disconnected")
// some time later when we change our minds
presenceRef.cancelDisconnectOperations()

Objective-C

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
[presenceRef onDisconnectSetValue:@"I disconnected"];
// some time later when we change our minds
[presenceRef cancelDisconnectOperations];

Phát hiện trạng thái kết nối

Đối với nhiều tính năng liên quan đến sự hiện diện, ứng dụng của bạn cần biết khi nào ứng dụng trực tuyến hoặc ngoại tuyến. Cơ sở dữ liệu theo thời gian thực của Firebase cung cấp một vị trí đặc biệt tại /.info/connected và được cập nhật mỗi khi trạng thái kết nối của ứng dụng Cơ sở dữ liệu theo thời gian thực Firebase thay đổi. Bạn có thể tham khảo ví dụ sau đây:

Swift

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
let connectedRef = Database.database().reference(withPath: ".info/connected")
connectedRef.observe(.value, with: { snapshot in
  if snapshot.value as? Bool ?? false {
    print("Connected")
  } else {
    print("Not connected")
  }
})

Objective-C

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
FIRDatabaseReference *connectedRef = [[FIRDatabase database] referenceWithPath:@".info/connected"];
[connectedRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
  if([snapshot.value boolValue]) {
    NSLog(@"connected");
  } else {
    NSLog(@"not connected");
  }
}];

/.info/connected là một giá trị boolean không được đồng bộ hoá giữa các ứng dụng Cơ sở dữ liệu theo thời gian thực vì giá trị này phụ thuộc vào trạng thái của ứng dụng. Nói cách khác, nếu một ứng dụng đọc /.info/connected là false, thì điều này không đảm bảo rằng một ứng dụng riêng biệt cũng sẽ đọc giá trị false.

Độ trễ xử lý

Dấu thời gian của máy chủ

Các máy chủ Cơ sở dữ liệu theo thời gian thực Firebase cung cấp một cơ chế để chèn dấu thời gian được tạo trên máy chủ dưới dạng dữ liệu. Tính năng này (kết hợp với onDisconnect) giúp bạn dễ dàng ghi lại một cách đáng tin cậy thời điểm ứng dụng Cơ sở dữ liệu theo thời gian thực ngắt kết nối:

Swift

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
let userLastOnlineRef = Database.database().reference(withPath: "users/morgan/lastOnline")
userLastOnlineRef.onDisconnectSetValue(ServerValue.timestamp())

Objective-C

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
FIRDatabaseReference *userLastOnlineRef = [[FIRDatabase database] referenceWithPath:@"users/morgan/lastOnline"];
[userLastOnlineRef onDisconnectSetValue:[FIRServerValue timestamp]];

Mặt đồng hồ

Mặc dù firebase.database.ServerValue.TIMESTAMP chính xác hơn và phù hợp hơn cho hầu hết các thao tác đọc/ghi, nhưng đôi khi cũng có thể hữu ích khi ước tính độ lệch đồng hồ của ứng dụng đối với các máy chủ của Cơ sở dữ liệu theo thời gian thực Firebase. Bạn có thể đính kèm lệnh gọi lại vào vị trí /.info/serverTimeOffset để lấy giá trị (tính bằng mili giây) mà các ứng dụng Cơ sở dữ liệu theo thời gian thực của Firebase thêm vào thời gian báo cáo cục bộ (thời gian bắt đầu của hệ thống tính bằng mili giây) để ước tính thời gian của máy chủ. Xin lưu ý rằng độ chính xác của độ lệch này có thể chịu ảnh hưởng của độ trễ kết nối mạng. Vì vậy, tính năng này chủ yếu hữu ích khi phát hiện sự chênh lệch lớn (> 1 giây) về thời gian đồng hồ.

Swift

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
let offsetRef = Database.database().reference(withPath: ".info/serverTimeOffset")
offsetRef.observe(.value, with: { snapshot in
  if let offset = snapshot.value as? TimeInterval {
    print("Estimated server time in milliseconds: \(Date().timeIntervalSince1970 * 1000 + offset)")
  }
})

Objective-C

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
FIRDatabaseReference *offsetRef = [[FIRDatabase database] referenceWithPath:@".info/serverTimeOffset"];
[offsetRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
  NSTimeInterval offset = [(NSNumber *)snapshot.value doubleValue];
  NSTimeInterval estimatedServerTimeMs = [[NSDate date] timeIntervalSince1970] * 1000.0 + offset;
  NSLog(@"Estimated server time: %0.3f", estimatedServerTimeMs);
}];

Ứng dụng mẫu hiện diện

Bằng cách kết hợp các thao tác ngắt kết nối với tính năng theo dõi trạng thái kết nối và dấu thời gian của máy chủ, bạn có thể xây dựng một hệ thống hiện diện của người dùng. Trong hệ thống này, mỗi người dùng lưu trữ dữ liệu tại một vị trí cơ sở dữ liệu để cho biết ứng dụng Cơ sở dữ liệu theo thời gian thực có trực tuyến hay không. Các ứng dụng đặt vị trí này thành true khi có kết nối mạng và dấu thời gian khi ngắt kết nối. Dấu thời gian này cho biết lần gần đây nhất một người dùng cụ thể có kết nối mạng.

Lưu ý rằng ứng dụng của bạn nên đưa các thao tác ngắt kết nối vào hàng đợi trước khi người dùng được đánh dấu là trực tuyến để tránh mọi tình huống tương tranh trong trường hợp ứng dụng mất kết nối mạng trước khi có thể gửi cả hai lệnh đến máy chủ.

Dưới đây là một hệ thống đơn giản giúp xác định sự hiện diện của người dùng:

Swift

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
// since I can connect from multiple devices, we store each connection instance separately
// any time that connectionsRef's value is null (i.e. has no children) I am offline
let myConnectionsRef = Database.database().reference(withPath: "users/morgan/connections")

// stores the timestamp of my last disconnect (the last time I was seen online)
let lastOnlineRef = Database.database().reference(withPath: "users/morgan/lastOnline")

let connectedRef = Database.database().reference(withPath: ".info/connected")

connectedRef.observe(.value, with: { snapshot in
  // only handle connection established (or I've reconnected after a loss of connection)
  guard snapshot.value as? Bool ?? false else { return }

  // add this device to my connections list
  let con = myConnectionsRef.childByAutoId()

  // when this device disconnects, remove it.
  con.onDisconnectRemoveValue()

  // The onDisconnect() call is before the call to set() itself. This is to avoid a race condition
  // where you set the user's presence to true and the client disconnects before the
  // onDisconnect() operation takes effect, leaving a ghost user.

  // this value could contain info about the device or a timestamp instead of just true
  con.setValue(true)

  // when I disconnect, update the last time I was seen online
  lastOnlineRef.onDisconnectSetValue(ServerValue.timestamp())
})

Objective-C

Lưu ý: Sản phẩm Firebase này không dùng được trên mục tiêu App Clip (Đoạn video ứng dụng).
// since I can connect from multiple devices, we store each connection instance separately
// any time that connectionsRef's value is null (i.e. has no children) I am offline
FIRDatabaseReference *myConnectionsRef = [[FIRDatabase database] referenceWithPath:@"users/morgan/connections"];

// stores the timestamp of my last disconnect (the last time I was seen online)
FIRDatabaseReference *lastOnlineRef = [[FIRDatabase database] referenceWithPath:@"users/morgan/lastOnline"];

FIRDatabaseReference *connectedRef = [[FIRDatabase database] referenceWithPath:@".info/connected"];
[connectedRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
  if([snapshot.value boolValue]) {
    // connection established (or I've reconnected after a loss of connection)

    // add this device to my connections list
    FIRDatabaseReference *con = [myConnectionsRef childByAutoId];

    // when this device disconnects, remove it
    [con onDisconnectRemoveValue];

    // The onDisconnect() call is before the call to set() itself. This is to avoid a race condition
    // where you set the user's presence to true and the client disconnects before the
    // onDisconnect() operation takes effect, leaving a ghost user.

    // this value could contain info about the device or a timestamp instead of just true
    [con setValue:@YES];


    // when I disconnect, update the last time I was seen online
    [lastOnlineRef onDisconnectSetValue:[FIRServerValue timestamp]];
  }
}];