Presence¶
Presence tells you whether a friend has the App open. Each device connects to an MQTT broker and reports a
status; the broker passes it on to the user's friends. PresenceManager handles it, through Revue.presence on
Revue.shared, and your RevueDelegate receives the presence events.
Call every PresenceManager method on the main thread.
To start presence, switch accounts or stop it, see Start presence after sign-in on the Initialization page.
Known issue
After stopPresence(), friends can see this user go .offline and then return to .online, and stay there, while a friend connection between them is still up. Until this is fixed, call destroy() if friends must see the user as offline.
What presence is, and what it is not¶
Presence is one of three separate things:
| Answers | Read with | |
|---|---|---|
| Presence | Does the broker see the friend with the App open? | PresenceManager.getFriendStatus(friendId:) |
| Friend connection | Is there a path between your device and the friend's? | FriendManager.getFriendPaths(friendId:) (see Connection state) |
| Discoverable | Can other users find you on the network? | Revue.connectionState (see Connection state) |
A friend the broker sees as online is not necessarily reachable: a call needs a friend connection. To decide whether a call can go through, look at the friend connection, not at presence.
Once presence is connected, the SDK also exchanges connection details with friends automatically, which helps friend connections form faster.
Your own status¶
Set the status your friends see with PresenceManager.setStatus(_:). It accepts four values:
PresenceStatus.online, PresenceStatus.away, PresenceStatus.busy and PresenceStatus.offline. Do not pass
PresenceStatus.unknown; it describes a friend whose status cannot be read, not a status you can set.
- You can set it before the broker is connected. The SDK keeps it and sends it once the connection is up.
PresenceManager.getStatus()returns what you set. Until you callPresenceManager.setStatus(_:), it returnsPresenceStatus.online.
Invisible. PresenceManager.setStatus(_:) with PresenceStatus.offline makes friends see you as offline.
Friend connections and calls are not affected: friends can still reach you and call you.
import RevueSDK
/// What the user picks in the App's status menu.
enum StatusChoice {
case available, away, busy, invisible
}
/// Set your own presence status. Invisible is `.offline`: friends see you offline, but friend
/// connections and calls keep working.
@MainActor
func setOwnPresence(_ choice: StatusChoice) -> Bool {
let status: PresenceStatus
switch choice {
case .available: status = .online
case .away: status = .away
case .busy: status = .busy
case .invisible: status = .offline
}
// Works before the broker is connected too: the SDK sends it once connected.
return Revue.shared.presence.setStatus(status)
}
/// Read back what you set. Before the first `setStatus(_:)`, this is `.online`.
@MainActor
func currentOwnPresence() -> PresenceStatus {
Revue.shared.presence.getStatus()
}
Presence status or user status¶
The SDK has a second status, UserStatus, set with Revue.setSelfStatus(_:) (see
Friends). They travel differently:
| Sent through | A friend receives it | |
|---|---|---|
PresenceStatus |
The broker | Without a friend connection between you |
UserStatus |
The friend connection | Only once the friend connection is up |
For away and busy in a friend list, use PresenceStatus: friends see it even before a friend connection forms.
Friends' status¶
Two methods read a friend's presence status, and they return the same value:
PresenceManager.getFriendStatus(friendId:) and FriendManager.getFriendPresenceStatus(friendId:). They differ
only before the SDK is initialized: the first returns PresenceStatus.unknown, the second
PresenceStatus.offline.
When a friend's status changes, your RevueDelegate receives didUpdatePresenceStatus with the friend ID and
the new PresenceStatus.
PresenceStatus.unknownmeans the status cannot be read right now: the broker is not connected, presence is not set up, or the friend has never reported a status.- A status you read is only reliable while
PresenceManager.getConnectionState()isPresenceConnectionState.connected. PresenceStatus.isOnlineis true forPresenceStatus.online,PresenceStatus.awayandPresenceStatus.busy.
import RevueSDK
/// What a friend list row shows for a friend's presence status.
enum PresenceLabel {
case available, away, busy, offline, unknown
}
func presenceLabel(_ status: PresenceStatus) -> PresenceLabel {
switch status {
case .online: return .available
case .away: return .away
case .busy: return .busy
case .offline: return .offline
case .unknown: return .unknown
}
}
/// Read a friend's presence status. Only trust it while the broker connection is `.connected`.
@MainActor
func loadFriendPresence(friendId: String) -> PresenceLabel {
let presence = Revue.shared.presence
guard presence.getConnectionState() == .connected else { return .unknown }
return presenceLabel(presence.getFriendStatus(friendId: friendId))
}
/// Keep the friend list current: friends' status changes, and your own broker connection.
final class FriendPresenceObserver: RevueDelegate {
var onFriendPresenceChanged: ((String, PresenceLabel) -> Void)?
var onBrokerConnectionChanged: ((Bool) -> Void)?
func revue(_ revue: Revue, friend friendId: String, didUpdatePresenceStatus status: PresenceStatus) {
onFriendPresenceChanged?(friendId, presenceLabel(status))
}
func revue(_ revue: Revue, didUpdatePresenceConnectionState state: PresenceConnectionState) {
// Anything other than .connected: every friend's status reads as .unknown until it comes back.
onBrokerConnectionChanged?(state == .connected)
}
}
Connection to the broker¶
PresenceManager.getConnectionState() returns your own connection to the broker as a
PresenceConnectionState, and your RevueDelegate receives didUpdatePresenceConnectionState whenever it
changes:
| State | Meaning |
|---|---|
PresenceConnectionState.notInitialized |
Presence is not started. |
PresenceConnectionState.connecting |
Connecting for the first time. |
PresenceConnectionState.connected |
Connected. Friends' statuses are reliable only in this state. |
PresenceConnectionState.reconnecting |
The connection was up and dropped; the SDK is reconnecting. |
PresenceConnectionState.disconnected |
Disconnected. |
Detecting a lost network¶
- Your network drops. The SDK closes the broker connection right away. You receive
didUpdatePresenceConnectionStatewith a state other thanPresenceConnectionState.connected, and every friend's status reads asPresenceStatus.unknown. When the network comes back, the SDK reconnects on its own. - A friend's network drops. The friend cannot tell the broker. The broker notices when it stops hearing from
the friend's device and then announces the friend as offline for them, so you receive
didUpdatePresenceStatuswithPresenceStatus.offline. This takes some time. - To know whether a call to a friend can go through, look at the friend connection (paths), not at presence.