Can't get realtime callback on flutter via appwrite - appwrite

I am using appwrite for my backend project. and for the getx state manager. So far, everything is fine with me except for realtime. Please explain how the real-time application recording mechanism works. I have seen many video tutorials and read many articles. How can I get it through the stream? I repeated everything exactly as in the video, but it does not work. How to know if the callback fired or not. In the console it only says that the subscription has been created and that's it. there is nothing more. How to get data from this stream.
//
I have a subscription but I can't get a response//
subscription: ws://195.49.210.221/v1/realtime?project=jetservice_server&channels%5B%5D=collection.77079959596.documents
get_realtime_chat_list() async {
var realtime_chat = await serverProvider.realtime.subscribe([
'collection.' +
userdata.userDataStorage.value.read(my_phone) +
'.documents'
]);
realtime_chat.stream.listen((data) {
try {
if (data.payload.isNotEmpty) {
switch (data.events) {
case ['collection.document.create']:
print(data.payload);
break;
case ['collection.document.delete']:
print("document_deleted");
}
}
print(realtime_chat);
} on AppwriteException catch (e) {
print(e.message);
}
});
}

I don't think the channel you subscribed to is correct. As stated in the docs, it should be databases.[ID].collections.[ID].documents.
Also, it seems like the switch statement isn't quite right. I would suggest grabbing the 1st event from the events array and checking that. You might want to check if the event ends with .create or .delete.
Finally, Appwrite's realtime sends messages when they're triggered. Did you create a new document after subscribing? Also, did you ensure the user has access to the documents?

Related

Distinct Stream in Dart

I'm writing a flutter app which sends commands via BlueTooth (FlutterBlue) to a device. The device controlls some LEDs.
The communication is working in general quite well but:
On the UI I have a slider controlling the light intensity. When I pull the slider there are more values generated than the bluetooth backend can handle.
In my first implementation I was sending the data directly to the bluetooth characteristic, resulting in exceptions from the bluetooth backend and some values get lost. It's hard to fade light down to zero.
In my second approach I'm using a stream and an await for loop to send the data. Now all values are send without any exceptions but it takes several seconds after releasing the slider until all values are send. Since I want direct visual feedback on the LEDs, this is not an option.
Since there are multiple commands of the same type to be send, I can skip all commands of the same type which were added while the bluetooth send routine was processing a write event.
I saw that there is a Stream.Distinct method but: It returns a new stream. So I have to exit my await for loop and handle the new stream.
Is there a way of removing undesired events from an existing stream without creating a new stream where I have to listen to?
Here is what I'm doing:
class MyBlueToothDevice {
BluetoothDevice _device;
List<BluetoothCharacteristic> _characteristics =
List<BluetoothCharacteristic>();
final _sendStream = StreamController<Tuple2<SendCommands, List<int>>>();
MyBlueToothDevice(this._device) {
_writeNext();
}
Future<void> write(SendCommands command, List<int> value) async {
if (isConnected) {
_sendStream.add(Tuple2<SendCommands, List<int>>(command, value));
// await _characteristics[command.index].write(value).catchError((value) {
// print("Characteristics.write error: $value");
// });
}
}
Future<void> _writeNext() async {
await for (var tuple in _sendStream.stream) {
await _characteristics[tuple.item1.index]
.write(tuple.item2)
.catchError((value) {
print("Characteristics.write error: $value");
});
}
}
}
The best solution is to use application state management to receive all the events from your slider. The state manager will then rate-limit the messages to the device to something it can handle, and also ensure that the most recent message is not lost.
A very basic solution would receive the slider value and update the value in the state manager. A periodic timer with a suitable rate could then update that value to the device; possibly only if the value actually changed since the last time it was sent.

Firebase Firestore Upload Methods Taking Far Longer Than Other Methods

So I created an app using Ionic and Firebase as my back-end. When the app is run in a web browser or on an iOS emulator, the response is very fast and the app works really well. On iOS however, uploading anything to Firebase takes forever. Note that downloading information from Firebase is fairly fast and simple. Uploading however poses an issue. The wifi I am testing this on is very fast. Does anyone know why this is happening?
The app was released recently and this has been an issue for a lot of my users and myself included!
UPDATE: So after more testing it appears that the issue is specifically with certain functions. These methods are .update() and .add()
Anytime I try to update a field in Firebase it takes forever. Anytime I try to add a document to a collection it also takes forever. Why is this occuring? Here's some code that takes forever to achieve:
async createDMChat(otherUID: string, otherName: string) {
let newDoc: DocumentReference = this.db.firestore.collection('dmchats').doc();
let docId: string = newDoc.id;
let chatsArray = this.dmChats.value;
let timestamp = Date.now();
chatsArray.push(docId);
//Adds to your dm chat
await this.db.collection('users').doc('dmchatinfo').collection('dmchatinfo').doc(this.dataService.uid.value).set({
chatsArray: chatsArray
});
//Adds to other person DM chat
//-------------------THIS IS THE PART THAT TAKES FOREVER-----------------------
//The .update() method is the problem as well as .add() to a collection
await this.db.collection('users').doc('dmchatinfo').collection('dmchatinfo').doc(otherUID).update({
chatsArray: firebase.firestore.FieldValue.arrayUnion(docId)
});
//Pull info on person's UID
let otherUserInfo = await this.db.firestore.collection('users').doc('user').collection('user').doc(otherUID).get();
let otherAvatar = otherUserInfo.data().avatar;
//Sets message in database
await newDoc.set({
chatName: otherName + " & " + this.dataService.user.value.name,
users: [otherUID, this.dataService.uid.value],
lastPosted: timestamp,
avatar1: this.dataService.avatarUrl.value,
avatar2: otherAvatar,
person1: otherName,
person2: this.dataService.user.value.name
});
await newDoc.collection('messages').doc('init').set({
init: 'init'
});
await this.dataService.storage.set(docId, timestamp);
}
In the above code, the .update() is the method that takes forever. Also other functions with the .add() method adding documents to a collection takes forever.
Again THESE METHODS ARE FAST ON WEB BROWSERS AND EMULATORS. Just not in the mobile app.
===========================================================================
NEW UPDATE: So it appears that the problem is actually in waiting for the Promise to return. I rewrote all of the functions used to no longer use add() or update(), but rather used set() after making a new document with doc() to replace add(). I then used set({...},{merge: true}) to replace update().
This time around the changes to the server were instant, but the problem came when waiting for the methods to return a promise from the server. This is the part that is causing the lag now. Does anyone know why this is occurring? I could simply change my code to not wait for these promises to return, but I would like to keep await within my code without having this issue.

Bidirectional gRPC stream sometimes stops processing responses after stopping and starting

In short
We have a mobile app that streams fairly high volumes of data to and from a server through various bidirectional streams. The streams need to be closed on occasion (for example when the app is backgrounded). They are then reopened as needed. Sometimes when this happens, something goes wrong:
From what I can tell, the stream is up and running on the device's side (the status of both the GRPCProtocall and the GRXWriter involved is either started or paused)
The device sends data on the stream fine (the server receives the data)
The server seems to send data back to the device fine (the server's Stream.Send calls return as successful)
On the device, the result handler for data received on the stream is never called
More detail
Our code is heavily simplified below, but this should hopefully provide enough detail to indicate what we're doing. A bidirection stream is managed by a Switch class:
class Switch {
/** The protocall over which we send and receive data */
var protocall: GRPCProtoCall?
/** The writer object that writes data to the protocall. */
var writer: GRXBufferedPipe?
/** A static GRPCProtoService as per the .proto */
static let service = APPDataService(host: Settings.grpcHost)
/** A response handler. APPData is the datatype defined by the .proto. */
func rpcResponse(done: Bool, response: APPData?, error: Error?) {
NSLog("Response received")
// Handle response...
}
func start() {
// Create a (new) instance of the writer
// (A writer cannot be used on multiple protocalls)
self.writer = GRXBufferedPipe()
// Setup the protocall
self.protocall = Switch.service.rpcToStream(withRequestWriter: self.writer!, eventHandler: self.rpcRespose(done:response:error:))
// Start the stream
self.protocall.start()
}
func stop() {
// Stop the writer if it is started.
if self.writer.state == .started || self.writer.state == .paused {
self.writer.finishWithError(nil)
}
// Stop the proto call if it is started
if self.protocall?.state == .started || self.protocall?.state == .paused {
protocall?.cancel()
}
self.protocall = nil
}
private var needsRestart: Bool {
if let protocall = self.protocall {
if protocall.state == .notStarted || protocall.state == .finished {
// protocall exists, but isn't running.
return true
} else if writer.state == .notStarted || writer.state == .finished {
// writer isn't running
return true
} else {
// protocall and writer are running
return false
}
} else {
// protocall doesn't exist.
return true
}
}
func restartIfNeeded() {
guard self.needsRestart else { return }
self.stop()
self.start()
}
func write(data: APPData) {
self.writer.writeValue(data)
}
}
Like I said, heavily simplified, but it shows how we start, stop, and restart streams, and how we check whether a stream is healthy.
When the app is backgrounded, we call stop(). When it is foregrounded and we need the stream again, we call start(). And we periodically call restartIfNeeded(), eg. when screens that use the stream come into view.
As I mentioned above, what happens occasionally is that our response handler (rpcResponse) stops getting called when server writes data to the stream. The stream appears to be healthy (server receives the data we write to it, and protocall.state is neither .notStarted nor .finished). But not even the log on the first line of the response handler is executed.
First question: Are we managing the streams correctly, or is our way of stopping and restarting streams prone to errors? If so, what is the correct way of doing something like this?
Second question: How do we debug this? Everything we could think of that we can query for a status tells us that the stream is up and running, but it feels like the objc gRPC library keeps a lot of its mechanics hidden from us. Is there a way to see whether responses from server may do reach us, but fail to trigger our response handler?
Third question: As per the code above, we use the GRXBufferedPipe provided by the library. Its documentation advises against using it in production because it doesn't have a push-back mechanism. To our understanding, the writer is only used to feed data to the gRPC core in a synchronised, one-at-a-time fashion, and since server receives data from us fine, we don't think this is an issue. Are we wrong though? Is the writer also involved in feeding data received from server to our response handler? I.e. if the writer broke due to overload, could that manifest as a problem reading data from the stream, rather than writing to it?
UPDATE: Over a year after asking this, we have finally found a deadlock bug in our server-side code that was causing this behaviour on client-side. The streams appeared to hang because no communication sent by the client was handled by server, and vice-versa, but the streams were actually alive and well. The accepted answer provides good advice for how to manage these bi-directional streams, which I believe is still valuable (it helped us a lot!). But the issue was actually due to a programming error.
Also, for anyone running into this type of issue, it might be worth investigating whether you're experiencing this known issue where a channel gets silently dropped when iOS changes its network. This readme provides instructions for using Apple's CFStream API rather than TCP sockets as a possible fix for that issue.
First question: Are we managing the streams correctly, or is our way of stopping and restarting streams prone to errors? If so, what is the correct way of doing something like this?
From what I can tell by looking at your code, the start() function seems to be right. In the stop() function, you do not need to call cancel() of self.protocall; the call will be finished with the previous self.writer.finishWithError(nil).
needsrestart() is where it gets a bit messy. First, you are not supposed to poll/set the state of protocall yourself. That state is altered by itself. Second, setting those state does not close your stream. It only pause a writer, and if app is in background, pausing a writer is like a no-op. If you want to close a stream, you should use finishWithError to terminate this call, and maybe start a new call later when needed.
Second question: How do we debug this?
One way is to turn on gRPC log (GRPC_TRACE and GRPC_VERBOSITY). Another way is to set breakpoint at here where gRPC objc library receives a gRPC message from the server.
Third question: Is the writer also involved in feeding data received from server to our response handler?
No. If you create a buffered pipe and feed that as request of your call, it only feed data to be sent to server. The receiving path is handled by another writer (which is in fact your protocall object).
I don't see where the usage of GRXBufferedPipe in production is discouraged. The known drawback about this utility is that if you pause the writer but keep writing data to it with writeWithValue, you end up buffering a lot of data without being able to flush them, which may cause memory issue.

Function which triggers when there is an event on a specific table in database with PARSE

I am developing an iOS analytic application (with Swift) that needs charts. The point is, I would like to update these charts when there is an update or insert in a specific table in database with Parse. I'm not looking for push notification and the Parse documentation doesn't mention a function to do that.
Is there a way to create a function which triggered when an event happens in a table for a specific user with Parse?
Thank you for your advice
Yes you can do this with cloud code..
Read this guide how to get started.
Then you can use triggers like beforeSave or afterSave or beforeDelete and afterDelete.
An example would be:
Parse.Cloud.beforeSave("Review", function(request, response) {
if (request.object.get("stars") < 1) {
response.error("you cannot give less than one star");
} else if (request.object.get("stars") > 5) {
response.error("you cannot give more than five stars");
} else {
response.success();
}
});

How to buffer stream events?

I have a web component which subscribes to a stream.
Since the web component is re-created each time it's displayed, I have to clean up the subscriber and redo it.
Right now I am adding all subscribers to a list and in removed() life-cycle method I'm doing :
subscriptions.forEach((sub) => sub.cancel());
Now, to the problem: when the web component isn't displayed, there's no one listening to the stream. The issue is that the component is missing data/events when it's not displayed.
What I need is buffering. Events need to be buffered and sent at once when a listener is registered. According to the documentation, buffering happens until a listener is registered:
The controller will buffer all incoming events until the subscriber is registered.
This works, but the problem is that the listener will at some point removed, and re-registered, and it appears this does not trigger buffering.
It appears that buffering happens only initially, not later on even if all listeners are gone.
So the question is: how do I buffer in this situation where listeners may be gone and back?
Note: normally you shouldn't be able to resubscribe to a Stream that has already been closed. This seems to be a bug we forgot to fix.
I'm unfamiliar with web-components but I hope I'm addressing your problem with the following suggestion.
One way (and there are of course many) would be to create a new Stream for every subscriber (like html-events do) that pauses the original stream.
Say origin is the original Stream. Then implement a stream getter that returns a new Stream that is linked to origin:
Untested code.
Stream origin;
var _subscription;
final _listeners = new Set<StreamController>();
_addListener(controller) {
_listeners.add(controller);
if (_subscription == null) {
_subscription = origin.listen((event) {
// When we emit the event we want listeners to be able to unsubscribe
// or add new listeners. In order to avoid ConcurrentModificationErrors
// we need to make sure that the _listeners set is not modified while
// we are iterating over it with forEach. Here we just create a copy with
// toList().
// Alternatively (more efficient) we could also queue subscription
// modification requests and do them after the forEach.
_listeners.toList().forEach((c) => c.add(event));
});
}
_subscription.resume(); // Just in case it was paused.
}
_removeListener(controller) {
_listeners.remove(controller);
if (_listeners.isEmpty) _subscription.pause();
}
Stream get stream {
var controller;
controller = new StreamController(
onListen: () => _addListener(controller),
onCancel: () => _removeListener(controller));
return controller.stream;
}
If you need to buffer events immediately you need to start the subscription right away and not lazily as in the sample code.

Resources