### Javascript
```
const { unsubscribe } = room.presence.subscribe((event) => {
console.log(`Presence event ${event.type} from ${event.member.clientId} with data ${event.member.data}`);
});
```
### React
```
import React from 'react';
import { usePresenceListener } from '@ably/chat/react';
const MyComponent = () => {
const { presenceData, error } = usePresenceListener({
listener: (event) => {
console.log('Presence event: ', event);
},
});
return (
Presence data:
{error === undefined ? (
{presenceData.map((presence) => (
- {presence.clientId}
))}
) : (
Error loading presence data
)}
);
};
```
### Swift
```
let presenceSubscription = room.presence.subscribe()
for await event in presenceSubscription {
print("Presence event `\(event.type)` from `\(event.member.clientId)` with data `\(event.member.data)`")
}
```
### Kotlin
```
import com.ably.chat.PresenceEventType
import com.ably.chat.PresenceEvent
val subscription = room.presence.subscribe { event: PresenceEvent ->
when (event.type) {
PresenceEventType.Enter -> println("${event.member.clientId} entered")
PresenceEventType.Leave -> println("${event.member.clientId} left")
PresenceEventType.Update -> println("${event.member.clientId} updated: ${event.member.data}")
PresenceEventType.Present -> println("${event.member.clientId} already present")
}
}
```
### Android
```
import androidx.compose.material.*
import androidx.compose.runtime.*
import com.ably.chat.Room
import com.ably.chat.extensions.compose.collectAsPresenceMembers
@Composable
fun PresenceComponent(room: Room) {
val presenceMembers by room.collectAsPresenceMembers()
Text("Present members: ${presenceMembers.size}")
presenceMembers.forEach { member ->
Text("${member.clientId} is present with data: ${member.data}")
}
}
```
### Presence event structure
The following are the properties of a presence event:
| Property | Description | Type |
| -------- | ----------- | ---- |
| `type` | The type of the event | `String` |
| `member` | The presence member that the event pertains to | `PresenceMember` |
| `member.clientId` | The ID of the client that triggered the event. | `String` |
| `member.connectionId` | The connection ID of the client that triggered the event. Use this in combination with `clientId` to identify a user across multiple devices. | `String` |
| `member.data` | Optional user data. | `Object` |
| `member.updatedAt` | The time that the event was emitted. | `DateTime` |
| `member.extras` | A JSON object of arbitrary key-value pairs that may contain metadata, and/or ancillary payloads related to the user's latest presence event. | `JSONObject`(optional) |
| `member.userClaim` | A server-signed [user claim](https://ably.com/docs/chat/setup.md#user-claims) attached to this presence event, derived from the user's [JWT](https://ably.com/docs/chat/setup.md#set-user-claims). | `String` (optional) |
## Unsubscribe from presence
### Javascript
```
// Initial subscription
const { unsubscribe } = room.presence.subscribe((event) => {
console.log(`Presence event ${event.type} from ${event.member.clientId} with data ${event.member.data}`);
});
// To remove the listener
unsubscribe();
```
### Kotlin
```
// Initial subscription
val (unsubscribe) = room.presence.subscribe { event ->
println("Presence event ${event.type} from ${event.member.clientId} with data ${event.member.data}")
}
// To remove the listener
unsubscribe()
```
### Javascript
```
await room.presence.enter({ status: 'available' });
```
### React
```
import React, { useMemo } from 'react';
import { usePresence } from '@ably/chat/react';
const MyComponent = () => {
// This data is only relevant when auto-entry is enabled
// Once it has been used once, subsequent changes will have no effect
const presenceParams = {
initialData: { status: 'Online' },
};
// By default, presence will be entered when the hook mounts and left
// when it subsequently unmounts.
const { myPresenceState } = usePresence(presenceParams);
return (
Presence status: {myPresenceState.present ? 'Online' : 'Offline'}
);
};
```
### Swift
```
try await room.presence.enter(withData: ["status": "Online"])
```
### Kotlin
```
// import com.ably.chat.json.*
room.presence.enter(
jsonObject {
put("status", "Online")
},
)
```
### Android
```
import androidx.compose.runtime.*
import com.ably.chat.Room
import com.ably.chat.RoomStatus
import com.ably.chat.json.*
@Composable
fun EnterPresenceComponent(room: Room) {
LaunchedEffect(room) {
// Ensure room is attached before entering presence
if (room.status != RoomStatus.Attached) {
room.attach()
}
room.presence.enter(
jsonObject {
put("status", "Online")
}
)
}
// Leave presence when component is disposed
DisposableEffect(room) {
onDispose {
// Note: Consider using a coroutine scope for leave()
// as DisposableEffect doesn't support suspend functions directly
}
}
}
```
### Javascript
```
await room.presence.update({ status: 'busy' });
```
### React
```
import React, { useMemo } from 'react';
import { usePresence } from '@ably/chat/react';
const MyComponent = () => {
const presenceParams = {
initialData: { status: 'Online' },
};
const { update, myPresenceState } = usePresence(presenceParams);
const updatePresence = () => {
update({ status: 'Away' });
};
return (
Presence status: {myPresenceState.present ? 'Online' : 'Offline'}
);
};
```
### Swift
```
try await room.presence.update(withData: ["status": "Busy"])
```
### Kotlin
```
// import com.ably.chat.json.*
room.presence.update(
jsonObject {
put("status", "Busy")
},
)
```
### Android
```
import androidx.compose.material.*
import androidx.compose.runtime.*
import com.ably.chat.Room
import com.ably.chat.json.*
import kotlinx.coroutines.launch
@Composable
fun UpdatePresenceComponent(room: Room) {
val coroutineScope = rememberCoroutineScope()
Button(onClick = {
coroutineScope.launch {
room.presence.update(
jsonObject {
put("status", "Busy")
}
)
}
}) {
Text("Set Status to Busy")
}
}
```
### Javascript
```
await room.presence.leave({ status: 'Be back later!' });
```
### React
```
import React, { useMemo } from 'react';
import { usePresence } from '@ably/chat/react';
const MyComponent = () => {
const presenceParams = {
initialData: { status: 'Online' },
};
// Call leave explicitly to disable auto-entry and leave presence
const { myPresenceState, leave } = usePresence(presenceParams);
return (
Presence status: {myPresenceState.present ? 'Online' : 'Offline'}
);
};
```
### Swift
```
try await room.presence.leave(withData: ["status": "Be back later!"])
```
### Kotlin
```
// import com.ably.chat.json.*
room.presence.leave(
jsonObject {
put("status", "Be back later!")
},
)
```
### Android
```
import androidx.compose.material.*
import androidx.compose.runtime.*
import com.ably.chat.Room
import com.ably.chat.json.*
import kotlinx.coroutines.launch
@Composable
fun LeavePresenceComponent(room: Room) {
val coroutineScope = rememberCoroutineScope()
Button(onClick = {
coroutineScope.launch {
room.presence.leave(
jsonObject {
put("status", "Be back later!")
}
)
}
}) {
Text("Leave Presence")
}
}
```
When a user goes offline or closes their [connection](https://ably.com/docs/chat/connect.md), a leave event is also emitted and they are removed from the presence set.
#### React
```
import React, { useMemo, useCallback } from 'react';
import { usePresence } from '@ably/chat/react';
const MyComponent = () => {
// Mount the hook without auto-entry
const presenceParams = {
autoEnterLeave: false,
};
const { myPresenceState, leave, enter, update } = usePresence(presenceParams);
// Manual mount behavior - enter presence when component mounts
useEffect(() => {
enter({ status: 'active' });
// Manual unmount behavior - leave presence when component unmounts
return () => {
leave({ status: 'disconnecting' });
};
}, [enter, leave]);
// Update presence data when some event happens
const onSomeEvent = useCallback(() => {
const now = Date.now();
update({status: 'active', updated: now});
}, [update]);
return (
Presence status: {myPresenceState.present ? 'Online' : 'Offline'}
);
};
```
### Multiple Presence Data Contributors
Using multiple instances of `usePresence` with `autoEnterLeave` set to `true` is not recommended as the hooks maintain internal state to manage this behavior that is not shared between hooks. Furthermore, all instances of the hook use the same underlying presence instances, so calls to `enter`, `update` and `leave` can interact in unintended ways and lead to race conditions. Furthermore, the calls are PUT-like operations that replace your entire presence data payload. If you have multiple areas of your application that contribute data to presence, you can achieve this by using a central place to store your presence data.
To implement this pattern, you would define a Provider or Context further up your application tree, or using a global state store such as Redux. Individual areas of your codebase then update this value (or contribute to parts of it). A single call to `usePresence` can then be used to keep this state synchronized with presence whenever the value changes. This allows multiple distinct components to contribute to the presence data, without multiple calls to `usePresence` and importantly, without requiring them to know anything about the complete presence data.
### Javascript
```
// Retrieve all users entered into presence as an array:
const presentMembers = await room.presence.get();
// Retrieve the status of specific users by their clientId:
const presentMember = await room.presence.get({ clientId: 'clemons123' });
```
### Swift
```
// Retrieve all users entered into presence as an array:
let presentMembers = try await room.presence.get()
// Retrieve the status of specific users by their clientId:
let presentMember = try await room.presence.get(withParams: .init(clientID: "clemons123"))
```
### Kotlin
```
// Retrieve all users entered into presence as an array:
val presentMembers = room.presence.get()
// Retrieve the status of specific users by their clientId:
val presentMember = room.presence.get(clientId = "clemons123")
```
### Android
```
// Retrieve all users entered into presence as an array:
val presentMembers = room.presence.get()
// Retrieve the status of specific users by their clientId:
val presentMember = room.presence.get(clientId = "clemons123")
```
Alternatively, use the
### Javascript
```
const isPresent = await room.presence.isUserPresent('clemons123');
```
### Swift
```
let isPresent = try await room.presence.isUserPresent(withClientID: "clemons123")
```
### Kotlin
```
val isPresent = room.presence.isUserPresent("client-id")
```
### Android
```
val isPresent = room.presence.isUserPresent("client-id")
```