I'm learning about NativeScript plugins and am trying to get the PubNub iOS SDK working. So far (with the TypeScript below), I am able to successfully configure, subscribe to channels, and publish messages. I'm trying to receive messages as well by converting the "// Handle new message..." section to TypeScript as well, but haven't been able to get it working. How would I write this?
Objective-C:
// Initialize and configure PubNub client instance
PNConfiguration *configuration = [PNConfiguration configurationWithPublishKey:#"demo" subscribeKey:#"demo"];
self.client = [PubNub clientWithConfiguration:configuration];
[self.client addListener:self];
// Subscribe to demo channel with presence observation
[self.client subscribeToChannels: #[#"my_channel"] withPresence:YES];
// Handle new message from one of channels on which client has been subscribed.
- (void)client:(PubNub *)client didReceiveMessage:(PNMessageResult *)message {
NSLog(#"Received message");
}
// Publish message
[self.client publish: #{#"message": #"this is my message"}
toChannel: #"my_channel" withCompletion:^(PNPublishStatus *status) {
}];
Typescript:
// Initialize and configure PubNub client instance
this.config = PNConfiguration.configurationWithPublishKeySubscribeKey("demo", "demo");
this.client = PubNub.clientWithConfiguration(this.config);
this.client.addListener();
// Subscribe to demo channel with presence observation
this.client.subscribeToChannelsWithPresence(channels, true);
// Handle new message from one of channels on which client has been subscribed.
?
// Publish message
this.client.publishToChannelWithCompletion(msgObj, channel, function(publishStatus) {
console.log(publishStatus.data)
})
Looks like you are missing the PNObjectEventListener delegate here. You should implement the delegate and pass it's instance to addListener function for the didReceiveMessage callback to be invoked upon a new message.
For example here you can see how the core framework implements UITextViewDelegate for TextView so it could be notified upon changes and other events.
Since you are using TypeScript, take advantage of typings for your PubNub library so it may be easy for you to get find the right syntax.
Related
I'm creating few microservices using nestjs.
For instance I have x, y & z services all interconnected by grpc but I want service x to send updates to a webapp on a particular entity change so I have considered server-sent-events [open to any other better solution].
Following the nestjs documentation, they have a function running at n interval for sse route, seems to be resource exhaustive. Is there a way to actually sent events when there's a update.
Lets say I have another api call in the same service that is triggered by a button click on another webapp, how do I trigger the event to fire only when the button is clicked and not continuously keep sending events. Also if you know any idiomatic way to achieve this which getting hacky would be appreciated, want it to be last resort.
[BONUS Question]
I also considered MQTT to send events. But I get a feeling that it isn't possible for a single service to have MQTT and gRPC. I'm skeptical of using MQTT because of its latency and how it will affect internal message passing. If I could limit to external clients it would be great (i.e, x service to use gRPC for internal connections and MQTT for webapp just need one route to be exposed by mqtt).
(PS I'm new to microservices so please be comprehensive about your solutions :p)
Thanks in advance for reading till end!
You can. The important thing is that in NestJS SSE is implemented with Observables, so as long as you have an observable you can add to, you can use it to send back SSE events. The easiest way to work with this is with Subjects. I used to have an example of this somewhere, but generally, it would look something like this
#Controller()
export class SseController {
constructor(private readonly sseService: SseService) {}
#SSE()
doTheSse() {
return this.sseService.sendEvents();
}
}
#Injectable()
export class SseService {
private events = new Subject();
addEvent(event) {
this.events.next(event);
}
sendEvents() {
return this.events.asObservable();
}
}
#Injectable()
export class ButtonTriggeredService {
constructor(private readonly sseService: SseService) {}
buttonClickedOrSomething() {
this.sseService.addEvent(buttonClickedEvent);
}
}
Pardon the pseudo-code nature of the above, but in general it does show how you can use Subjects to create observables for SSE events. So long as the #SSE() endpoint returns an observable with the proper shape, you're golden.
There is a better way to handle events with SSE of NestJS:
Please see this repo with code example:
https://github.com/ningacoding/nest-sse-bug/tree/main/src
Where basically you have a service:
import {Injectable} from '#nestjs/common';
import {fromEvent} from "rxjs";
import {EventEmitter} from "events";
#Injectable()
export class EventsService {
private readonly emitter = new EventEmitter();
subscribe(channel: string) {
return fromEvent(this.emitter, channel);
}
emit(channel: string, data?: object) {
this.emitter.emit(channel, {data});
}
}
Obviously, channel can be any string, as recommendation use path style.
For example: "events/for/<user_id>" and users subscribed to that channel will receive only the events for that channel and only when are fired ;) - Fully compatible with #UseGuards, etc. :)
Additional note: Don't inject any service inside EventsService, because of a known bug.
#Sse('sse-endpoint')
sse(): Observable<any> {
//data have to strem
const arr = ['d1','d2', 'd3'];
return new Observable((subscriber) => {
while(arr.len){
subscriber.next(arr.pop()); // data have to return in every chunk
}
if(arr.len == 0) subscriber.complete(); // complete the subscription
});
}
Yes, this is possible, instead of using interval, we can use event emitter.
Whenever the event is emitted, we can send back the response to the client.
I am trying to use messaging center instead of Messenger in xamarin forms I have no idea about messaging center I tried Bellow code to subscribe and Send Message in xamarin forms
MessagingCenter.Send(this, "TodoTable", "Todo");
But I have not Idea from where I can subscribe to this message I tried bellow code :
MessagingCenter.Subscribe<TodoClass>(this, Todo, async (sender, arg) =>
{
await RefreshCommand.ExecuteAsync();
});
This is giving me error Any Help will appreciated :)
It is a quirk of XF messaging centre that (it seems) you need to know who will be sending the message, and potentially who will be receiving it.
However it can be object. The signature of subscribe is:
void MessagingCenter.Subscribe<TSender>(object subscriber, string message, Action<TSender> callback, TSender sender = null)
The trick is to subscribe thus:
MessagingCenter.Subscribe<object>(this, "messageName", Action<object> callback)
This says object or anything derived from object can be a sender, ie, everything.
However if you want to only subscribe to messages sent by a particular instance of a type:
MessagingCenter.Subscribe<MyClass>(this, "messageName", Action<MyClass> callback)
The use of the full signature is a bit suspect. Basically it is saying only if sent from the source object are subscribers who used that source object when subscribing.
MessagingCenter.Subscribe<object, string>(this, "Hi",
(sender, arg) =>
{
DisplayAlert("Message Received", "arg=" + arg, "OK");
},
BindingContext);
if you use the following to send the message it wont be received by the subscriber just above:
MessagingCenter.Send<object, string>(this, "Hi", "John");
But the following will be received
MessagingCenter.Send<object, string>(BindingContext, "Hi", "John");
Though why would you want to send a message to yourself. (Assuming the subscribe and send were in the same page in this case).
However if there were multiple pages with the exact same binding context the message will be sent to all such subscribers. Eg, pages bound to the same view model.
To improve the answer by #user2825546, if you wish to subscribe to only messages from your view-models, you need to specify the base class type when sending the message:
MessagingCenter.Subscribe<BaseViewModel, string>(this, "ShowError", (view, message) => { });
public class StartupViewModel : BaseViewModel
{
//Like this...
MessagingCenter.Send<BaseViewModel, string>(this, "ShowError", "Message");
//Or...
MessagingCenter.Send((BaseViewModel)this, "ShowError", "Message");
}
When testing, I tried to send the message as StartupViewModel, but the listener was not receiving the messages. I guessed that it would, since the class derives from the BaseViewModel.
Send Method
MessagingCenter.Send<Application>(Application.Current,"RefreshDocs");
Subscribe Method
MessagingCenter.Subscribe<Application>(Application.Current , "RefreshDocs" , (sender) =>
{
});
The goal of MVVM is to abstract away your Views from your Business Logic. This ensures great code reuse, testability, and is pretty awesome. Many MVVM Frameworks offer tools to enhance this such as data binding and dependency services to make our lives easier. These are built into Xamarin.Forms, which is awesome, but one feature less talked about is the Messaging Center. It’s entire goal is to enable ViewModels or other components to communicate with each other without having to know anything about each other besides a simple Message contract.
So for instance, let’s say you are in a master/detail setup where your MasterViewModel has a list of items and your DetailViewModel allows you to create a new item, update an item, or delete an item. When your user is on the detail page and triggers an event you need to somehow message back to your MasterViewModel that has a list of Items so the UI can react on the Master page when we navigate back.
So let’s say our MasterViewModel subscribes to “Update” and “AddNew” message events. It will then update it’s observable collection based on when it receives messages. Our DetailViewModel would then send a message in our SaveCommand to notify anyone that is subscribed to these specific messages:
public ObservableCollection<TripExpense> Expenses { get; set; }
public ExpensesViewModel()
{
Expenses = new ObservableCollection<TripExpense>();
//Subscibe to insert expenses
MessagingCenter.Subscribe<TripExpense>(this, "AddNew", (expense) =>
{
Expenses.Add(expense);
});
//subscribe to update expenxes
MessagingCenter.Subscribe<TripExpense>(this, "Update", (expense) =>
{
ExecuteUpdateExpense(expense);
});
}
private async Task ExecuteSaveCommand()
{
if (IsBusy)
return;
IsBusy = true;
//Send a message to insert/update the expense to all subscribers
if(isNew)
{
MessagingCenter.Send(expense, "AddNew");
}
else
{
MessagingCenter.Send(expense, "Update");
}
IsBusy = false;
navigation.PopAsync();
}
There you have it, messaging made easy! Don’t forget to unsubscribe if you no longer wish to receive notifications.
I'm looking for some guidance on the correct way to setup a WebSocket connection with RxJS 5. I am connecting to a WebSocket that uses JSON-RPC 2.0. I want to be able to execute a function which sends a request to the WS and returns an Observable of the associated response from the server.
I set up my initial WebSocketSubject like so:
const ws = Rx.Observable.webSocket("<URL>")
From this observable, I have been able to send requests using ws.next(myRequest), and I have been able to see responses coming back through the ws` observable.
I have struggled with creating functions that will filter the ws responses to the correct response and then complete. These seem to complete the source subject, stopping all future ws requests.
My intended output is something like:
function makeRequest(msg) {
// 1. send the message
// 2. return an Observable of the response from the message, and complete
}
I tried the following:
function makeRequest(msg) {
const id = msg.id;
ws.next(msg);
return ws
.filter(f => f.id === id)
.take(1);
}
When I do that however, only the first request will work. Subsequent requests won't work, I believe because I am completing with take(1)?
Any thoughts on the appropriate architecture for this type of situation?
There appears to be either a bug or a deliberate design decision to close the WebSocket on unsubscribe if there are no further subscribers. If you are interested here is the relevant source.
Essentially you need to guarantee that there is always a subscriber otherwise the WebSocket will be closed down. You can do this in two ways.
Route A is the more semantic way, essentially you create a published version of the Observable part of the Subject which you have more fine grained control over.
const ws = Rx.Observable.webSocket("<URL>");
const ws$ = ws.publish();
//When ready to start receiving messages
const totem = ws$.connect();
function makeRequest(msg) {
const { id } = msg;
ws.next(msg);
return ws$.first(f => f.id === id)
}
//When finished
totem.unsubscribe();
Route B is to create a token subscription that simply holds the socket, but depending on the actual life cycle of your application you would do well to attach to some sort of closing event just to make sure it always gets closed down. i.e.
const ws = Rx.Observable.webSocket("<URL>");
const totem = ws.subscribe();
//Later when closing:
totem.unsubscribe();
As you can see both approaches are fairly similar, since they both create a subscription. B's primary disadvantage is that you create an empty subscription which will get pumped all the events only to throw them away. They only advantage of B is that you can refer to the Subject for emission and subscription using the same variable whereas A you must be careful that you are using ws$ for subscription.
If you were really so inclined you could refine Route A using the Subject creation function:
const safeWS = Rx.Subject.create(ws, ws$);
The above would allow you to use the same variable, but you would still be responsible for shutting down ws$ and transitively, the WebSocket, when you are done with it.
Can't send a json message from my android app to the receiver app.
Android App
I've created my custom MessageStream and I'm using this namespace "com.jujuy.chromecast".
Once I get the channel from the session I attach MyCustomMessageStream to it then and call the method to send the message.
MyCustomMessageStream cm = new MyCustomMessageStream();
channel.attachMessageStream(cm);
cm.sendTestMessage("Hello!");
Receiver App
var receiver = new cast.receiver.Receiver(
APP-ID,
["com.jujuy.chromecast"],
"",
5);
var channelHandler = new cast.receiver.ChannelHandler("com.jujuy.chromecast"); // I think it's not necessary to use com.jujuy.chromecast
channelHandler.addEventListener(cast.receiver.Channel.EventType.MESSAGE, onMessage.bind(this));
channelHandler.addChannelFactory(receiver.createChannelFactory("com.jujuy.chromecast"));
receiver.start();
// message listener
function onMessage(event) {
document.getElementById("messageLabel").innerHTML = event.message.type;
}
After start the session () I receive this message
"failed to start application: no channel info received"
on onSessionStartFailed() method and the tv screen turns black.
I think something is wrong with the world "com.jujuy.chromecast", I saw in other examples they use cast.receiver.RemoteMedia.NAMESPACE, I'm not sure if I can change it with the namespace used in MyCustomMessageStream.
I saw in TicTacToe example they use a different way to get de CastDevice object than the documentation says. Could be this the problem?
My chromecast is whitelisted and I was able to run many examples without problem.
I used a custom receiver app to test play video and audio. Any idea?
This is most likely because your namespace on the sender doesn't match the namespace on the receiver. You need to make sure you pass it in as a parameter to the constructor for your custom MessageStream.
Here's an example:
public class MyCustomMessageStream extends MessageStream {
private static final String APP_NAMESPACE = "com.jujuy.chromecast";
protected MyCustomMessageStream(){
super(APP_NAMESPACE);
}
public final void sendTestMessage(String message){
// ...
}
//...
}
You shouldn't need to use the remote media namespace to send messages, that's for media playback. Here's a more in depth answer: https://stackoverflow.com/a/18499253/1839298
At first I couldn't get my package namespace to work, you might try a single word namespace, like 'TEST', to see if you can get that working then proceed from there.
I would like to achieve a similar task, in which I want to re-establish my push notification channel using background agent. Is it possible?
I am using the following code inside my ScheduledAgent, but its not working. If I cannot access the channel APIs, is there any alternative to that? How have popular apps like whats-app and others been able to achieve this? Please help me.
Can you advise me an alternative? How can I update my user that there is something new for him from my server without using this approach?
protected override void OnInvoke(ScheduledTask task)
{
//TODO: Add code to perform your task in background
HttpNotificationChannel pushChannel = HttpNotificationChannel.Find("HikeApp");
if (pushChannel == null)
{
pushChannel = new HttpNotificationChannel("HikeApp");
// Register for all the events before attempting to open the channel.
pushChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(PushChannel_ChannelUriUpdated);
pushChannel.Open();
pushChannel.BindToShellTile();
pushChannel.BindToShellToast();
}
else
{
// the channel was already open, so just register for all the events.
pushChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(PushChannel_ChannelUriUpdated);
}
#if DEBUG_AGENT
ScheduledActionService.LaunchForTest(task.Name, TimeSpan.FromSeconds(1));
#endif
NotifyComplete();
}
As per the list of Unsupported APIs in Background Agents, it is not possible to do anything in the Microsoft.Phone.Notification namespace, which includes everything to do with push notifications, from a scheduled agent.