I am developing a mobile app using flutter and firebase. I want to create a new document on Cloud Firestore and get the response if the document has been created or not.
If the mobile app does not have internet access, the request is saved, and run when the mobile device is back online. If the mobile is online, I will get a response, if it is not online, I do not get any response.
Future<Vote> commitVote(Vote vote) async {
DocumentReference ref = voteCollection.document(vote.id1+"-"+vote.id2);
final Map<String, dynamic> data = vote.toMap();
Vote result = Vote.fromMap(data);
await ref.setData(data).then((doc) {
print("doc save successful");
}).catchError((error) {
print("doc save error");
print(error);
});
return result;
}
I want to set a timeout for the connection and have the function return true if the document is saved and false if the document has not been saved (when device is offline).
await ref.setData(data).then((doc) {
print("doc save successful");
}).timeout(Duration(seconds:10)).catchError((error) {
print("doc save error");
print(error);
});
This will print doc save error if the setData transaction doesn't complete within 10 seconds.
Related
I am working on an iOS app using Firebase as backend. I am encountering a problem where a listener on a sub collection is behaving unexpectedly. Let me explain my data models first:
I have a top-level collection called "families". Within this collection, I have a sub-collection called "chores". It looks something like this:
Within my iOS app, I am adding a listener to this "chores" sub collection like this:
func readChoreCollection(_ familyId: String) {
if familyChoresListener == nil {
let choreCollection = database.collection("families").document(familyId).collection("chores")
familyChoresListener = choreCollection.order(by: "created")
.addSnapshotListener(includeMetadataChanges: false) { [weak self] querySnapshot, error in
print("\(#fileID) \(#function): \(choreCollection.path)")
guard let querySnapshot = querySnapshot else {
print("\(#fileID) \(#function): Error fetching documents: \(error!)")
return
}
let chores: [Chore] = querySnapshot.documents
.compactMap { document in
do {
return try document.data(as: Chore.self)
} catch {
print("\(#fileID) \(#function): error")
return nil
}
}
if chores.isEmpty {
print("\(#fileID) \(#function): received empty list, publishing nil...")
self?.familyChoresPublisher.send(nil)
} else {
print("\(#fileID) \(#function): received chores data, publishing ... \(querySnapshot.metadata.hasPendingWrites)")
self?.familyChoresPublisher.send(chores)
}
}
}
}
According to the Firestore doc:
The snapshot handler will receive a new query snapshot every time the query results change (that is, when a document is added, removed, or modified
So, when I add a new document to the "chores" sub-collection, the listener did trigger, that is expected. However, it is triggered twice, one from local change, and one from remote change. As shown in the log below:
ChoreReward/ChoreRepository.swift readChoreCollection(_:): received chores data, publishing ... true
ChoreReward/ChoreService.swift addSubscription(): received and cached a non-nil chore list
ChoreReward/ChoreRepository.swift readChoreCollection(_:): families/tgO0B4bjq8uwAzmBaOtL/chores
ChoreReward/ChoreRepository.swift readChoreCollection(_:): received chores data, publishing ... false
ChoreReward/ChoreService.swift addSubscription(): received and cached a non-nil chore list
You can see that the listener is called twice, one with hasPendingWrites = true and one with hasPendingWrites = false. So the documentation did mentioned that the local changes will fire-off the callback to listener first before sending data back to Firestore. So this behavior is kinda expected??? On my other listeners (document listeners) within the app, they are only getting called once by the remote changes, not twice. Maybe there is a different in behavior of document vs. collection/query listener? Can anybody verify this difference?
Here is my data structure:
I have an ios app that is attempting to access data from Cloud Firestore. I have been successful in retrieving full documents and querying for documents. However I need to access specific fields from specific documents. How would I make a call that retrieves me just the value of one field from Firestore in swift? Any Help would be appreciated.
There is no API that fetches just a single field from a document with any of the web or mobile client SDKs. Entire documents are always fetched when you use getDocument(). This implies that there is also no way to use security rules to protect a single field in a document differently than the others.
If you are trying to minimize the amount of data that comes across the wire, you can put that lone field in its own document in a subcollection of the main doc, and you can request that one document individually.
See also this thread of discussion.
It is possible with server SDKs using methods like select(), but you would obviously need to be writing code on a backend and calling that from your client app.
This is actually quite simple and very much achievable using the built in firebase api.
let docRef = db.collection("users").document(name)
docRef.getDocument(source: .cache) { (document, error) in
if let document = document {
let property = document.get(field)
} else {
print("Document does not exist in cache")
}
}
There is actually a way, use this sample code provided by Firebase itself
let docRef = db.collection("cities").document("SF")
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let property = document.get('fieldname')
print("Document data: \(dataDescription)")
} else {
print("Document does not exist")
}
}
I guess I'm late but after some extensive research, there is a way in which we can fetch specific fields from firestore. We can use the select keyword, your query would be somthing like (I'm using a collection for a generalized approach):
const result = await firebase.database().collection('Users').select('name').get();
Where we can access the result.docs to further retrieved the returned filtered result. Thanks!
//this is code for javascript
var docRef = db.collection("users").doc("ID");
docRef.get().then(function(doc) {
if (doc.exists) {
//gives full object of user
console.log("Document data:", doc.data());
//gives specific field
var name=doc.get('name');
console.log(name);
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});
Scenario: Guided-Mode locked app accepts some user input (name, etc) and prints them out a ticket. The user cannot be able choose a printer.
My planned solution was to save the URL of the printer, which I have and is in the form of:
ipp://<hostname>.local.:5001/<printername>
To build the UIPrinter object from this stored string:
var printerURL = NSUrl.FromString(SettingsService.OptPrinterUrl);
var printer = UIPrinter.FromUrl(printerURL);
I then call:
printer.ContactPrinter((available) => {
if (available) {
//
}
});
OR
var printInterface = UIPrintInteractionController.SharedPrintController;
printInterface.PrintToPrinter(printer, (handler, completed, error) =>
{
if (!completed && error != null) {
UIAlertView alert = new UIAlertView("Guest Check-In App", "Print Error", null, "Ok", null);
alert.Show();
}
});
Without positive result.
However, when using a UIPrinter object returned from UIPrinterPickerController (as shown below) it all works.
var printerPicker = UIPrinterPickerController.FromPrinter(null);
printerPicker.PresentFromRect(new CGRect(0,0,100,100),this.View,true, delegate(UIPrinterPickerController handler, bool completed, NSError error) {
printer = printerPicker.SelectedPrinter;
});
I have even tried getting the UIPrinter object from PrinterPicker, and trying to build a new UIPrinter with UIPrinter.FromUrl using the url from the UIPrinter object taken from PrinterPicker.
I was only able to create a working UIPrinter object without directly using the one from UIPrinterPickerController like so:
var printerPicker = UIPrinterPickerController.FromPrinter(null);
printerPicker.PresentFromRect(new CGRect(0,0,100,100),this.View,true, delegate(UIPrinterPickerController handler, bool completed, NSError error) {
printerPickerPrinter = printerPicker.SelectedPrinter;
});
var printer = (UIPrinter)UIPrinter.FromObject(printerPickerPrinter);
Summary
What I need is a way to 'remember' a printer, and use that printer to print automatically in a xamarin-built iOS app.
This is an iOS bug. I reported to Apple Bug Reporter (47180997) and they gave me a lame response:
Shared printers on Linux will not work with this contactPrinter method. It requires that the printer attributes are returned successfully. We often still send a print job even if the other parts fail.
For these methods to work properly, the server has to be a licensed and correctly implemented AirPrint server. CUPS running somewhere isn’t enough.
I am trying to push notifications through topic system in a iOS device with the new API of Google Cloud Messaging designed for iOS device.
I have the right certificates so I can receive notifications from a topic created. My code to subscribe to a topic is the following :
if (_registrationToken && _connectedToGCM) {
[[GCMPubSub sharedInstance] subscribeWithToken:_registrationToken
topic:topicToSubscribe
options:nil
handler:^(NSError *error) {
if (error) {
//handle error here
} else {
self.subscribedToTopic = true;
}
}];
}
I know the equivalent function to unsubscribe but this function need a topic name.
Is there a way to retrieve all topics where my app is possibly subscribed to unregistered them before subscribing ?
There is no way to retrieve the list of topics that your app is subscribed to from the Google Cloud Messaging service.
You have to keep track of the list and persist it on your app (hard coded, stored in preferences, database, file, etc.) or your server.
When you decide to let the user unsubscribe, retrieve the list of topics from where you stored it and pass it to unsubscribeWithToken:token:topic:options:handler as mentioned on the Implementing Topic Messaging page
Alternatively, when receiving messages you can check who is the message 'from'. If it is from a topic you are no longer interested in, you can unsubscribe instead of processing the message.
If you have the registration token it's possible to get the topics the device is subscribed to by using https://iid.googleapis.com/iid/info/IID_TOKEN (with authorization key in the header). Where IID_TOKEN is the registration token.
Find more info at https://developers.google.com/instance-id/reference/server#get_information_about_app_instances.
If you want unsubscribe from all topics simply execute:
GGLInstanceID *iid = [GGLInstanceID sharedInstance];
GGLInstanceIDDeleteHandler deleteHandler = ^void(NSError *error) {
if (error) {
// failed to delete the identity for the app
// do an exponential backoff and retry again.
} else {
// try to get a new ID
dispatch_async(dispatch_get_main_queue(), ^{
GGLInstanceIDHandler handler =
^void(NSString *identity, NSError *error) {
if (error) {
// failed to get the identity for the app
// handle error
} else {
NSString *instanceID = identity;
// handle InstanceID for the app
}
}
[iid getIDWithHandler:handler];
});
}
}
[iid deleteIDWithHandler:deleteHandler];
More info
Don't forget to refresh your TOKEN!
I cannot for the life of me figure out why the response object returned by Plupload below cannot be parsed.
Im returning a JsonResult from my ASP.NET MVC controller as follows:
public JsonResult Upload()
{
// code to process the upload
return Json(new { success = true, data = "Some response data" });
}
Im reading it in the view as follows:
uploader.bind("FileUploaded", function (up, file, response) {
response = $.parseJSON(response);
alert("I managed to parse it!");
if (response.success) {
// do something with the response data
} else {
// tell the user there was an error
}
});
It never gets to the alert "I managed to parse it!"
SOLVED
The Plupload FileUploaded event documentation suggests that the third parameter is a response object. It is infact not! The response object is contained inside THAT object i.e. to see the response data you'd have to do the following:
uploader.bind("FileUploaded", function (up, file, response) {
alert(response.response);
});
Hope this saves somebody else some time :)