Khả năng ngoại tuyến trên nền tảng Apple

Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.

Các ứ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ụ để lưu trữ dữ liệu cục bộ, quản lý sự hiện diện và xử lý độ trễ.

Độ bền của đĩa

Ứng dụng Firebase tự động xử lý các gián đoạn mạng tạm thời. Dữ liệu được lưu trong bộ nhớ cache khả dụng khi ngoại tuyến và Firebase gửi lại mọi lần ghi khi kết nối mạng được khôi phục.

Khi bạn bật tính năng duy trì ổ đĩa, ứng dụng của bạn sẽ ghi dữ liệu cục bộ vào thiết bị để ứng dụng của bạn có thể duy trì trạng thái khi ngoại tuyến, 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ể kích hoạt tính bền bỉ của ổ đĩa chỉ với một dòng mã.

Nhanh

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

Objective-C

Lưu ý: Sản phẩm Firebase này không khả dụng trên mục tiêu Clip ứng dụng.
[FIRDatabase database].persistenceEnabled = YES;

Hành vi bền bỉ

Bằng cách bật tính năng duy trì, mọi dữ liệu mà ứng dụng Cơ sở dữ liệu thời gian thực Firebase sẽ đồng bộ hóa trong khi trực tuyến vẫn tiếp tục vào đĩa và khả dụng ngoại tuyến, 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 hoạt động như khi trực tuyến bằng cách sử dụng dữ liệu cục bộ được lưu trữ trong bộ nhớ cache. Các cuộc gọi lại của người nghe sẽ tiếp tục kích hoạt đối với các bản cập nhật cục bộ.

Ứng dụng khách Cơ sở dữ liệu thời gian thực của Firebase tự động giữ một hàng đợi tất cả các thao tác ghi được thực hiện trong khi ứng dụng của bạn ngoại tuyến. Khi tính năng duy trì được bật, hàng đợi này cũng được lưu vào đĩa để tất cả các lần ghi của bạn đều 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 khôi phục lại kết nối, tất cả các hoạt động sẽ được gửi đến máy chủ Cơ sở dữ liệu thời gian thực của Firebase.

Nếu ứng dụng của bạn sử dụng Xác thực Firebase , ứng dụng khách Cơ sở dữ liệu thời gian thực của Firebase sẽ duy trì mã xác thực của người dùng qua các lần khởi động lại ứng dụng. Nếu mã thông báo xác thực hết hạn trong khi ứng dụng của bạn ngoại tuyến, ứ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 hoạt động ghi có thể không thành công do các quy tắc bảo mật.

Giữ dữ liệu mới

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

Nhanh

Lưu ý: Sản phẩm Firebase này không khả dụng trên mục tiêu Clip ứ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 khả dụng trên mục tiêu Clip ứng dụng.
FIRDatabaseReference *scoresRef = [[FIRDatabase database] referenceWithPath:@"scores"];
[scoresRef keepSynced:YES];

Ứng dụng khách Cơ sở dữ liệu thời gian thực của Firebase tự động tải xuống dữ liệu tại các vị trí này và giữ cho dữ liệu được đồng bộ hóa ngay cả khi tham chiếu không có trình xử lý đang hoạt động. Bạn có thể tắt đồng bộ hóa lại bằng dòng mã sau.

Nhanh

Lưu ý: Sản phẩm Firebase này không khả dụng trên mục tiêu Clip ứng dụng.
scoresRef.keepSynced(false)

Objective-C

Lưu ý: Sản phẩm Firebase này không khả dụng trên mục tiêu Clip ứng dụng.
[scoresRef keepSynced:NO];

Theo mặc định, 10MB dữ liệu đã đồng bộ hóa trước đó được lưu vào bộ nhớ đệm. Điều này là đủ cho hầu hết các ứng dụng. Nếu bộ nhớ cache lớn hơn kích thước đã định cấu hình, thì Cơ sở dữ liệu thời gian thực của Firebase sẽ xóa dữ liệu được sử dụng ít nhất gần đây. Dữ liệu được giữ đồng bộ sẽ không bị xóa khỏi bộ nhớ cache.

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

Cơ sở dữ liệu 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 ngoại tuyến. Đối với các truy vấn được tạo khi ngoại tuyến, Cơ sở dữ liệu thời gian thực của Firebase tiếp tục hoạt động đối với dữ liệu đã tải trước đó. Nếu dữ liệu được yêu cầu chưa tải, thì Cơ sở dữ liệu thời gian thực của Firebase sẽ tải dữ liệu từ bộ nhớ cache cục bộ. Khi kết nối mạng khả dụng trở lại, dữ liệu sẽ tải và sẽ phản ánh truy vấn.

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

Nhanh

Lưu ý: Sản phẩm Firebase này không khả dụng trên mục tiêu Clip ứ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 khả dụng trên mục tiêu Clip ứ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ử rằng 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 ngoại tuyến, ứng dụng sẽ truy vấn hai mục cuối cùng từ cùng một vị trí. Truy vấn này sẽ trả về thành công hai mục cuối cùng vì ứng dụng đã tải tất cả bốn mục trong truy vấn ở trên.

Nhanh

Lưu ý: Sản phẩm Firebase này không khả dụng trên mục tiêu Clip ứ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 khả dụng trên mục tiêu Clip ứ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 khách Cơ sở dữ liệu thời gian thực của Firebase tăng các sự kiện 'con được thêm vào' cho hai con khủng long có điểm cao nhất bằng cách sử dụng bộ nhớ cache liên tục. Nhưng nó sẽ không tăng sự kiện 'giá trị', vì ứng dụng chưa bao giờ thực hiện truy vấn đó khi trực tuyến.

Nếu ứng dụng yêu cầu sáu mục cuối cùng khi ngoại tuyến, thì ứng dụng sẽ nhận được các sự kiện 'thêm con' cho bốn mục được lưu trong bộ nhớ cache ngay lập tức. Khi thiết bị trực tuyến trở lại, ứng dụng khách Cơ sở dữ liệu thời gian thực của Firebase sẽ đồng bộ hóa với máy chủ và nhận hai sự kiện 'con thêm' và 'giá trị' cuối cùng cho ứng dụng.

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

Mọi giao dịch được thực hiện trong khi ứng dụng ngoại tuyến đều được xếp hàng đợi. Sau khi ứng dụng lấy lại kết nối mạng, các giao dịch sẽ được gửi đến máy chủ Cơ sở dữ liệu thời gian thực.

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

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

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

Đây là một ví dụ đơn giản về việc ghi dữ liệu khi ngắt kết nối bằng cách sử dụng nguyên thủy onDisconnect :

Nhanh

Lưu ý: Sản phẩm Firebase này không khả dụng trên mục tiêu Clip ứ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 khả dụng trên mục tiêu Clip ứng dụng.
FIRDatabaseReference *presenceRef = [[FIRDatabase database] referenceWithPath:@"disconnectmessage"];
// Write a string when this client loses connection
[presenceRef onDisconnectSetValue:@"I disconnected!"];

Cách thức hoạt động của onDisconnect

Khi bạn thiết lập một onDisconnect() , thao tác này sẽ tồn tại trên máy chủ Cơ sở dữ liệu thời gian thực của Firebase. Máy chủ kiểm tra 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 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 chờ hoặc bị đóng bởi máy khách Cơ sở dữ liệu thời gian thực, máy chủ sẽ kiểm tra bảo mật lần thứ hai (để đảm bảo hoạt động vẫn còn hiệu lực) và sau đó gọi sự kiện.

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

Nhanh

Lưu ý: Sản phẩm Firebase này không khả dụng trên mục tiêu Clip ứ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 khả dụng trên mục tiêu Clip ứng dụng.
[presenceRef onDisconnectRemoveValueWithCompletionBlock:^(NSError *error, FIRDatabaseReference *reference) {
  if (error != nil) {
    NSLog(@"Could not establish onDisconnect event: %@", error);
  }
}];

Sự kiện onDisconnect cũng có thể bị hủy bằng cách gọi .cancel() :

Nhanh

Lưu ý: Sản phẩm Firebase này không khả dụng trên mục tiêu Clip ứ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 khả dụng trên mục tiêu Clip ứ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 sẽ hữu ích khi biết khi nào nó trực tuyến hay ngoại tuyến. Cơ sở dữ liệu thời gian thực của Firebase cung cấp một vị trí đặc biệt tại /.info/connected , vị trí này được cập nhật mỗi khi trạng thái kết nối của máy khách Cơ sở dữ liệu thời gian thực Firebase thay đổi. Đây là một ví dụ:

Nhanh

Lưu ý: Sản phẩm Firebase này không khả dụng trên mục tiêu Clip ứ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 khả dụng trên mục tiêu Clip ứ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ộ hóa giữa các máy khách Cơ sở dữ liệu thời gian thực vì giá trị phụ thuộc vào trạng thái của máy khách. Nói cách khác, nếu một máy khách đọc /.info/connected là sai, thì điều này không đảm bảo rằng một máy khách riêng biệt cũng sẽ đọc sai.

Xử lý độ trễ

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

Máy chủ Cơ sở dữ liệu thời gian thực của Firebase cung cấp 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 , cung cấp một cách dễ dàng để ghi chú lại một cách đáng tin cậy về thời điểm mà ứng dụng khách Cơ sở dữ liệu thời gian thực bị ngắt kết nối:

Nhanh

Lưu ý: Sản phẩm Firebase này không khả dụng trên mục tiêu Clip ứ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 khả dụng trên mục tiêu Clip ứng dụng.
FIRDatabaseReference *userLastOnlineRef = [[FIRDatabase database] referenceWithPath:@"users/morgan/lastOnline"];
[userLastOnlineRef onDisconnectSetValue:[FIRServerValue timestamp]];

Đồng hồ Skew

Mặc dù firebase.database.ServerValue.TIMESTAMP chính xác hơn nhiều và thích hợp cho hầu hết các hoạt động đọc / ghi, nhưng đôi khi có thể hữu ích khi ước tính độ lệch đồng hồ của máy khách đối với các máy chủ của Cơ sở dữ liệu thời gian thực của Firebase. Bạn có thể đính kèm một 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 khách Cơ sở dữ liệu thời gian thực của Firebase thêm vào thời gian được báo cáo cục bộ (thời gian bằng mili giây) để ước tính thời gian máy chủ. Lưu ý rằng độ chính xác của độ trễ này có thể bị ảnh hưởng bởi độ trễ mạng và do đó, chủ yếu hữu ích để phát hiện ra sự chênh lệch lớn (> 1 giây) trong thời gian đồng hồ.

Nhanh

Lưu ý: Sản phẩm Firebase này không khả dụng trên mục tiêu Clip ứ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 khả dụng trên mục tiêu Clip ứ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 hiện diện mẫu

Bằng cách kết hợp các hoạt động ngắt kết nối với giám sát 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 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 máy khách Cơ sở dữ liệu thời gian thực có trực tuyến hay không. Khách hàng đặt vị trí này thành true khi họ trực tuyến và dấu thời gian khi họ ngắt kết nối. Dấu thời gian này cho biết lần cuối cùng người dùng đã cho trực tuyến.

Lưu ý rằng ứng dụng của bạn nên xếp hàng đợi các hoạt động ngắt kết nối trước khi người dùng được đánh dấu trực tuyến, để tránh bất kỳ điều kiện chạy đua nào trong trường hợp kết nối mạng của khách hàng bị mất trước khi cả hai lệnh có thể được gửi đến máy chủ.

Đây là một hệ thống hiện diện người dùng đơn giản:

Nhanh

Lưu ý: Sản phẩm Firebase này không khả dụng trên mục tiêu Clip ứ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 let connected = snapshot.value as? Bool, connected 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 khả dụng trên mục tiêu Clip ứ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]];
  }
}];