Create your go project.
Get the package:
go get github.com/lisomatrix/channels/channels
Paste this code in you main file
package main
import (
"log"
"github.com/lisomatrix/channels/channels"
"github.com/lisomatrix/channels/channels/auth"
"github.com/lisomatrix/channels/channels/storage/pgxsql"
)
func main() {
// Make sure the config.yaml is at the same path as the binary
config, err := channels.NewConfig("./config.yaml")
if err != nil {
log.Fatal(err)
}
// Since the components are pluggable, you must initialize them before
// In this case we are using the default ones, so just set their properties
// The InitEngineAndStart function initializes the engine and binds routes
auth.SetSecret(config.JWTSecret)
pgxsql.PGXSetConnectionParams(config.Database.User, config.Database.Password, config.Database.Host, config.Database.Port, config.Database.DB)
// Initializes the engine and starts
channels.InitEngineAndStart("0.0.0.0", "8090")
}
Copy this file and put it in the same place as the binary, fill your postgres connection fields. It has redis as cache and presence but it will still work fine even if you don't have it installed.
Now create the schema in this sql file.
Go to JWT.IO and paste the json below on payload a fill the same secret as in config.yaml.
{
"Role": "Admin",
"ClientID": "123",
"AppID": "123"
}
Copy the token and you can start working with Channels.
Learn on to create and app, client and channels in the docs
A simple server that treats communication like channels
Check the docs here: https://lisomatrix.github.io/Channels
Channels is a Golang server and some client libraries, mainly for IOS, Android and Javascript, that allow real time communication in channels.
Think of front facing publish subscribe system.
Think of a Pub/Sub with persistence and presence
This project was created to serve the purpose of creating a front facing broker and at the same time have the ability to get previous/history data.
For example: We have a chat app, with Channels we receive messages in real time, but we can also open our account in another device and get the messages history along with new updates!
- App Separation by AppID
- Publish and Subscribe Channels
- Real time events with WebSockets
- Publishing with HTTP
- Subscribe with SSE (Soon)
- HTTP Channel Events Sync (On channels with persistence)
- Channel Features:
- Close and Open (Something like freeze)
- Private or Open (Client needs to be added or can just subscribe)
- Optional persistence (for later access and sync)
- Optional client presence
- Client joined/left
- Client online/offline
- Multiple servers with Redis
- Pluggable parts:
- Database (Using PostgreSQL Currently)
- Cache (Using Redis and creating Ledis)
- Publisher (Using Redis)
- Presence (Using Redis)
The file app.go provides a function Start(host string, port string) that starts the Channel Servers with the default settings, currently the default settings are PostgreSQL for storage and the rest is using Redis, these can be changed!
Before starting we must provide the connection settings to PostgreSQL, we can do that by providing an config.yaml, and the SQL Schema here.
After that make sure you have your redis running locally and the server should start!
Looking at the file app.go, we see that we need instances of the structs that implement the following interfaces:
We currently have PostgresSQL Storage implementation (check here for a database/sql implementation). We also have Redis Presence implementation, Redis Publisher implementation and Redis Cache implementation, a Ledis cache implementation is in the works!
Looking again at app.go, we just need to initialize the Engine, call core.InitEngine(storage, cache, publisher, presence), and now you can use core.Engine for the Channels main logic, the object is accessible everywhere with core.GetEngine() and holds the interfaces provided at init.
In case you pretend to make your own HTTP handlers or some custom logic you can use some helpers like this Channel Helper, Client Helper and Hubs Handler (this one can be accessed with core.GetEngine().HubsHandler) to avoid repeating yourself.
First, the authentication is done by your application, Channels only stores client usernames and IDs (more information on Creating Client Section).
After authenticating a user, you must provide him a JWT Token
, we'll see how they should look like in a second.
The Channels has three roles being these:
Super Admin
: can do anything since he is super!Admin
: can do anything in it's own app.Client
: can publish and subscribe to allowed channels on their associated App
The attribution of the roles must be made by your server, app or manually. The roles are provided by the JWT Token
, so a client can be made a Admin
by having a token saying so!
For Admin
and Client
we need their Role
, ClientID
and AppID
, for Super Admin
we need Role
and ClientID
.
!> Important: Although we say Super Admin
without the _
the value in token must be Super_Admin
{
"Role": "Admin", // Here you can change to Client or Super_Admin
"ClientID": "123",
"AppID": "123" // If the role is Super_Admin we don't need this field
}
An app is a way of separating clients and channels, any publish by the a user in App 123
won't intefere with the App 321
, they can even have the same channel names, but for Channels they are completely different. Also, every client has an AppID associated!
!> Note: Almost all requests require the AppID to be sent in the headers, except WebSocket connections that also allows in the url params.
!> Note: Operations on Apps require the Role Super Admin
for creating, deleting and getting apps, on update app you can use the Role Admin
to update app associated with the Admin
client, or just use Super Admin
.
Since everything is separated by Apps, how do we create them?
It is super simple, we just need a simple POST
on /app
.
Headers:
Authorization: token
Body:
{
"AppID": "what any id you want",
"Name": "Any name you want"
}
Now you either get 200 OK
or 409 Conflict
(in case it already exists), in case you get 500 Internal Server Error
contact me :) .
Since app have little information, you can only update their name with a even simple PUT
on /app/{AppID}
Headers:
Authorization: token
Body:
{
"Name": "Any name you want"
}
You should get a 200 OK
or 404 Not Found
.
Deleting an app, as you might expect, it's just a DELETE
on /app/{1234}
.
Headers:
Authorization: token
You should get a 200 OK
or 404 Not Found
.
You also can get all apps by sending a GET
on /app
.
Headers:
Authorization: token
Result:
{
"Apps": [
{
"AppID": "1234",
"Name": "App1"
},
{
"AppID": "12344",
"Name": "App2"
},
{
"AppID": "123",
"Name": "App3"
}
]
}
Clients, as name suggests are users that connect to Channels, the clients can be apps, servers and web apps.
Each client can have any number of devices connected at the same time.
However, while Channels knows about clients, their authentication and management is up to you. Channels doesn't authenticate clients with their credentials, it's up to you to provide a JWT Token
to the clients so they can connect to Channels.
You can see more about authentication here
!> All client requests require an Admin
role in order to manage the clients in the Admin
app, while Super Admin
can change all apps.
So in order for Channels to know about your clients you need to tell it.
For Example: You register a new client and you tell Channels about it.
So you can just POST
on /client
.
Clients have a
extra
field where you can put anystring
value including json, you should keep it small.
Headers:
Authorization: token
AppID: appID // The appID the client will belong
Body:
{
"clientID": "what any id you want",
"username": "Any name you want",
"extra": "A string about your user, it can be JSON"
}
Now you either get 200 OK
or 409 Conflict
(in case it already exists).
Like we saw above, clients have little information, which you can make up using the extra field, we recommend to keep it as tiny as possible, but it's up to you.
Anyway, you can update the client username
and extra
by sending a PUT
on /client/{clientID}
.
Headers:
Authorization: token
AppID: appID // The appID the client belongs
Body:
{
"username": "Any name you want",
"extra": "A string about your user, it can be JSON"
}
Now you either get 200 OK
or 404 Not Found
.
Losing a client is very sad, and we are sorry for you if you are checking this part.
But you can clear your tears and do it by send a DELETE
on /client/{clientID}
Headers:
Authorization: token
AppID: appID // The appID the client belongs
Now you always get 200 OK
, if it existed now it doesn't, if it didn't in the first place Channels did its best.
You can see how much your user base has grown by asking Channels for clients.
You can request all clients of all apps or all clients of a app. For the first simply do not include the AppID
header.
!> Note: Only a Super Admin can get all clients from all apps, while Admin can only get the clients of it's apps.
You just send a GET
on /client
Headers:
Authorization: token
AppID: appID // The appID you want to check, our don't send it to get all
Result:
{
"clients": [
{
"ID": "123",
"Username": "lisomatrix",
"AppID": "123",
"Extra": "test_extra"
},
{
"ID": "55",
"Username": "lisomatrix",
"AppID": "123",
"Extra": "{ \"Im_Json\": \"Hi Json, Im Dad\" }"
}
]
}
Like the name mentions, Channels separates things by ... channels (surprising I know).
A channel can be any string like my_public_channel
or my_not_so_private_channel
. You can have repeated channels as long as they are in different apps. Also, currently we don't support topics like MQTT with +
and #
, a channel is just a string, think of it like and ID.
Currently, channels have some featues like:
- Persistence: Store all events published*
- Private: Requires and
Admin
orSuper Admin
to join/remove them - Presence: Check for client connections and clients joined/removed to/from channel.
Also you have an extra
field just like with clients to store additional data for a channel (Yes, it can be JSON).
!> Channels Management can only be made by Admin for it's App or Super Admin for every one
So, in order to start publishing and subscribing and sending events you first must define the channel where you will do it.
!> Important: This is the hardest request in Channels, make sure you understand it :)
You just send a POST
to /channel
.
Headers:
Authorization: token
AppID: appID // The appID the channel will belong
Body:
{
"channelID": "what any id you want",
"name": "Any name you want",
"persistent": true,
"private": false,
"presence": true,
"extra": "A string about the channel, it can be JSON"
}
And you should get a 201 Created
or 409 Conflict
.
You can delete channels, and if they are persistent every single event published to this channel will be deleted!
so ... be ... careful .. like .. VERY!
You just send a DELETE
to /channel/{channelID}
.
Headers:
Authorization: token
AppID: appID // The appID the channel belongs
And you should get a 200 OK
or 404 Not Found
.
Instead of deleting a channel, if you just want events to stop being published then closing a channel is the right choice!
You just send a POST
to /channel/{channelID}/close
.
Headers:
Authorization: token
AppID: appID // The appID the channel belongs
And you should get a 200 OK
or 404 Not Found
.
And you can't publish to the channel anymore!.
If you think that a channels feels alone you can open it again any time.
You just send a POST
to /channel/{channelID}/open
.
Headers:
Authorization: token
AppID: appID // The appID the channel belongs
And you should get a 200 OK
or 404 Not Found
.
And you can start publishing to the channel again!
You can request all channels of all apps or all channels of a app. For the first simply do not include the AppID
header.
!> Note: Only a Super Admin can get all channels from all apps, while Admin can only get the channels of it's apps.
You just send a GET
on /channel/open
for open channels or /channel/private
for private channels.
!>Note: You can't request both public and private in one request (atleast yet).
Headers:
Authorization: token
AppID: appID // The appID you want to check, our don't send it to get all
Result:
{
"channels": [
{
"id": "",
"appID": "123",
"name": "Hi_Im_Channel_One",
"createdAt": 1615244965,
"isClosed": false,
"extra": "no extra",
"isPersistent": true,
"isPrivate": false,
"isPresence": false
},
{
"id": "1234",
"appID": "123",
"name": "Hi_Channel_One_Im_Dad",
"createdAt": 1615734846,
"isClosed": false,
"extra": "no extra",
"isPersistent": true,
"isPrivate": true,
"isPresence": false
},
{
"id": "123",
"appID": "123",
"name": "Hi_Hi_Channel_One_Im_Dad_Im_Dad__OH_WAIT",
"createdAt": 1615734842,
"isClosed": false,
"extra": "",
"isPersistent": true,
"isPrivate": false,
"isPresence": true
}
]
}
For private channels, the Admin
or Super Admin
must add the users, of course this process could be made by a server with a JWT Token
with one of those roles.
Connected clients that are affected by this change are notified by receiving a
ADD_CHANNEL
orREMOVE_CHANNEL
event, if a client is connected to a channel you just removed Channels will take care of unsubscribing the client, or if the client is connected and received access to a new channel it can subscribe right away!
Also, on channels with presence enabled, connected clients will be notified that user has joined/left a channel.
So after that brief explanation, we can join or remove a client by send a POST
to /channel/{channelID}/join/{clientID}
to join a client or /channel/{channelID}/leave/{clientID}
to remove.
Headers:
Authorization: token
AppID: appID // The appID the channel belongs
The response should be 200 OK
or 404 Not Found
.
Publishing should mostly be done with WebSockets, but some times we need to make a simple POST
and a WebSocket would be overkill.
So we in order to publish and event with HTTP
just send a POST
to /channel/{channelID}/publish
.
Headers:
Authorization: token
AppID: appID // The appID the channel belongs
BODY
{
"payload": "Your_Json_Payload",
"eventType": "Any event type you want" // This can be used to know what to expect from the payload
}
And you get 200 OK
or 404 Not Found
or in case the channel is closed 400 Bad Request
.
A lot of time our networks go down or our mobile device runs out of battery and we stop receiving messages from our friends including our friend Channels :(
But as soon we get up online we can start getting all data we lost while we were away :)
!> This only works with channels with persistence enabled
We can do it some ways:
- Get last event channels: last 200 messages for example
- Get all events since specific time: based on a timestamp
- Get last events since specific time: based on a timestamp and a given amount to retrieve
- Get all events between to specific times: also base on timestamps and they are inclusive
!> Before retrieving syncing data make sure you connection is established and you are subscribed so we can prevent missing events published between the time your fetched and subscribed!
!> Also, events are stored in database in batches, while they have timers there is a very small chance that you might lose some events that were delayed and only stored just right after you fetched them, were are not sure this happens yet, but could happen if the server or database are under higher load and they delay the storing of the messages.
If it is critical to receive those events you could fetch the events between the last timestamp of the fetched event and the first timestamp you get from the WebSocket.
The events should still be stored, but not at the time you fetch them.
But once again, we are not sure yet!
In order get last events from a channel we just send a GET
to /last/{channelID}/{amount}
, the amount specified how much events you wan't to retrieve.
This can be useful in a chat application in Browsers, you just need the last messages right away.
Also the request it's pretty fast, it will try to fetch the events from cache first, but even when hitting the database with channel with more than 2 Million Rows it takes up to 20 ms returning 300 events.
This test was made in a consumer HDD with Database and Channels running locally and using the PGX implementation!
Headers:
Authorization: token
AppID: appID // The appID the channel belongs
Result:
{
"events": [
{
"senderID": "123",
"eventType": "testing publish type",
"payload": "can_be_json_or_not",
"channelID": "123",
"timestamp": 1615737318
},
{
"senderID": "123",
"eventType": "testing publish type",
"payload": "can_be_json_or_not",
"channelID": "123",
"timestamp": 1615735212
}
]
}
In order get last events since a given timestamp from a channel we just send a GET
to /last/{channelID}/{amount}/last/{timeStamp}
, the amount specified how much events you wan't to retrieve.
This request is also pretty fast, actually even faster than the one before, im the same conditions it takes ~4ms to return 500 events!
Headers:
Authorization: token
AppID: appID // The appID the channel belongs
Result:
{
"events": [
{
"senderID": "123",
"eventType": "testing publish type",
"payload": "can_be_json_or_not",
"channelID": "123",
"timestamp": 1615737318
},
{
"senderID": "123",
"eventType": "testing publish type",
"payload": "can_be_json_or_not",
"channelID": "123",
"timestamp": 1615735212
}
]
}
In order get events since a given timestamp from a channel we just send a GET
to /c/{channelID}/sync/{timeStamp}
.
This request performance depends on the amount of events are stored after (it is inclusive) the given timestamp, so be a bit careful with the timestamps or you could literally download an entire channel history!
If that's what you want... well ... go ahead!
Headers:
Authorization: token
AppID: appID // The appID the channel belongs
Result:
{
"events": [
{
"senderID": "123",
"eventType": "testing publish type",
"payload": "can_be_json_or_not",
"channelID": "123",
"timestamp": 1615737318
},
{
"senderID": "123",
"eventType": "testing publish type",
"payload": "can_be_json_or_not",
"channelID": "123",
"timestamp": 1615735212
}
]
}
In order get events between two timestamps from a channel we just send a GET
to /sync/{channelID}/{sinceTimestamp}/to/{upToTimestamp}
, the results are inclusive!
You should expect similar performance to the previous request
Headers:
Authorization: token
AppID: appID // The appID the channel belongs
Result:
{
"events": [
{
"senderID": "123",
"eventType": "testing publish type",
"payload": "can_be_json_or_not",
"channelID": "123",
"timestamp": 1615737318
},
{
"senderID": "123",
"eventType": "testing publish type",
"payload": "can_be_json_or_not",
"channelID": "123",
"timestamp": 1615735212
}
]
}
Channels can be used with multiple servers using a Pub/Sub system... wait ... ain't this Pub/Sub already?
Well, you are right, but pushing the work to a already battle tested and lightweight software like Redis works to, and very well acctually, and saves us time for other things, and we can also use it for caching!
But, if you don't like the solution and want to implement other thing other than Redis go ahead! You just need to implement this interface and you are good to go!
Well, it's acctually pretty simple.
First let's describe how Channels works with apps and channels.
When a new user connects to Channels it checks if the Hub
of the app, which the client is associated, is currently active in that server, if not it creates a Hub
instance.
Think of a
Hub
like a class that routes messages to channels and subscribes clients to channels. EachHub
represents an app, and we can have lots of apps in one server and just one in another. They are created as needed, something along the lines of lazy loading.
Inside each Hub
we can have a lot of channels
or maybe none. Like the the app they are loaded as needed. Once a channel
is loaded Channels
will subscribe to a topic like {AppID}:{ChannelID}
in Redis
(with the current implementation) and start to send to the subscribed client the external data they are getting.
For presence information updates, pub/sub is used, but also the K/V store in Redis
to store information and use it as shared data, this helps to detect if a user is offline by checking if there aren't any devices associated with a user that are still connected.
Another question you might have is, what happens if Redis goes down? Well, some events could not be broadcasted to other instances, and presence information would be incorrect, but you could also try redis clustering, or bring your publisher implementation.
And what happens if we lose access to database? In that case I'm sorry, but for now your events won't be stored, if we stored them in memory the server could go down pretty fast with lack of memory.
Event if both things fail, Channels should not crash, but won't have the desired results either.
If you want to connect to channels with your devices, then this is the right place for you!
First get the code from here and check the authentication section for creating a JWT Token
.
After getting the token, initialize the SDK with:
ChannelsSDK.initialize(context, "wss:https://server_url:port","AppID", "your_jwt_Token");
This should connect to the server if both ulr and credentials are right.
To get the client channels you can retrieve the channels that are public or the ones that are private. To do that just use the following:
ChannelsSDK
.getInstance()
.getChannelService()
.getPublicChannels(new GetChannelsCallback() { // For Private just replace the word public
@Override
public void onSuccess(List<ChannelInfo> channelInfos) {
// Here you get your channels information
}
@Override
public void onError(Throwable throwable) {
// In case an error happens
}
});
With the ChannelInfo
you can get a Channel
, with the static method Channel.fromChannelInfo(ChannelInfo)
;
We will see what you can do with it in a second.
You can know you when lost access to a channel for received with:
ChannelsSDK.setChannelsListener(new ChannelsListener() {
@Override
public void onChannelRemoved(String channelID) {
// When your remove a client from a channel
}
@Override
public void onChannelAdded(String channelID) {
// When you add a channel to a client
}
});
The object Channel
is the object you will use the most, with it you can subscribe, publish, get other clients presence and get events.
First, in order to get a instance you can use the ChannelInfo
you get from getPublicChannels()
or getPrivateChannels()
with Channel.fromChannelInfo(ChannelInfo)
, or after you get the public or private channels you can get one with ChannelsSDK.getChannel("channelID");
, if none is found it will return null!
!> Please don't try to create an instance out of the ways we provide, but in case you do register it with ChannelsHandler.getInstance().registerChannel(channel);
or you won't receive any updates.
Once you have a Channel
you subscribe with:
m_channel.subscribe(new ChannelListener() {
@Override
public void onPublishAcknowledge(RequestAcknowledge requestAcknowledge) {
// When you publick with ACK enabled
}
@Override
public void onSubscribed() {
// Callback so you know when you are subscribed
}
@Override
public void onChannelEvent(ChannelEvent event) {
// When your receive a message
}
@Override
public void onRemoved() {
// When you lost access to a channel
}
});
For a channel
with presence enabled you have another callback, a long one!
m_channel.setPresenceListener(new ChannelPresenceListener() {
@Override
public void onClientJoinChannel(ClientJoin clientJoin) {
// When a user is added to a channel
}
@Override
public void onClientLeaveChannel(ClientLeave clientLeave) {
// When a user is removed from a channel
}
@Override
public void onOnlineStatusUpdate(OnlineStatusUpdate onlineStatusUpdate) {
// You receive an event when a client goes online or offline
// And you can get the all the presence information with:
Map<String, ClientPresenceStatus> presenceStatusMap = m_channel.getPresences();
// The key in the map is the ClientID
}
@Override
public void onInitialStatusUpdate() {
// After you subscribe to a channel, the channel should send it's
// Current presence state to you
// When you get it, this 'little' call is called and here you can get presences with:
Map<String, ClientPresenceStatus> presenceStatusMap = m_channel.getPresences();
}
});
You should this set this listener before subscribing but you still can get the presence status with the
.getPresences()
.
To publish messages is simple as:
m_channel.publish("event_type", "any_payload", true);
/* Set to true when you want to get a confirmation */
!> Important: If your event doesn't request a confirmation, Channels
will consider that the event is not important to store on channels with persistence enabled!
For getting events it's as simple as:
You can check other types of getting events on the section Synchronization
ChannelsSDK.getChannelLastEvents("channelID", 20 /* Amount */, new GetChannelEventsCallback() {
@Override
public void onSuccess(List<ChannelEvent> events) {
}
@Override
public void onError(Throwable throwable) {
}
});
or
m_channel.getLastEvents(10 /* Amount */, new GetChannelEventsCallback() {
@Override
public void onSuccess(List<ChannelEvent> events) {
}
@Override
public void onError(Throwable throwable) {
}
});
If you want to connect to channels with your browser, then this is the right place for you!
First get the code from here and check the authentication section for creating a JWT Token
.
After getting the token, initialize the SDK with:
let channelsSDK = new ChannelsSDK({
url: ':https://url:port', // Don't fill behind the :https://
appID: 'AppID',
token: 'JWT Token',
secure: false // If should be WSS or WS and HTTPS or HTTP
});
You need to keep the object around and using for almost everything
To get the client channels you can retrieve the channels that are public or the ones that are private. To do that just use the following:
The package works in browser, it uses the native WebSocket, if you can replace it, then it can work on Node
channelsSDK.fetchPublicChannels().then(channels => { // Replace public with private for private channels
});
You can know you when lost access to a channel for received with:
channelsSDK.setOnChannelAdded((channelID) => {
// When you receive access to a channel
})
channelsSDK.setOnChannelRemoved((channelID) => {
// When you lose access to a channel
})
The object Channel
is the object you will use the most, with it you can subscribe, publish, get other clients presence and get events.
First, in order to get a instance you can get from getPublicChannels()
or getPrivateChannels()
or after you get the public or private channels you can get one with channelsSDK.getChannel("channelID");
, if none is found it will return null!
Once you have a Channel
you subscribe with:
channel.subscribe(() => {
// Callback for when the subscribe is confirmed
})
And you can get events with:
channel.setOnMessage((event) => {
// Callback for the event
});
For presences events you have:
// On initial status from the channel
channel.setOnInitialStatusUpdate(() => {
// You can get the presence with
channel.getPresencesStatus();
});
// When status changes
channel.setOnOnlineStatusUpdate((statusUpdate) => {
// The user status that changed
});
// When a user is added to the channel
channel.setOnJoin((join) => {
});
// When a user is removed from the channel
channel.setOnLeave((leave) => {
});
To publish messages is simple as:
channel.publish("my_event", "my_payload_can_be_json", () => {
// Published confirmation
});
// If you don't want the confirmation or don't want the event to be stored pass null on the callback
channel.publish("my_event", "my_payload_can_be_json", null);
!> Important: If your event doesn't request a confirmation, Channels
will consider that the event is not important to store on channels with persistence enabled!
For getting events it's as simple as:
You can check other types of getting events on the section Synchronization
// Get last X (5 in this case) events
channel.fetchLastEvents(5).then(events => {
console.log(events);
});
// Get X (5 in this case) since given timestamp (inclusive)
channel.fetchLastEventsSince(5, 1615570823).then(events => {
console.log(events);
});
// Get events since timestamp (inclusive)
channel.fetchEventsSince(1615570823).then(events => {
console.log(events);
});
// Since timestamp to Up to timestamp (inclusive)
channel.fetchEventsBetween(1615570823, 1615570823).then(events => {
console.log(events);
});
If you want to connect to channels with your devices, then this is the right place for you!
First get the code from here and check the authentication section for creating a JWT Token
.
After getting the token, initialize the SDK with:
// Not for the URL don't put schema just for example 192.168.1.2 or example.pt
ChannelsSDK.shared().initialize(url: "example.com:port", appID: "your app ID", secure: false)
// After initializing you can connect with
ChannelsSDK.shared().connect(token: "your jwt token", deviceID: nil) // nil deviceID to auto generate
This should connect to the server if both ulr and credentials are right.
To get the client channels you can retrieve the channels that are public or the ones that are private. To do that just use the following:
let channelsAPI = ChannelsSDK.shared().getChannelsAPI()
channelsAPI.getPublicChannels() { channels, isOK in // Change the Public to private for private channels
if (isOK) {
print("Public \\(channels!)")
}
}
You can know you when lost access to a channel for received by implementing ChannelsAccessListener
protocol and:
// Set listener
ChannelsSDK.shared().setChannelAccessListener(listener: self)
// And you get your events
func onChannelAdded(channelID: String) {
// When you add a channel to a client
}
func onChannelRemoved(channelID: String) {
// When your remove a client from a channel
}
The object Channel
is the object you will use the most, with it you can subscribe, publish, get other clients presence and get events.
First, in order to get a instance you can get from getPublicChannels()
or getPrivateChannels()
or after you get the public or private channels you can get one with ChannelsSDK.shared().getChannel(channelID: "channelID");
, if none is found it will return null (I mean NIL!)!
Once you have a Channel
you subscribe by implementing ChannelListener
and:
channel.subscribe(listener: self)
// And you get your events
func onPublishAcknowledge(ack: RequestAcknowledge) {
/* Confirmation that publish was received */
}
func onSubscribed() {
/* Confirmation that you subscribed */
}
func onChannelEvent(event: EventChannel) {
/* Handle new channel events */
print("Event received of type \(event.eventType) with data \(event.payload)");
}
func onRemoved() {
/* You lost access to channel */
}
For a channel
with presence enabled you have another callback, a long one!
func onClientJoinChannel(clientJoin: ClientJoined) {
/* When client is added to channel */
}
func onClientLeaveChannel(clientLeave: ClientLeft) {
/* When client is removed from channel */
}
func onOnlineStatusUpdate(onlineStatusUpdate: ClientPresenceStatus) {
/* When client goes offline or online */
}
func onInitialStatusUpdate() {
/* When you received the initial presence state from a channel when you subscribe */
let presences: Dictionary<String, ClientPresenceStatus> = channel!.getPresences()
print(presences["clientID"]?.status) // IsOnline
print(presences["clientID"]?.timestamp) // last timestamp
}
You should this set this listener before subscribing but you still can get the presence status with the
.getPresences()
.
To publish messages is simple as:
channel.publish(eventType: "event_type", payload: "you_payload_can_be_json", notify: true)
/* Set to true when you want to get a confirmation */
!> Important: If your event doesn't request a confirmation, Channels
will consider that the event is not important to store on channels with persistence enabled!
For getting events it's as simple as:
You can check other types of getting events on the section Synchronization
let channelsAPI = ChannelsSDK.shared().getChannelsAPI()
channelsAPI.getLastChannelEvents(channelID: "channelID", amount: 10) { events, isOK in
if (isOK) {
print("Events: \\(events!)")
}
}
or
chann.getLastEvents(amount: 50) { events, isOK in
if (isOK) {
print("Events: \\(events!)")
}
}