Firebase + iOS: Receiving stale data using observeSingleEvent without using isPersistence = true - ios

I currently use observeSingleEvent to fetch data periodically in our game. It seems that the client is receiving stale data at times while using this method. From what I have read, I believe this should only happen if isPersistence = true, which is not the case. Is this still expected behavior? Shouldn't I receive fresh data each time I query? Thanks in advance.
EDIT: More detailed query:
for levelNumber in 1...numberOfLevels
{
ref.child(pathToLevelData + "/" + levelNumber).queryOrderedByValue().queryStarting(atValue:
highScore+1).observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children
{
let snap = child as! DataSnapshot
guard let value = snap.value as? Int else { return }
// Process value, but it is not always fresh data from Firebase
}
})
}

Related

Why would database snapshot loops run far too many loops when app is built new vs when just restarted or logged in?

What I observe: After an app has been built on iOS; if I log in an out the correct number of loops run. However, if I build the app new with the user still signed in (then it goes automatically to the homepage), way too many snapshot loops get run.
Here is an excerpt of the code:
let refArtists2 = Database.database().reference().child("people").queryOrdered(byChild: "caption").queryStarting(atValue:myInt).queryEnding(atValue: myInt1)
var handle: UInt = 0
handle = refArtists2.observe(DataEventType.value, with: { snapshot in
....
self.query1 = geoFire.query(at: self.dict, withRadius: 500)
self.handle1 = self.query1?.observe(.keyEntered, with: { (key, location) in
})
self.handle2 = self.query1?.observe(.keyExited, with: { key1, location in
})
self.query1?.observeReady({
while let people = enumerator.nextObject() as? DataSnapshot {
if people.key != thisUsersUid && self.componentArray.contains(people.key) {
print(self.componentArray,"man")
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
if snapshot.exists() && self.people.isEmpty == false {
self.ref.removeObserver(withHandle: handle)
self.ref.removeObserver(withHandle: self.handle1)
self.ref.removeObserver(withHandle: self.handle2)
}
}
}
}
})
The key print to look at is "man". If there are 3 users that get displayed, man is printed 3 times, so loop is done 3 times. However, in the instance where way too many loops are run, it seems man is printed for each iteration of users that are within 500 miles.
The error ended up being related to the while let loop. Using for in has fixed it.

How does control flow work when retrieving Information from Firebase?

var ergebnisBluetezeit = Set<String>()
let refBluetezeit = rootRef.child("Pflanzen").child("Eigenschaften").child("Blütezeit")
refBluetezeit.child("Februar").observeSingleEvent(of: .value, with: { snapshot in
for plant in snapshot.children {
self.ergebnisBluetezeit.insert((plant as AnyObject).value)
}
})
print(ergebnisBluetezeit)
I want to retrieve Data from my Firebase Database. The Retrieving Process does work already, but the following confuses me: the current output from the print is an empty set, but when i use the var ergebnisBluetezeit elsewhere (for example setup a button, which action is to print ergebnisBluetezeit), it is filled. When i put the print in the for loop, it does print the right output, too.
I seem to not have understood the control flow here, so my Question:
How can i use the Set where the print statement is at the moment?
Thanks for your help.
It's the logic of asynchronous calls
print("1") // empty
refBluetezeit.child("Februar").observeSingleEvent(of: .value, with: { snapshot in
print("3") // empty
for plant in snapshot.children {
self.ergebnisBluetezeit.insert((plant as AnyObject).value)
}
print(ergebnisBluetezeit) // not empty
})
print("2") // empty
the value is empty until the request finishes regardless of where in code ordering you run the print , as the numbering above in order 1 , 2 , 3 to know when it finishes you can use completions like
func getData(completion:#escaping() -> ()) {
let refBluetezeit = rootRef.child("Pflanzen").child("Eigenschaften").child("Blütezeit")
refBluetezeit.child("Februar").observeSingleEvent(of: .value, with: { snapshot in
for plant in snapshot.children {
self.ergebnisBluetezeit.insert((plant as AnyObject).value)
}
completion()
})
}
And call
getData {
print(ergebnisBluetezeit)
}

how to make getting data faster from Firebase Firestore?

I am new in programming and in iOS development. I am trying to make an app using Firestore database from Firebase. I don't know if it is normal or not, but when I am trying to get a data from firestore database, it seems too long for me. I don't know if I make a mistake or not
here is my code to get all city data from firestore
reference :
import Foundation
import FirebaseFirestore
import Firebase
enum FirestoreCollectionReference {
case users
case events
case cities
private var path : String {
switch self {
case .users : return "users"
case .events : return "events"
case .cities : return "cities"
}
}
func reference () -> CollectionReference {
return Firestore.firestore().collection(path)
}
}
I use getAllCitiesDataFromFirestore method in CityKM class to get the city data that stored in firestore
class CityKM {
var name : String
var coordinate : GeoPoint
init (name: String , coordinate: GeoPoint ) {
self.name = name
self.coordinate = coordinate
}
init (dictionary: [String:Any]) {
// this init will be used if we get data from firebase observation to construct an event object
name = dictionary["name"] as! String
coordinate = dictionary["coordinate"] as! GeoPoint
}
static func getAllCitiesDataFromFirestore (completion: #escaping ( [CityKM]? )->Void) {
// to retrieve all cities data from Firebase database by one read only, not using realtime fetching listener
let startTime = CFAbsoluteTimeGetCurrent() // to track time consumption of this method
FirestoreCollectionReference.cities.reference().getDocuments { (snapshot, error) in
if let error = error {
print("Failed to retrieve all cities data: \(error.localizedDescription)")
} else {
print("Sucessfully get all cities data from firestore")
guard let documentsSnapshot = snapshot, !documentsSnapshot.isEmpty else {
completion(nil)
return
}
let citiesDocuments = documentsSnapshot.documents
var cityArray = [CityKM]()
for document in citiesDocuments {
guard let cityName = document.data()["name"] as? String,
let cityCoordinate = document.data()["coordinate"] as? GeoPoint else {return}
let theCity = CityKM(name: cityName, coordinate: cityCoordinate)
cityArray.append(theCity)
}
completion(cityArray)
let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime // to track time consumption of this method
print("Time needed to get all cities data from Firestore : \(timeElapsed) s.") // to track time consumption of this method
}
}
}
}
extension CityKM {
// MARK: - User Helper Methods
func toDictionary() -> [String:Any]{
return [
"name" : name,
"coordinate" : coordinate
]
}
}
from my debugging area, it is printed
"Time needed to get all cities data from Firestore : 1.8787678903 s."
is it possible to make it faster? Is 1.8s normal? am i make a mistake in my code that make the request data takes too long time ? I hope that I can make request time is below one second
I don't think the internet speed is the problem, since I can open video on youtube without buffering
That performance sounds a bit worse than what I see, but nothing excessive. Loading data from the cloud simply takes time. A quick approach to hide that latency is by making use of Firebase's built-in caching.
When you call getDocuments, the Firebase client needs to check on the server what the document's value is before it can call your code, which then shows the value to the user. As said: there is no way to speed up this reading in your code, so it'll always take at least 1.8s before the user sees a document.
If instead you listen for realtime updates from the database with addSnapshotListener, the Firebase client may be able to immediately call your code with values from its local cache, and then later re-invoke your code in case there has been an update to the data on the server.

Swift Firebase ondisconnect action when there is no internet

I have a user to user app. if the user lose internet connection I want firebase to query "isUserLogon:false". I use ondisconnect this work fine when the user terminate the app but not when they are disconnected from the internet. What is the best solution to resolve. Im assuming because there is no connection firebase cannot update. If the user is disconnected from the internet i don't want firebase to think they are still active when they are not, how do other apps handle this scenario.
let path = "rquest/frontEnd/users/\(self.currentUserId()!)"
let myConnectionsRef = FIRDatabase.database().reference(withPath: path).child("isUserLogon")
let lastOnlineRef = FIRDatabase.database().reference(withPath: path).child("lastOnline")
let connectedRef = FIRDatabase.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
// this value could contain info about the device or a timestamp instead of just true
let con = myConnectionsRef
con.setValue(true)
// when this device disconnects, remove it
con.onDisconnectSetValue(false)
// when I disconnect, update the last time I was seen online
lastOnlineRef.onDisconnectSetValue("Date here")
})
This article explains how to build presence system to maintain online/offline status in firebase.
Essential steps to set up basic presence system:
var amOnline = new Firebase('https://<demo>.firebaseio.com/.info/connected');
var userRef = new Firebase('https://<demo>.firebaseio.com/presence/' + userid);
amOnline.on('value', function(snapshot) {
if (snapshot.val()) {
userRef.onDisconnect().remove();
userRef.set(true);
}
});
Thats it! now just make a call to below function passing uid of the users you want to know the network status. it returns either true/false based on their network availability.
checkNetworkStatus(uid) {
let userRef = this.rootRef.child('/presence/' + uid);
return userRef.on('value', function (snapshot) {
return snapshot.val();
});
}

aws dynamodb how to use object mapper with batch get in ios swift

Thanks in advance for any help. I am trying to get Batch items (Load multiple) items from one DynamoDb table using the AWS iOS SDK (Swift). I can load one item using the Block syntax, but I need to load 10 or more than that. I don't want to use 10 Block calls to load them individually. I tried to follow the attach stackoverflow Link (where the similar solution is given) but I am getting the following compiler error message. I come from Java background, hence could also be a syntax issue. Is it the right way to load multiple items? I don't want to use low level API. Any help, where I am going wrong. Thanks.
aws dynamodb how to use object mapper with batch get in ios
let dynamoDBObjectMapper = AWSDynamoDBObjectMapper.default()
var tasksList = Array<AWSTask<AnyObject>>()
for i in 1...10 {
tasksList.append(dynamoDBObjectMapper.load(AWSCards.self, hashKey: "SH_"+String(i), rangeKey: nil))
}
AWSTask.init(forCompletionOfAllTasksWithResults: tasksList).continueWithBlock { (task) -> AnyObject? in
if let cards = task.result as? [AWSCards] {
print(cards.count)
}
else if let error = task.error {
print(error.localizedDescription)
}
return nil
}
Have a try with the following codes (Swift 4.1, Feb 9th, 2018):
let dynamoDBObjectMapper = AWSDynamoDBObjectMapper.default()
var tasksList = Array<AWSTask<AnyObject>>()
for i in 1...10 {
tasksList.append(dynamoDBObjectMapper.load(AWSCards.self, hashKey: "SH_"+String(i), rangeKey: nil))
}
AWSTask<AnyObject>.init(forCompletionOfAllTasksWithResults: tasksList).continueWith { (task) -> Any? in
if let cards = task.result as? [AWSCards] {
print(cards.count)
}
else if let error = task.error {
print(error.localizedDescription)
}
return nil
}
Your question is "how to use the object mapper" but it might be more efficient for you to not use it.
However, there is a way to use it. See Niklas's answer here and here (he copy & pasted), but something about it strikes me as fishy. I want to make the assertion that it is not as fast as the built-in batch-get function, but I am unsure. I suspect that this does not complete the items in parallel, or at least not as efficiently as in BatchGetItem.
See the docs: "In order to minimize response latency, BatchGetItem retrieves items in parallel."
According to Yosuke, "Currently, AWSDynamoDBObjectMapper does not support the batch get item. You need to load one item at a time if you want to use the object mapper" as of 2016. This still seems to be the case. I am using a version a couple versions behind, but not too far behind. Someone check.
In conclusion, if you are loading one item at a time, you are likely missing out on the whole purpose of BatchGetItem (low latency).
Pulling from various sources, including John Davis's question here, I have tested and ran this BatchGetItem result. Here ya go.
import AWSDynamoDB
let primaryKeyToSortKeyDict : [String : String] = .... // Your stuff
var keys = [Any]()
for key in primaryKeyToSortKeyDict.keys {
let partitionKeyValue = AWSDynamoDBAttributeValue()
partitionKeyValue?.s = String(key)
let sortValue = AWSDynamoDBAttributeValue()
sortValue?.s = String(primaryKeyToSortKeyDict[key]!)
keys.append(["partitionKeyAttributeName": partitionKeyValue, "sortKeyAttributeName": sortValue])
}
let keysAndAttributesMap = AWSDynamoDBKeysAndAttributes()
keysAndAttributesMap?.keys = keys as? [[String : AWSDynamoDBAttributeValue]]
keysAndAttributesMap?.consistentRead = true
let tableMap = [table : keysAndAttributesMap]
let request = AWSDynamoDBBatchGetItemInput()
request?.requestItems = tableMap as? [String : AWSDynamoDBKeysAndAttributes]
request?.returnConsumedCapacity = AWSDynamoDBReturnConsumedCapacity.total
guard request != nil else {
print("Handle some error")
return
}
AWSDynamoDB.default().batchGetItem(request!) { (output, error) in
print("Here is the batchgetitem output")
if error == nil {
// do output stuff
} else {
// handle error
}
}

Resources