Firebase Firestore Swift, Timestamp but server time? - ios

With Firestore, I add a timestamp field like this
var ref: DocumentReference? = nil
ref = Firestore.firestore()
.collection("something")
.addDocument(data: [
"name": name,
"words": words,
"created": Timestamp(date: Date())
]) { ...
let theNewId = ref!.documentID
...
}
That's fine and works great, but it's not really correct. Should be using the "server timestamp" which Firestore supplies.
Please note this is on iOS (Swift) and Firestore, not Firebase.
What is the syntax to get a server timestamp?

The syntax you're looking for is:
"created": FieldValue.serverTimestamp()
This creates a token which itself has no date value. The value is assigned by the server when the write is actually written to the database, which could be much later if there are network issues, so keep that in mind.
Also keep in mind that because they are tokens, they can present different values when you read them, to which we can configure how they should be interpreted:
doc.get("created", serverTimestampBehavior: .none)
doc.get("created", serverTimestampBehavior: .previous)
doc.get("created", serverTimestampBehavior: .estimate)
none will give you a nil value if the value hasn't yet been set by the server. For example, if you're writing a document that relies on latency-compensated returns, you'll get nil on that latency-compensated return until the server eventually executes the write.
previous will give you any previous values, if they exist.
estimate will give you a value, but it will be an estimate of what the value is likely to be. For example, if you're writing a document that relies on a latency-compensated returns, estimate will give you a date value on that latency-compensated return even though the server has yet to execute the write and set its value.
It is for these reasons that dealing with Firestore's timestamps may require handling more returns by your snapshot listeners (to update tokens). A Swift alternative to these tokens is the Unix timestamp:
extension Date {
var unixTimestamp: Int {
return Int(self.timeIntervalSince1970 * 1_000) // millisecond precision
}
}
"created": Date().unixTimestamp
This is definitely the best explanation of how the timestamps work (written by the same Doug Stevenson who actually posted an answer): https://medium.com/firebase-developers/the-secrets-of-firestore-fieldvalue-servertimestamp-revealed-29dd7a38a82b

If you want a server timestamp for a field's value, use FieldValue.serverTimestamp(). This will return a token value that gets interpreted on the server after the write completes.

Related

Illegal sap.ui.model.odata.type.DateTimeOffset

Posting for prosperity. I had zero hits from google.
I'm writing a SAP Business One Web Client extension. I'm using the worklist template and have the b1s/v2/ Service Layer metadata (auto generated while setting up the template)
Running the sandbox version ("npm run start-local") it automatically generates fake data based on the metadata. My data included an edm.DateTimeOffset which is resolved by Fiori using the sap.ui.model.odata.type.DateTimeOffset model.
Here is an example response object from the test data proxy (all autogenerated)
{
DocEntry: 1,
U_CardCode: "U_CardCode_0",
U_CardName: "U_CardName_0",
U_DocCurrency: "U_DocCurrency_0",
U_DocTotal: "",
U_DueDate: "2017-04-13T14:50:39.000Z",
U_Status: "U_Status_0",
U_SupplierInvNo: "U_SupplierInvNo_0",
}
A perfectly normal U_DueDate value that, according to all the documentation I could find is an accepted format, doublely confirmed by the fact that it's a sap generated value.
This produces an error on screen
Illegal sap.ui.model.odata.type.DateTimeOffset
Adding a formatter doesn't work. If it's unable to parse the value then it won't pass it on to a formatter.
Turns out there is a way to override the expected type in the metadata. I couldn't find much documentation on it, but I changed the element from
text="{'U_DueDate'}" />
to
text="{
path: 'U_DueDate',
targetType: 'any',
formatter: '.formatter.date'
}" />
The date is now accepted as a string so I can use a custom formatter.
Here is the date function in the formetter:
date : function (sValue) {
if (!sValue) {
return "";
}
var date = new Date(sValue)
var asString = date.toLocaleDateString("en-GB")
return asString;
}
You don't have to hard code the localization, but my use case is that niche

Parse Platform on iOS: best way to replace changed local values with more-recently changed server values?

So imagine the following scenario, using the Parse platform on iOS:
I get a PFObject from the server, let's call it GlassChalice.
Someone else, let's say Bill Blofeld, changes GlassChalice from a different location.
Later, I make some changes to my local GlassChalice, but don't save them to the server.
Still later, I want to update GlassChalice, but I want to update it to the current server values, in other word Bill Blofeld's values. I do not want to replace the server values with my local values, and also do not want to reset my local values to the values GlassChalice was loaded with.
So if I use revert(), will I get what I want?
According to the Parse docs:
- revert Clears any changes to this object made since the last call to save and sets it back to the server state.
...but, as in my example, clearing "changes made since the last call to save" and setting it "back to the server state" aren't always the same thing.
So far this seems like the only way to guarantee the results I want, but it has one obvious problem:
public func updateObjectFromServer(_ objectToUpdate: PFObject, then doThis: (()->Void)? = nil) {
let query = PFObject.query()
query?.whereKey("objectId", equalTo: objectToUpdate.objectId!)
query?.getFirstObjectInBackground (block: {
(serverObject, error) in
if error.isNil() {
objectToUpdate["numberOfLimbs"] = serverObject?["numberOfLimbs"]
objectToUpdate["eyePlacement"] = serverObject?["eyePlacement"]
objectToUpdate["crossStitchingTalentRating"] = serverObject?["crossStitchingTalentRating"]
objectToUpdate["clamsEaten"] = serverObject?["clamsEaten"]
} else {
//handle error...
}
doThis?()
})
}
But the huge problem here is that I have to know all the key names, and type them in explicitly, for this to work.
Is there a better, more generic, way?

Snowflake Tasks causing error in timezone in queries

I am running a simple insert query inside a stored procedure with to_timeatamp_ntz("column value") along with other columns.
This works fine when I am running it with the snowflake UI and logged in with my account.
This works fine when I am calling it using python scripts from my visual studio instance.
The same stored procedure fails when it is being called by a scheduled task.
I am thinking if it has something to do with the user's timezone of 'System' vs my time zone.
Execution error in store procedure LOAD_Data(): Failed to cast variant
value "2019-11-27T13:42:03.221Z" to TIMESTAMP_NTZ At
Statement.execute, line 24 position 57
I tried to provide timezone as session parameters in task and in the stored proc but does not seem to be addressing the issue. Any ideas?
I'm guessing (since you didn't include the SQL statement that causes the error) that you are trying to bind a Date object when creating a Statement object. That won't work.
The only parameters you can bind are numbers, strings, null, and the special SfDate object that you can only get from a result set (to my knowledge). Most other parameters must be converted to string using mydate.toJSON(), JSON.stringify(myobj), etc., before binding, eg:
var stmt = snowflake.createStatement(
{ sqlText: `SELECT :1::TIMESTAMP_LTZ NOW`, binds: [(new Date).toJSON()] }
);
Date object errors can be misleading, because Date objects causing an error can be converted and displayed as strings in the error message.
I found the issue:
my Task was using a copy paste effect similar to this:
CREATE TASK TASK_LOAD_an_sp
WAREHOUSE = COMPUTE_WH
TIMEZONE = 'US/Eastern'
SCHEDULE = 'USING CRON 0/30 * * * * America/New_York'
TIMESTAMP_INPUT_FORMAT = 'YYYY-MM-DD HH24'
AS
Call LOAD_an_sp();
The Timestamp input format was causing this.

data not being saved in firebase database

I am just starting out with Firebase and have managed to send data to a Firebase Realtime Database. The problem is that some times it works and sometimes not. I am struggling to understand why.
Here is a code snippet
var pq_data = jsPsych.data.get().values();
for (var ix= 0; ix < pq_data.length; ix++){
var object=pq_data[ix];
var pq_boo = pq_database.ref(subj_id +ix.toString()+'/').update(object)
}
As I say this works sometimes but not always and I understand that it may have something to do with the code completing before the write operations have(?)
I have read but do not clearly understand advice about onCompletion and I am still in the dark. I need to make sure each object is written to the database - is this possible and if so how?
Very much a beginner,
Philip.
// Import Admin SDK
var admin = require("firebase-admin");
// Get a database reference to our blog
var db = admin.database();
var ref = db.ref("server/saving-data/fireblog");
First, create a database reference to your user data. Then use set() / setValue() to save a user object to the database with the user's username, full name, and birthday. You can pass set a string, number, boolean, null, array or any JSON object. Passing null will remove the data at the specified location. In this case you'll pass it an object:
var usersRef = ref.child("users");
usersRef.set({
alanisawesome: {
date_of_birth: "June 23, 1912",
full_name: "Alan Turing"
},
gracehop: {
date_of_birth: "December 9, 1906",
full_name: "Grace Hopper"
}
});
Thanks for this Yes For brevity I didn't show all the code and so I have access to the database. The problem is with sending data to it. Sometimes it works and sometimes not.
I now realise this is related to my failure to deal with Promises. I have now some understanding of these but still need to make sure that the data gets captured in the database. SO even though the Promise may return an Error I still need to re-send the data so that it will get written to the database. Still not sure whether this is advisable or even possible.

Check Firebase for existing data before overwriting?

I have the following data in Firebase:
devices
iphone5
date_created: "1456183905"
I'm trying to determine if "date_created" exists, and if it doesn't then create it.
I read about snapshots, but is there an easier way to check Firebase to see if this data exists? What I have now is using snapshots, but it is tied to an event handler. Can't I just do a basic query to see if this entry exists or not?
Thanks.
You can test if a value exists in your Swift code, by:
let ref = Firebase(url: "https://stackoverflow.firebaseio.com/35570687")
ref.observeSingleEventOfType(.Value, withBlock: { snapshot in
if (!snapshot.exists()) {
ref.setValue([".sv": "timestamp"])
}
else {
print("already exist")
}
})
But since this is only client-side, there is a chance that two clients will run the code at almost the exact time and both end up writing the timestamp. In the snippet above that wouldn't be a problem, but in real use-cases this sort of race condition might be unwanted.
As Andre commented, you can validate this in your security rules:
"date_created": {
".write": "!data.exists() || data.val() == newData.val()"
This validates that either this is the first time you write date_created (so the data won't exist yet) or otherwise that the value is unchanged.

Resources