<?php
$channels = [
    'account' => 'All account related events (session create, name update...)',
    'databases.[ID].collections.[ID].documents' => 'Any create/update/delete events to any document in a collection',
    'documents' => 'Any create/update/delete events to any document',
    'databases.[ID].collections.[ID].documents.[ID]' => 'Any update/delete events to a given document',
    'files' => 'Any create/update/delete events to any file',
    'buckets.[ID].files.[ID]' => 'Any update/delete events to a given file of the given bucket',
    'buckets.[ID].files' => 'Any update/delete events to any file of the given bucket',
    'teams' => 'Any create/update/delete events to a any team',
    'teams.[ID]' => 'Any update/delete events to a given team',
    'memberships' => 'Any create/update/delete events to a any membership',
    'memberships.[ID]' => 'Any update/delete events to a given membership',
    'executions' => 'Any update to executions',
    'executions.[ID]' => 'Any update to a given execution',
    'functions.[ID]' => 'Any execution event to a given function'
];
?>
<p>Appwrite supports multiple protocols for accessing the server, including <a href="/docs/rest">REST</a>, <a href="/docs/graphql">GraphQL</a>, and <a href="/docs/realtime">Realtime</a>.</p>

<p>Realtime allows you to listen to any events on the server-side in realtime using the <b>subscribe</b> method.</p>

<p>Instead of requesting new data via HTTP, the subscription will receive new data every time it changes, any connected client receives that update within milliseconds via a WebSocket connection.</p>

<p>This lets you build an interactive and responsive user experience by providing information from all of Appwrite's services in realtime. The example below shows subscribing to realtime events for file uploads.</p>

<ul class="phases clear" data-ui-phases>
    <li>
        <h3>Web</h3>
        <div class="ide margin-bottom" data-lang="javascript" data-lang-label="Web SDK">
            <pre class="line-numbers"><code class="prism language-javascript" data-prism>import { Client } from "appwrite";

const client = new Client()
    .setEndpoint('https://cloud.appwrite.io/v1')
    .setProject('[PROJECT_ID]');

// Subscribe to files channel
client.subscribe('files', response => {
    if(response.events.includes('buckets.*.files.*.create')) {
        // Log when a new file is uploaded
        console.log(response.payload);
    }
});</code></pre>
        </div>
    </li>
    <li>
        <h3>Flutter</h3>

        <div class="ide margin-bottom" data-lang="dart" data-lang-label="Flutter SDK">
            <pre class="line-numbers"><code class="prism language-dart" data-prism>import 'package:appwrite/appwrite.dart';

final client = Client()
    .setEndpoint('https://cloud.appwrite.io/v1')
    .setProject('[PROJECT_ID]');

final realtime = Realtime(client);

// Subscribe to files channel
final subscription = realtime.subscribe(['files']);

subscription.stream.listen((response) {
    if(response.events.contains('buckets.*.files.*.create')) {
    // Log when a new file is uploaded
    print(response.payload);
    }
});</code></pre>
        </div>
    </li>
    <li>
        <h3>Android</h3>
        <div class="ide margin-bottom" data-lang="android" data-lang-label="Android SDK">
            <pre class="line-numbers"><code class="prism language-kotlin" data-prism>import io.appwrite.Client
import io.appwrite.services.Realtime

val client = Client(context)
    .setEndpoint("https://cloud.appwrite.io/v1")
    .setProject("[PROJECT_ID]")

val realtime = Realtime(client)

// Subscribe to files channel
let subscription = realtime.subscribe("files") {
    if(it.events.contains("buckets.*.files.*.create")) {
        // Log when a new file is uploaded
        print(it.payload.toString());
    }
}</code></pre>
        </div>
    </li>
    <li>
        <h3>Apple</h3>
        <div class="ide margin-bottom" data-lang="apple" data-lang-label="Apple SDK">
            <pre class="line-numbers"><code class="prism language-swift" data-prism>import Appwrite
import AppwriteModels

let client = Client()
    .setEndpoint("https://cloud.appwrite.io/v1")
    .setProject("[PROJECT_ID]")

let realtime = Realtime(client)

// Subscribe to files channel
let subscription = realtime.subscribe(channels: ["files"]) { response in
    if (message.events!.contains("buckets.*.files.*.create")) {
        // Log when a new file is uploaded
        print(String(describing: response))
    }
}</code></pre>
        </div>
    </li>
</ul>

<p>To subscribe to updates from different resources, you need to specify one or more channels. The <a href="/docs/realtime#channels">channels</a> offer a wide and powerful selection that will allow you to listen to all possible resources. This allows you to receive updates not only from the database, but from all the services that Appwrite offers.</p>

<p>If you subscribe to a channel, you will receive callbacks for a variety of events related to the channel. The <b>events</b> attribute in the callback can be used to filter and respond to specific events in a channel.</p>

<p>All possible events can be found <a href="/docs/events">here</a>.</p>

<div class="notice margin-top-large margin-bottom-large">
    <h3>Permissions</h3>
    <p>All subscriptions are secured by the <a href="/docs/permissions">permissions system</a> offered by Appwrite, meaning a user will only receive updates to resources they have permission to access.</p>
    <p>Using <span class="tag">Role.any()</span> on read permissions will allow any client to receive updates.</p>
</div>

<h2><a href="/docs/realtime#authentication" id="authentication">Authentication</a></h2>
<p>Realtime authenticates using an existing user session. If you authenticate <b>after</b> creating a subscription, the subscription will not receive updates for the newly authenticated user. You will need to re-create the subscription to work with the new user.</p>

<p>More information and examples of authenticating users can be found in the dedicated <a href="/docs/authentication">authentication guide</a>.</p>

<h2><a href="/docs/realtime#examples" id="examples">Examples</a></h2>
<p>The examples below will show you how you can use Realtime in various ways.</p>

<h3><a href="/docs/realtime#subscribe-to-a-channel" id="subscribe-to-a-channel">Subscribe to a Channel</a></h3>

<p>In this example we are subscribing to all updates related to our account by using the <span class="tag">account</span> channel. This will be triggered by any update related to the authenticated user, like updating the user's name or e-mail address.</p>

<ul class="phases clear" data-ui-phases>
    <li>
        <h3>Web</h3>
        <div class="ide margin-bottom" data-lang="javascript" data-lang-label="Web SDK">
            <pre class="line-numbers"><code class="prism language-javascript" data-prism>import { Client } from "appwrite";

const client = new Client()
    .setEndpoint('https://cloud.appwrite.io/v1')
    .setProject('[PROJECT_ID]');

client.subscribe('account', response => {
    // Callback will be executed on all account events.
    console.log(response);
});</code></pre>
        </div>
    </li>
    <li>
        <h3>Flutter</h3>

        <div class="ide margin-bottom" data-lang="dart" data-lang-label="Flutter SDK">
            <pre class="line-numbers"><code class="prism language-dart" data-prism>import 'package:appwrite/appwrite.dart';

final client = Client()
    .setEndpoint('https://cloud.appwrite.io/v1')
    .setProject('[PROJECT_ID]');

final realtime = Realtime(client);

final subscription = realtime.subscribe(['account']);

subscription.stream.listen((response) {
    // Callback will be executed on all account events.
    print(response);
})</code></pre>
        </div>
    </li>
    <li>
        <h3>Android</h3>
        <div class="ide margin-bottom" data-lang="android" data-lang-label="Android SDK">
            <pre class="line-numbers"><code class="prism language-kotlin" data-prism>import io.appwrite.Client
import io.appwrite.services.Realtime

val client = Client(context)
    .setEndpoint("https://cloud.appwrite.io/v1")
    .setProject("[PROJECT_ID]")

val realtime = Realtime(client)

val subscription = realtime.subscribe("account") {
    // Callback will be executed on all account events.
    print(it.payload.toString())
}</code></pre>
        </div>
    </li>
    <li>
        <h3>Apple</h3>
        <div class="ide margin-bottom" data-lang="apple" data-lang-label="Apple SDK">
            <pre class="line-numbers"><code class="prism language-swift" data-prism>import Appwrite
import AppwriteModels

let client = Client()
    .setEndpoint("https://cloud.appwrite.io/v1")
    .setProject("[PROJECT_ID]")

let realtime = Realtime(client)

let subscription = realtime.subscribe(channel: "account", callback: { response in
    // Callback will be executed on all account events.
    print(String(describing: response))
})</code></pre>
        </div>
    </li>
</ul>

<h3><a href="/docs/realtime#subscribe-to-multiple-channels" id="subscribe-to-multiple-channels">Subscribe to Multiple Channels</a></h3>

<p>You can also listen to multiple <a href="/docs/realtime#channels">channels</a> at once by passing an array of channels. This will trigger the callback for any events for all channels passed.</p>
<p>In this example we are listening to the document A and all files by subscribing to the <span class="tag">databases.A.collections.A.documents.A</span> and <span class="tag">files</span> channels.</p>

<ul class="phases clear" data-ui-phases>
    <li>
        <h3>Web</h3>
        <div class="ide margin-bottom" data-lang="javascript" data-lang-label="Web SDK">
            <pre class="line-numbers"><code class="prism language-javascript" data-prism>import { Client } from "appwrite";

const client = new Client()
    .setEndpoint('https://cloud.appwrite.io/v1')
    .setProject('[PROJECT_ID]');

client.subscribe(['collections.A.documents.A', 'files'], response => {
    // Callback will be executed on changes for documents A and all files.
    console.log(response);
});</code></pre>
        </div>
    </li>
    <li>
        <h3>Flutter</h3>

        <div class="ide margin-bottom" data-lang="dart" data-lang-label="Flutter SDK">
            <pre class="line-numbers"><code class="prism language-dart" data-prism>import 'package:appwrite/appwrite.dart';

final client = Client()
    .setEndpoint('https://cloud.appwrite.io/v1')
    .setProject('[PROJECT_ID]');

final realtime = Realtime(client);

final subscription = realtime.subscribe(['databases.A.collections.A.documents.A', 'files']);

subscription.stream.listen((response) {
    // Callback will be executed on changes for documents A and all files.
    print(response);
})</code></pre>
        </div>
    </li>
    <li>
        <h3>Android</h3>
        <div class="ide margin-bottom" data-lang="android" data-lang-label="Android SDK">
            <pre class="line-numbers"><code class="prism language-kotlin" data-prism>import io.appwrite.Client
import io.appwrite.services.Realtime

val client = Client(context)
    .setEndpoint("https://cloud.appwrite.io/v1")
    .setProject("[PROJECT_ID]")
val realtime = Realtime(client)

realtime.subscribe("databases.A.collections.A.documents.A", "files") {
    // Callback will be executed on changes for documents A and all files.
    print(it.toString())
}</code></pre>
        </div>
    </li>
    <li>
        <h3>Apple</h3>
        <div class="ide margin-bottom" data-lang="apple" data-lang-label="Apple SDK">
            <pre class="line-numbers"><code class="prism language-swift" data-prism>import Appwrite
import AppwriteModels

let client = Client()
    .setEndpoint("https://cloud.appwrite.io/v1")
    .setProject("[PROJECT_ID]")

let realtime = Realtime(client)

realtime.subscribe(channels: ["databases.A.collections.A.documents.A", "files"]) { response in
    // Callback will be executed on changes for documents A and all files.
    print(String(describing: response))
}</code></pre>
        </div>
    </li>
</ul>

<h3><a href="/docs/realtime#unsubscribe" id="unsubscribe">Unsubscribe</a></h3>

<p>If you no longer want to receive updates from a subscription, you can unsubscribe so that your callbacks are no longer called. Leaving old subscriptions alive and resubscribing can result in duplicate subscriptions and cause race conditions.</p>

<ul class="phases clear" data-ui-phases>
    <li>
        <h3>Web</h3>
        <div class="ide margin-bottom" data-lang="javascript" data-lang-label="Web SDK">
            <pre class="line-numbers"><code class="prism language-javascript" data-prism>import { Client } from "appwrite";

const client = new Client()
    .setEndpoint('https://cloud.appwrite.io/v1')
    .setProject('[PROJECT_ID]');

const unsubscribe = client.subscribe('files', response => {
    // Callback will be executed on changes for all files.
    console.log(response);
});

// Closes the subscription.
unsubscribe();</code></pre>
        </div>
    </li>
    <li>
        <h3>Flutter</h3>

        <div class="ide margin-bottom" data-lang="dart" data-lang-label="Flutter SDK">
            <pre class="line-numbers"><code class="prism language-dart" data-prism>import 'package:appwrite/appwrite.dart';

final client = Client()
    .setEndpoint('https://cloud.appwrite.io/v1')
    .setProject('[PROJECT_ID]');

final realtime = Realtime(client);

final subscription = realtime.subscribe(['files']);

subscription.stream.listen((response) {
    // Callback will be executed on changes for all files.
    print(response);
})

// Closes the subscription.
subscription.close();</code></pre>
        </div>
    </li>
    <li>
        <h3>Android</h3>
        <div class="ide margin-bottom" data-lang="android" data-lang-label="Android SDK">
            <pre class="line-numbers"><code class="prism language-kotlin" data-prism>import io.appwrite.Client
import io.appwrite.services.Realtime

val client = Client(context)
    .setEndpoint("https://cloud.appwrite.io/v1")
    .setProject("[PROJECT_ID]")

val realtime = Realtime(client)

val subscription = realtime.subscribe("files") {
    // Callback will be executed on changes for all files.
    print(param.toString())
}

// Closes the subscription.
subscription.close()</code></pre>
        </div>
    </li>
    <li>
        <h3>Apple</h3>
        <div class="ide margin-bottom" data-lang="apple" data-lang-label="Apple SDK">
            <pre class="line-numbers"><code class="prism language-swift" data-prism>
let client = Client()
let realtime = Realtime(client)

let subscription = realtime.subscribe(channel: "files") { response in
    // Callback will be executed on changes for all files.
    print(response.toString())
}

// Closes the subscription.
subscription.close()</code></pre>
        </div>
    </li>
</ul>

<h2><a href="/docs/realtime#payload" id="payload">Payload</a></h2>

<p>The payload from the subscription will contain following properties:</p>

<table class="vertical full args">
    <thead>
        <tr>
            <td style="width: 140px">Name</td>
            <td style="width: 120px">Type</td>
            <td>Description</td>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td data-title="Name: ">
                events
            </td>
            <td data-title="Type: ">
                string[]
            </td>
            <td data-title="Description: ">
                The <a href="/docs/events">system events</a> that triggered this update.
            </td>
        </tr>
        <tr>
            <td data-title="Name: ">
                channels
            </td>
            <td data-title="Type: ">
                string[]
            </td>
            <td data-title="Description: ">
                An array of <a href="/docs/realtime#channels">channels</a> that can receive this message.
            </td>
        </tr>
        <tr>
            <td data-title="Name: ">
                timestamp
            </td>
            <td data-title="Type: ">
                string
            </td>
            <td data-title="Description: ">
                The <a href="https://en.wikipedia.org/wiki/ISO_8601" rel="noopener" target="_blank">ISO 8601 timestamp</a> in UTC timezone from the server to ensure consistency across all client platforms and real-time technologies.
            </td>
        </tr>
        <tr>
            <td data-title="Name: ">
                payload
            </td>
            <td data-title="Type: ">
                object
            </td>
            <td data-title="Description: ">
                Payload contains the data equal to the response model.
            </td>
        </tr>
    </tbody>
</table>

<p>If you subscribe to the <span class="tag">documents</span> channel and a document the user is allowed to read is updated, you will receive an object containing information about the event and the updated document.</p>

<p>The response will look like this:</p>

<div class="ide margin-bottom" data-lang="json" data-lang-label="JSON">
    <pre class="line-numbers"><code class="prism language-json" data-prism>{
  "events": [
    "databases.default.collections.sample.documents.63c98b9baea0938e1206.update",
    "databases.*.collections.*.documents.*.update",
    "databases.default.collections.*.documents.63c98b9baea0938e1206.update",
    "databases.*.collections.*.documents.63c98b9baea0938e1206.update",
    "databases.*.collections.sample.documents.63c98b9baea0938e1206.update",
    "databases.default.collections.sample.documents.*.update",
    "databases.*.collections.sample.documents.*.update",
    "databases.default.collections.*.documents.*.update",
    "databases.default.collections.sample.documents.63c98b9baea0938e1206",
    "databases.*.collections.*.documents.*",
    "databases.default.collections.*.documents.63c98b9baea0938e1206",
    "databases.*.collections.*.documents.63c98b9baea0938e1206",
    "databases.*.collections.sample.documents.63c98b9baea0938e1206",
    "databases.default.collections.sample.documents.*",
    "databases.*.collections.sample.documents.*",
    "databases.default.collections.*.documents.*",
    "databases.default.collections.sample",
    "databases.*.collections.*",
    "databases.default.collections.*",
    "databases.*.collections.sample",
    "databases.default",
    "databases.*"
  ],
  "channels": [
    "documents",
    "databases.default.collections.sample.documents",
    "databases.default.collections.sample.documents.63c98b9baea0938e1206"
  ],
  "timestamp": "2023-01-19 18:30:04.051",
  "payload": {
    "ip": "127.0.0.1",
    "stringArray": [
      "sss"
    ],
    "email": "joe@example.com",
    "stringRequired": "req",
    "float": 3.3,
    "boolean": false,
    "integer": 3,
    "enum": "apple",
    "stringDefault": "default",
    "datetime": "2023-01-19T10:27:09.428+00:00",
    "url": "https://appwrite.io",
    "$id": "63c98b9baea0938e1206",
    "$createdAt": "2023-01-19T18:27:39.715+00:00",
    "$updatedAt": "2023-01-19T18:30:04.040+00:00",
    "$permissions": [],
    "$collectionId": "sample",
    "$databaseId": "default"
  }
}</code></pre>
</div>

<h2><a href="/docs/realtime#channels" id="channels">Channels</a></h2>

<p>A list of all channels available you can subscribe to. IDs cannot be wildcards.</p>

<table class="full text-size-small">
    <thead>
        <tr>
            <td style="width: 200px">Channel</td>
            <td>Description</td>
        </tr>
    </thead>
    <tbody>
        <?php foreach ($channels as $channel => $description) : ?>
            <tr>
                <td><?php echo $this->escape($channel); ?></td>
                <td><?php echo $this->escape($description); ?></td>
            </tr>
        <?php endforeach; ?>
    </tbody>
</table>

<h2><a href="/docs/realtime#custom-endpoint" id="custom-endpoint">Custom Endpoint</a></h2>

<p>The SDK will guess the endpoint of the Realtime API when setting the endpoint of your Appwrite instance. If you are running Appwrite with a custom proxy and changed the route of the Realtime API, you can call the <code>setEndpointRealtime</code> method on the Client SDK and set your new endpoint value.</p>

<p>By default the endpoint is <code>wss://cloud.appwrite.io/v1/realtime</code>.</p>

<ul class="phases clear" data-ui-phases>
    <li>
        <h3>Web</h3>
        <div class="ide margin-bottom" data-lang="javascript" data-lang-label="Web SDK">
            <pre class="line-numbers"><code class="prism language-javascript" data-prism>import { Client } from "appwrite";
const client = new Client();

client.setEndpointRealtime('cloud.appwrite.io');</code></pre>
        </div>
    </li>
    <li>
        <h3>Flutter</h3>

        <div class="ide margin-bottom" data-lang="dart" data-lang-label="Flutter SDK">
            <pre class="line-numbers"><code class="prism language-dart" data-prism>final client = Client();
client.setEndpointRealtime('cloud.appwrite.io');</code></pre>
        </div>
    </li>
    <li>
        <h3>Android</h3>
        <div class="ide margin-bottom" data-lang="android" data-lang-label="Android SDK">
            <pre class="line-numbers"><code class="prism language-kotlin" data-prism>val client = Client(context)
client.setEndpointRealtime("cloud.appwrite.io")</code></pre>
        </div>
    </li>
    <li>
        <h3>Apple</h3>
        <div class="ide margin-bottom" data-lang="apple" data-lang-label="Apple SDK">
            <pre class="line-numbers"><code class="prism language-swift" data-prism>let client = Client()
client.setEndpointRealtime("cloud.appwrite.io")</code></pre>
        </div>
    </li>
</ul>

<h2><a href="/docs/realtime#known-limitations" id="known-limitations">Known Limitations</a></h2>

<h3>Rapid Subscriptions Changes</h3>
<p>The SDK creates a single WebSocket connection for all subscribed channels. Each time a channel is added or unsubscribed - the SDK currently creates a completely new connection and terminates the old one. Therefore, subscriptions to channels should always be done in conjunction with state management so as not to be unnecessarily built up several times by multiple components' life cycles.</p>

<h3>Server-Side?</h3>
<p>We currently are not offering access to realtime with Server SDKs and an API key.</p>
