Search Results for

    Show / Hide Table of Contents

    Looking for WebSync documentation? This product is discontinued and has been replaced by LiveSwitch. Please contact Sales to learn more and plan your migration today.

    Migrate from WebSync Cloud to LiveSwitch Cloud

    LiveSwitch extends the WebSync to support the operations that are required by its new features. The extended API combines expressiveness with simplicity and offers full support for WebSockets, cross-platform messaging, and all the features that you've come to expect from WebSync.

    Cheat Sheet

    WebSync Cloud → LiveSwitch Cloud
    https://v4.websync.fm/websync.ashx https://cloud.liveswitch.io
    Client.RequestUrl Client.GatewayUrl
    Client.DomainName Client.ApplicationId
    Client.DomainKey Client.SharedSecret
    Client.Connect Client.Register
    Client.Disconnect Client.Unregister
    Client.Bind Client.Update
    Client.Unbind Client.Update
    Client.Subscribe Client.Join
    Client.Unsubscribe Client.Leave
    Client.Publish Channel.SendMessage
    SubscribeArgs.OnReceive Channel.OnMessage
    SubscribeArgs.OnClientSubscribe Channel.OnRemoteClientJoin
    SubscribeArgs.OnUserJoin Channel.OnRemoteClientJoin
    SubscribeArgs.OnClientUnsubscribe Channel.OnRemoteClientLeave
    SubscribeArgs.OnUserLeave Channel.OnRemoteClientLeave
    Publisher Web API

    Getting Started

    1. First, create a LiveSwitch Cloud account if you don't have one.
    2. Create an Application using the LiveSwitch Cloud Console.
    3. Download the latest SDK and add it to your app.

    You're ready to migrate.

    Migrating involves converting code that references WebSync into code that references the LiveSwitch equivalents.

    Not sure where to start? Just remove the WebSync SDK from your app and let your IDE tell you where to look.

    Key Differences

    There are some key differences to be aware of when migrating:

    1. Registering in LiveSwitch == connecting in WebSync.
    2. Unregistering in LiveSwitch == disconnecting in WebSync.
    3. Joining a channel in LiveSwitch == subscribing to a channel in WebSync.
    4. Leaving a channel in LiveSwitch == unsubscribing from a channel in WebSync.
    5. Updating a client in LiveSwitch == binding records to or unbinding records from a client in WebSync.
    6. Sending a message in LiveSwitch == publishing a message in WebSync.
    7. LiveSwitch uses a promise-based async/await model, as opposed to WebSync's callback model.
    8. Client requests are authorized by JSON web tokens.

    Servers

    LiveSwitch uses https://cloud.liveswitch.io as its signalling endpoint, also called the gateway URL. This is equivalent to the "request URL" in WebSync, which is https://v4.websync.fm/websync.ashx for WebSync Cloud.

    Tokens

    Tokens are used to authorize Register and Join requests. Register tokens authorize the initial connection, while Join tokens authorize a connected client to join a new channel. Register tokens can include a list of channels to join right away as part of the initial connection.

    LiveSwitch tokens are simple JSON Web Tokens, an open, industry-standard method for representing claims securely between two parties.

    Your LiveSwitch Cloud shared secret is used to sign tokens. It is very important to protect this shared secret. Generating tokens in a server-side API that can provide tokens to authorized users of your system is strongly recommended.

    The simplest way to generate a token is to use the LiveSwitch SDK: LiveSwitch SDK

    var registerToken = fm.livewitch.Token.generateClientRegisterToken(...);
    
    var joinToken = fm.livewitch.Token.generateClientJoinToken(...);
    

    You can also use a third-party library, like jsonwebtoken: Third-Party Library

    var sharedSecret = '...'; 
    
    var registerToken = jwt.sign({
        type: 'register',
        applicationId: '...',
        userId: '...',
        deviceId: '...',
        channels: [{ // optional, any count
            id: '...'
        }],
        clientRoles: ['...'], // optional, any count
        region: '...' // optional
    }, sharedSecret, {
        algorithm: 'HS256',
        expiresIn: 300 // seconds
    });
    
    var joinToken = jwt.sign({
        type: 'join',
        applicationId: '...',
        userId: '...',
        deviceId: '...',
        clientId: '...'
        channel: [{ // exactly 1 required
            id: '...'
        }]
    }, sharedSecret, {
        algorithm: 'HS256',
        expiresIn: 300 // seconds
    });
    

    Migrate

    Register (Connect)

    Registering in LiveSwitch is equivalent to connecting in WebSync. Registering with the server requires a signed token for authorization.

    Your LiveSwitch shared secret is equivalent to your WebSync domain key. It is private information for your servers only.

    Your LiveSwitch Application ID is equivalent to your WebSync domain name. It is public information to include in your client apps.

    Before (WebSync)

    var domainName = "localhost";
    var domainKey = "11111111-1111-1111-1111-111111111111";
    
    var client = new Client(domainKey, domainName);
    client.Connect(new ConnectArgs
    {
        OnSuccess = (connectSuccessArgs) =>
        {
            Console.WriteLine("Success!");
        },
        OnFailure = (connectFailureArgs) =>
        {
            Console.WriteLine($"Failure: {connectFailureArgs.Exception}");
        }
    });
    

    After (LiveSwitch)

    var applicationId = "12345678-1234-1234-1234-1234567890ab";
    var sharedSecret = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
    
    var client = new Client("https://cloud.liveswitch.io", applicationId);
    var registerToken = Token.GenerateClientRegisterToken(applicationId, null, null, client.Id, null, new ChannelClaim[0], sharedSecret);
    try
    {
        await client.Register(registerToken);
        Console.WriteLine("Success!");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Failure: {ex}");
    }
    

    Notice the null parameters when creating the Register token. These represent additional metadata (user ID, device ID, client roles) that can be bound to the client and verified on the server.

    Also notice that an array of channels can be provided when registering. If provided, the server joins the client to these channels on registration. A Channel array is returned from the call to Register.

    Unregister(Disconnect)

    Unregistering in LiveSwitch is equivalent to disconnecting in WebSync. Before (WebSync)

    client.Disconnect(new DisconnectArgs
    {
        OnComplete = (disconnectCompleteArgs) =>
        {
            Console.WriteLine("Done!");
        }
    });
    

    After (LiveSwitch)

    await client.Unregister();
    Console.WriteLine("Done!");
    

    Update (Bind)

    Updating in LiveSwitch is equivalent to binding and unbinding in WebSync.

    LiveSwitch takes a simple approach to client metadata that allows it to scale better than WebSync. Each client has a UserAlias, a DeviceAlias, and a Tag property. These values can be changed anytime by calling Update. Any of these three string properties can have any value, but the intention is that UserAlias represents the user's human-readable display name, DeviceAlias represents the device's human-readable display name, and Tag represents any additional metadata the app needs. Before (WebSync)

    client.Bind(new BindArgs(new[]
    {
        new Record("userName", Serializer.SerializeString("John Doe")),
        new Record("deviceName", Serializer.SerializeString("John's Phone")),
        new Record("otherData", Serializer.SerializeString("something else"))
    })
    {
        OnSuccess = (bindSuccessArgs) =>
        {
            Console.WriteLine("Success!");
        },
        OnFailure = (bindFailureArgs) =>
        {
            Console.WriteLine($"Failure: {bindFailureArgs.Exception}");
        }
    });
    

    After (LiveSwitch)

    var config = client.Config;
    config.UserAlias = "John Doe";
    config.DeviceAlias = "John's Phone";
    config.Tag = "something else";
    try
    {
        await client.Update(config);
        Console.WriteLine("Success!");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Failure: {ex}");
    }
    

    Join (Subscribe)

    Joining in LiveSwitch is equivalent to subscribing in WebSync. Joining a channel requires a signed token for authorization.

    LiveSwitch introduces a new Channel type that is returned by the call to Join. Received messages are delivered to channel-level event handlers. Before (WebSync)

    var channelId = "/my/channel";
    client.Subscribe(new SubscribeArgs(channelId)
    {
        OnSuccess = (subscribeSuccessArgs) =>
        {
            Console.WriteLine("Success!");
        },
        OnFailure = (subscribeFailureArgs) =>
        {
            Console.WriteLine($"Failure: {subscribeFailureArgs.Exception}");
        },
        OnReceive = (subscribeReceiveArgs) =>
        {
            Console.WriteLine($"Received a message from {subscribeReceiveArgs.PublishingClient.ClientId}: {Serializer.DeserializeString(subscribeReceiveArgs.DataJson)}");
        }
    });
    

    After (LiveSwitch)

    var channelId = "my-channel";
    var joinToken = Token.GenerateClientJoinToken(applicationId, null, null, client.Id, channelId, sharedSecret);
    try
    {
        var channel = await client.Join(channelId, joinToken);
        channel.OnMessage += (remoteClient, message) =>
        {
            Console.WriteLine($"Received a message from {remoteClient.Id}: {message}");
        };
        Console.WriteLine("Success!");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Failure: {ex}");
    }
    

    Leave (Unsubscribe)

    Leaving in LiveSwitch is equivalent to unsubscribing in WebSync. Before (WebSync)

    var channelId = "/my/channel";
    client.Unsubscribe(new UnsubscribeArgs(channelId)
    {
        OnSuccess = (unsubscribeSuccessArgs) =>
        {
            Console.WriteLine("Success!");
        },
        OnFailure = (unsubscribeFailureArgs) =>
        {
            Console.WriteLine($"Failure: {unsubscribeFailureArgs.Exception}");
        }
    });
    

    After (LiveSwitch)

    var channelId = "my-channel";
    try
    {
        await client.Leave(channelId);
        Console.WriteLine("Success!");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Failure: {ex}");
    }
    

    Send Messages (Publish)

    Sending messages in LiveSwitch is equivalent to publishing in WebSync.

    LiveSwitch uses Channel objects for contextual sending and receiving of messages, as opposed to global Client methods in WebSync. Before (WebSync)

    var channelId = "/my/channel";
    client.Publish(new PublishArgs(channelId, Serializer.SerializeString("message"))
    {
        OnSuccess = (publishSuccessArgs) =>
        {
            Console.WriteLine("Success!");
        },
        OnFailure = (publishFailureArgs) =>
        {
            Console.WriteLine($"Failure: {publishFailureArgs.Exception}");
        }
    });
    

    After (LiveSwitch)

    try
    {
        await channel.SendMessage("message");
        Console.WriteLine("Success!");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Failure: {ex}");
    }
    

    Channel-level methods to send a message to a specific user, device, or client are also available in LiveSwitch.

    Send Private Messages

    Sending private messages within a public channel is a LiveSwitch feature has no direct equivalent in WebSync.

    For each SendMessage method variant, there is an equivalent channel-level OnMessage event variant. Sending Private Messages

    try
    {
        await channel.SendUserMessage("john.doe", "message");
        Console.WriteLine("Success!");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Failure: {ex}");
    }
    

    Receive Private Messages

    channel.OnUserMessage += (remoteClient, message) =>
    {
        Console.WriteLine($"Received a private message from {remoteClient.Id}: {message}");
    };
    

    Server-Side Messaging (Publishers)

    The REST API in LiveSwitch replaces the Publisher class in WebSync.

    Because the SDK is no longer required, publishing is a simple matter of sending an HTTP request authorized with a valid API key. Before (WebSync)

    var domainKey = "11111111-1111-1111-1111-111111111111";
    var channelId = "/my/channel";
    var publisher = new Publisher(domainKey);
    publisher.Publish(channelId, Serializer.SerializeString("hello"));
    

    After (LiveSwitch)

    curl -X POST "https://api.liveswitch.io/Applications/12345678-1234-1234-1234-1234567890ab/SendChannelMessage" \
         -H "X-API-Key: aabbccddeeff00112233445566778899 \
         -H "Content-Type: application/json" \
         -d "{\"channelId\":\"my-channel\",\"message\":{\"message\":\"hello\"}}"
    

    The REST API allows other properties to be spoofed with the message, including the user ID, device ID, user alias, device alias, client tag, and client roles.

    Presence

    Presence in LiveSwitch is part of the core, instead of an extension like WebSync. Before (WebSync)

    client.Subscribe(new SubscribeArgs(...)
    {
        OnSuccess = (subscribeSuccessArgs) =>
        {
            foreach (var subscribedClient in subscribeSuccessArgs.GetSubscribedClients()[channelId])
            {
                if (subscribedClient.ClientId != client.ClientId)
                {
                    Console.WriteLine($"{subscribedClient.ClientId} is already here.");
                }
            }
        }
    }
    .SetOnClientSubscribe((clientSubscribeArgs) =>
    {
        Console.WriteLine($"{clientSubscribeArgs.SubscribedClient.ClientId} has joined.");
    })
    .SetOnClientUnsubscribe((clientUnsubscribeArgs) =>
    {
        Console.WriteLine($"{clientUnsubscribeArgs.SubscribedClient.ClientId} has left.");
    }));
    

    After (LiveSwitch)

    var channel = await client.Join(...);
    foreach (var remoteClient in channel.RemoteClientInfos)
    {
        Console.WriteLine($"{remoteClient.Id} is already here.");
    }
    channel.OnRemoteClientJoin += (remoteClient) =>
    {
        Console.WriteLine($"{remoteClient.Id} has joined.");
    };
    channel.OnRemoteClientLeave += (remoteClient) =>
    {
        Console.WriteLine($"{remoteClient.Id} has left.");
    };
    

    Port a Chat App

    Let's port a simple .NET chat app from WebSync to LiveSwitch.

    Note that with WebSync, you need to separate calls to Connect, Bind, and Subscribe, but with LiveSwitch, a single call to Register can do it all.

    Before (WebSync)

    using FM;
    using FM.WebSync;
    using FM.WebSync.Subscribers;
    using System;
    using System.Threading.Tasks;
    
    namespace Chat
    {
        class Program
        {
            static void Main(string[] args)
            {
                new Program().Run().GetAwaiter().GetResult();
            }
    
            private async Task Run()
            {
                var exit = new TaskCompletionSource<bool>();
    
                var domainName = "localhost";
                var domainKey = "11111111-1111-1111-1111-111111111111";
                var channelId = "/my/chat";
                var nameKey = "name";
    
                Console.WriteLine("Let's chat!");
                Console.WriteLine();
                Console.Write("Please enter your name: ");
                var name = Console.ReadLine();
                Console.WriteLine();
    
                var client = new Client(domainKey, domainName);
    
                Console.WriteLine("Connecting...");
    
                client.Connect(new ConnectArgs
                {
                    OnSuccess = (connectSuccessArgs) =>
                    {
                        Console.WriteLine("Connected!");
                        Console.WriteLine("Binding...");
    
                        client.Bind(new BindArgs(nameKey, Serializer.SerializeString(name))
                        {
                            OnSuccess = (bindSuccessArgs) =>
                            {
                                Console.WriteLine("Bound!");
                                Console.WriteLine("Subscribing...");
    
                                client.Subscribe(new SubscribeArgs(channelId)
                                {
                                    OnSuccess = (subscribeSuccessArgs) =>
                                    {
                                        Console.WriteLine("Subscribed!");
                                        Console.WriteLine();
    
                                        _ = Task.Run(() =>
                                        {
                                            Console.WriteLine("Type a message and press Enter to send. Type exit to quit.");
                                            Console.WriteLine();
    
                                            foreach (var subscribedClient in subscribeSuccessArgs.GetSubscribedClients()[channelId])
                                            {
                                                if (subscribedClient.ClientId != client.ClientId)
                                                {
                                                    var remoteName = Serializer.DeserializeString(subscribedClient.GetBoundRecordValueJson(nameKey));
                                                    Console.WriteLine($"{remoteName} is already here.");
                                                }
                                            }
    
                                            while (!exit.Task.IsCompleted)
                                            {
                                                var message = Console.ReadLine();
                                                if (message == "exit")
                                                {
                                                    exit.TrySetResult(true);
                                                }
                                                client.Publish(new PublishArgs(channelId, Serializer.SerializeString(message))
                                                {
                                                    OnFailure = (publishFailureArgs) =>
                                                    {
                                                        Console.WriteLine($"Publish failed. {publishFailureArgs.Exception}");
                                                    }
                                                });
                                            }
                                        });
                                    },
                                    OnFailure = (subscribeFailureArgs) =>
                                    {
                                        Console.WriteLine($"Subscribe failed. {subscribeFailureArgs.Exception}.");
                                    },
                                    OnReceive = (subscribeReceiveArgs) =>
                                    {
                                        if (subscribeReceiveArgs.PublishingClient.ClientId != client.ClientId)
                                        {
                                            var remoteName = Serializer.DeserializeString(subscribeReceiveArgs.PublishingClient.GetBoundRecordValueJson(nameKey));
                                            Console.WriteLine($"{remoteName}: {Serializer.DeserializeString(subscribeReceiveArgs.DataJson)}");
                                        }
                                    }
                                }
                                .SetOnClientSubscribe((clientSubscribeArgs) =>
                                {
                                    var remoteName = Serializer.DeserializeString(clientSubscribeArgs.SubscribedClient.GetBoundRecordValueJson(nameKey));
                                    Console.WriteLine($"{remoteName} has joined.");
                                })
                                .SetOnClientUnsubscribe((clientUnsubscribeArgs) =>
                                {
                                    var remoteName = Serializer.DeserializeString(clientUnsubscribeArgs.UnsubscribedClient.GetBoundRecordValueJson(nameKey));
                                    Console.WriteLine($"{remoteName} has left.");
                                }));
                            },
                            OnFailure = (bindFailureArgs) =>
                            {
                                Console.WriteLine($"Bind failed. {bindFailureArgs.Exception}");
                                bindFailureArgs.Retry = false;
                            }
                        });
                    },
                    OnFailure = (connectFailureArgs) =>
                    {
                        Console.WriteLine($"Connect failed. {connectFailureArgs.Exception}");
                        connectFailureArgs.Retry = false;
                    }
                });
    
                await exit.Task;
    
                Console.WriteLine("Disconnecting...");
    
                client.Disconnect(new DisconnectArgs
                {
                    OnComplete = (disconnectCompleteArgs) =>
                    {
                        Console.WriteLine("Disconnected!");
                    }
                });
            }
        }
    }
    
    

    After (LiveSwitch)

    using FM.LiveSwitch;
    using System;
    using System.Threading.Tasks;
    
    namespace Chat
    {
        class Program
        {
            static void Main(string[] args)
            {
                new Program().Run().GetAwaiter().GetResult();
            }
    
            private async Task Run()
            {
                var exit = new TaskCompletionSource<bool>();
                
                var applicationId = "12345678-1234-1234-1234-1234567890ab";
                var sharedSecret = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
                var channelId = "my-chat";
    
                Console.WriteLine("Let's chat!");
                Console.WriteLine();
                Console.Write("Please enter your name: ");
                var name = Console.ReadLine();
                Console.WriteLine();
    
                var client = new Client("https://cloud.liveswitch.io", applicationId)
                {
                    UserAlias = name
                };
    
                Console.WriteLine("Registering...");
                try
                {
                    var channels = await client.Register(Token.GenerateClientRegisterToken(client, new[] { channelId }, sharedSecret)).AsTaskAsync();
    
                    Console.WriteLine("Registered!");
                    Console.WriteLine();
    
                    var channel = channels[0];
    
                    Console.WriteLine("Type a message and press Enter to send. Type exit to quit.");
                    Console.WriteLine();
    
                    foreach (var remoteClient in channel.RemoteClientInfos)
                    {
                        Console.WriteLine($"{remoteClient.UserAlias} is already here.");
                    }
                    channel.OnRemoteClientJoin += (remoteClient) =>
                    {
                        Console.WriteLine($"{remoteClient.UserAlias} has joined.");
                    };
                    channel.OnRemoteClientLeave += (remoteClient) =>
                    {
                        Console.WriteLine($"{remoteClient.UserAlias} has left.");
                    };
                    channel.OnMessage += (remoteClient, message) =>
                    {
                        if (remoteClient.Id != client.Id)
                        {
                            Console.WriteLine($"{remoteClient.UserAlias}: {message}");
                        }
                    };
    
                    _ = Task.Run(async () =>
                    {
                        while (!exit.Task.IsCompleted)
                        {
                            var message = Console.ReadLine();
                            if (message == "exit")
                            {
                                exit.TrySetResult(true);
                            }
                            try
                            {
                                await channel.SendMessage(message).AsTaskAsync();
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine($"Send failed. {ex}");
                            }
                        }
                    });
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Register failed. {ex}");
                }
    
                await exit.Task;
    
                Console.WriteLine("Unregistering...");
                try
                {
                    await client.Unregister().AsTaskAsync();
    
                    Console.WriteLine("Unregistered!");
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Unregister failed. {ex}");
                }
            }
        }
    }
    
    In This Article
    Back to top Copyright © LiveSwitch Inc. All Rights Reserved.Documentation for LiveSwitch Version 1.25.0