Swift Client SDK

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let chatManager = ChatManager(
    id: "your-chatkit-instance-id",
    tokenProvider: yourTokenProvider,
    userId: "your-user-id"
)

chatManager.connect(delegate: yourDelegate) { currentUser, error in
    guard error == nil else {
        print("Error connecting: \(error.localizedDescription)")
        return
    }

    let rooms = currentUser.rooms
    print("Connected! \(currentUser.name)'s rooms: \(rooms)")
}

Initialization

The minimum you need to initialize a client is the following:

1
2
3
4
5
let chatManager = ChatManager(
    id: "your-chatkit-instance-id",
    tokenProvider: yourTokenProvider,
    userId: "your-user-id"
)

PCTokenProvider

1
2
3
4
5
6
7
8
9
10
let tokenProvider = PCTokenProvider(
    url: "your.auth.url",
    requestInjector: { req in
        req.addHeaders(["My-Header": "some value"])
        req.addQueryItems([
            URLQueryItem(name: "my_query", value: "a_value")
        ])
        return req
    }
)

PCTokenProvider Arguments

The TokenProvider constructor takes a single options object with the following properties.

ArgumentTypeDescription
urlstring (required)The URL that the ChatManager should make a POST request to in order to fetch a valid token. This will be either the test token provider or your own custom auth endpoint.
requestInjectorfunction (optional)A function with signature (PCTokenProviderRequest) -> PCTokenProviderRequest that provides a basic token provider request object on which you can call addHeaders([String: String]) or addQueryItems([URLQueryItem]) to set one or both of the request's headers and query items. A PCTokenProviderRequest must be returned by this function.

When making requests to a token endpoint, the TokenProvider expects the response to have the following format:

1
2
3
4
{
  "access_token": "<your token here>",
  "expires_in": "<seconds until token expiry here>"
}
For more information on the auth process please refer to the authentication docs.

Connecting

Once you've initialized your ChatManager object you are ready to connect to the Chatkit servers. This will establish a connection that will ensure that the client receives relevant updates. The full range of updates that the client can receive are described in the PCChatManagerDelegate section.

1
2
3
4
5
6
7
8
9
10
11
12
13
let chatManager = ChatManager(
    id: "your-chatkit-instance-id",
    tokenProvider: yourTokenProvider,
    userId: "your-user-id"
)

chatManager.connect(delegate: yourDelegate) { currentUser, error in
    guard error == nil else {
        print("Error connecting: \(error.localizedDescription)")
        return
    }
    print("Successfully connected")
}

The currentUser object that will be accessible in the completionHandler block, provided there were no errors on connection, gives you access to the list of rooms that the connected user is a member of.

PCCurrentUser

When an initial connection is successfully made to the Chatkit servers the client will receive a PCCurrentUser object, named currentUser in this example.

1
2
3
4
5
6
7
chatManager.connect(delegate: yourDelegate) { currentUser, error in
    guard error == nil else {
        print("Error connecting: \(error.localizedDescription)")
        return
    }
    print("Successfully connected with current user: \(currentUser)")
}

The PCCurrentUser object will almost always be the most useful in terms of updating UI components on initial load.

The available properties and what they represent are listed below:

rooms ([Room]) - the rooms that the connected user is a member of

PCChatManagerDelegate

The PCChatManagerDelegate you provide when initializing the ChatManager object will receive updates when events relevant to the connected user occur in the system. Please be aware that if you're updating the UI from any of the methods below then you'll need to ensure that you perform the updates on the main thread.

This lists the functions from the delegate protocol that you can implement, along with when they get called:

  • func addedToRoom(room: PCRoom) - the current user is added to a room
  • func removedFromRoom(room: PCRoom) - the current user is removed from a room
  • func roomUpdated(room: PCRoom) - a room that the current user is a member of has its name changed
  • func roomDeleted(room: PCRoom) - a room that the current user is a member of is deleted
  • func error(error: Error) - an error is received which relates to the current user

The following functions can be implemented as part of the PCChatManagerDelegate, but it's likely that implementing them as part of a PCRoomDelegate will be more useful. This is because they're likely to be useful to an end user when displayed at the room-specific level.

  • func userStartedTyping(room: PCRoom, user: PCUser) - a user started typing in a room in which the current user is a member
  • func userStoppedTyping(room: PCRoom, user: PCUser) - a user stopped typing in a room in which the current user is a member
  • func userJoinedRoom(room: PCRoom, user: PCUser) - a user joined a room in which the current user is a member
  • func userLeftRoom(room: PCRoom, user: PCUser) - a user left a room in which the current user is a member
  • func userCameOnline(user: PCUser) - a user came online
  • func userWentOffline(user: PCUser) - a user went offline

Rooms

There are a few important things to remember about Chatkit rooms:

  • rooms are either public or private
  • the users that are members of a room can change over time
  • all chat messages belong to a room

Creating a Room

All that you need to provide when creating a room is a name. The user that creates the room will automatically be added as a member of the room.

The following code will create a public room called my room name. Note that a room name must be no longer than 60 characters.

1
2
3
4
5
6
7
currentUser.createRoom(name: "my room name") { room, error in
    guard error == nil else {
        print("Error creating room: \(error.localizedDescription)")
        return
    }
    print("Created public room called \(room.name)")
}

Note that unless explicitly specified otherwise all rooms created will be public by default.

To create a private room you need to add in an extra parameter, isPrivate, and set it to true:

1
2
3
4
5
6
7
8
9
10
currentUser.createRoom(
    name: "my room name",
    isPrivate: true
) { room, error in
    guard error == nil else {
        print("Error creating room: \(error.localizedDescription)")
        return
    }
    print("Created private room called \(room.name)")
}

If you want to create a room and add some other users to the room at the point of creation then you need to specify the IDs of users you wish to include:

1
2
3
4
5
6
7
8
9
10
currentUser.createRoom(
    name: "my room name",
    addUserIds: yourListOfUserIds
) { room, error in
    guard error == nil else {
        print("Error creating room: \(error.localizedDescription)")
        return
    }
    print("Created room called \(room.name)")
}

Subscribing to a Room

Subscribing to a room means that the client will receive new messages as they are added. Along with the room that you wish to subscribe to, you must also provide an object that conforms to the PCRoomDelegate protocol. This will ensure that you receive new information pertaining to the room in realtime.

1
currentUser.subscribeToRoom(room: myRoom, roomDelegate: aRoomDelegate)

By default when you subscribe to a room you will receive up to the 20 most recent messages that have been added to the room. This is configurable by an extra parameter, messageLimit, as shown below:

1
2
3
4
5
currentUser.subscribeToRoom(
    room: myRoom,
    roomDelegate: aRoomDelegate,
    messageLimit: 10
)

If fewer messages have been added to the room than are requsted by the messageLimit parameter then only as many messages as have been added will be returned. Note that the upper limit on the number of messages you can receive upon subscription is 100.

If you wish to not receive any of the most recent messages upon subscription then you just need to set the messageLimit parameter to 0.

When a subscription opens, if you requested to receive some of the most recent messages then they will be delivered using the same mechanism as any other new messages: the newMessage function which is part of the PCRoomDelegate will be called for each message.

Check here for specifics on the PCRoomDelegate that you need to provide when subscribing to a room.

Fetching Messages From a Room

Subscribing to a room means that you'll get updated when new messages are added, and it can also provide you with up to the 100 most recent messages added to the room, but sometimes you'll also want the ability to fetch older messages.

In the sample code below the initialId parameter has a value of oldestMessageIdReceived. This is used to represent, as the name suggests, the ID of the oldest message that a client had received up until now.

As an example, let's assume a client subscribed to a room and upon subscription had received messages with IDs from 120 through to 139. Let's also assume that all message IDs for this room are contiguous. If the user wanted to see older messages in the room then the code they'd use would be something like this:

1
2
3
4
5
6
7
8
9
10
11
currentUser.fetchMessagesFromRoom(
    myRoom,
    initialId: oldestMessageIdReceived
) { messages, err in
    guard err == nil else {
        print("Error fetching messages from \(myRoom.name): \(err!.localizedDescription)")
        return
    }

    // do something with the messages
}

Here we assume that either the oldest message ID that has been received so far is being tracked in a variable, or the value is calculated as the request to fetch older messages is being formed, which could look like:

1
let oldestMessageIdReceived = String(roomMessages.last!.id)

In the snippet above we're using roomMessages to represent the array that is being used to keep track of received messages in the relevant room.

There is also an optional limit parameter that takes an Int value to describe the maximum number of messages you'd like to be returned. The default value for the limit is 20, with the maximum being 100 (for a single request).

The initialId parameter is also optional and if you don't provide a value then you will get up to the n most recent messages, where n corresponds to the limit value.

There is one final parameter you can provide: direction. This defaults to PCRoomMessageFetchDirection.older, which means that the messages will be fetched starting from the newest first and working backwards chronologically. If you want to fetch messages starting with the oldest first and working forwards chronologically then you need to set the direction parameter to be PCRoomMessageFetchDirection.newer.

As another example, the following code would fetch (up to) the next 10 messages with IDs greater than the initialID provided, "72".

1
2
3
4
5
6
7
8
9
10
11
12
13
currentUser.fetchMessagesFromRoom(
    myRoom,
    initialId: "72",
    limit: 10,
    direction: .newer
) { messages, err in
    guard err == nil else {
        print("Error fetching messages from \(myRoom.name): \(err!.localizedDescription)")
        return
    }

    // do something with the messages
}

Adding Users to a Room

If a user is a member of a room and they want to add another user to the room then you'd use the following code:

1
2
3
4
5
6
7
currentUser.addUser(anotherUser, to: myRoom) { error in
    guard error == nil else {
        print("Error adding user to \(myRoom.name): \(error.localizedDescription)")
        return
    }
    print("Added user \(anotherUser.id) to \(myRoom.name)")
}

You can also just provide the ID of the user to be added:

1
2
3
4
5
6
7
currentUser.addUser(id: anotherUserID, to: myRoomID) { error in
    guard error == nil else {
        print("Error adding user to \(myRoom.name): \(error.localizedDescription)")
        return
    }
    print("Added user \(anotherUserID) from \(myRoom.name)")
}

If you want to add multiple users at a time you can do so by providing an array of PCUser objects:

1
2
3
4
5
6
7
8
currentUser.addUsers(usersToAddArray, to: myRoom) { error in
    guard error == nil else {
        print("Error adding users to \(myRoom.name): \(error.localizedDescription)")
        return
    }
    let userIds = usersToAddArray.map { $0.id }.joined(separator: ", ")
    print("Added users \(userIds) to \(myRoom.name)")
}

Again, you can also just provide an array of user IDs:

1
2
3
4
5
6
7
8
currentUser.addUsers(usersIDsToAddArray, to: myRoomId) { error in
    guard error == nil else {
        print("Error adding users to \(myRoom.name): \(error.localizedDescription)")
        return
    }
    let userIds = usersIDsToAddArray.joined(separator: ", ")
    print("Added users \(userIds) to \(myRoom.name)")
}

Removing Users From a Room

If a user is a member of a room and they want to remove a user from the room then you'd use the following code:

1
2
3
4
5
6
7
currentUser.removeUser(anotherUser, from: myRoom) { error in
    guard error == nil else {
        print("Error removing user from \(myRoom.name): \(error.localizedDescription)")
        return
    }
    print("Removed user \(anotherUser.id) from \(myRoom.name)")
}

You can also just provide the ID of the user to be removed:

1
2
3
4
5
6
7
currentUser.removeUser(id: anotherUserID, from: myRoomID) { error in
    guard error == nil else {
        print("Error removing user from \(myRoom.name): \(error.localizedDescription)")
        return
    }
    print("Removed user \(anotherUserID) from \(myRoom.name)")
}

If you want to remove multiple users at a time you can do so by providing an array of PCUser objects:

1
2
3
4
5
6
7
8
currentUser.removeUsers(usersToRemoveArray, from: myRoom) { error in
    guard error == nil else {
        print("Error removing users from \(myRoom.name): \(error.localizedDescription)")
        return
    }
    let userIds = usersToRemoveArray.map { $0.id }.joined(separator: ", ")
    print("Removed users \(userIds) from \(myRoom.name)")
}

Again, you can also just provide an array of user IDs:

1
2
3
4
5
6
7
8
currentUser.removeUsers(userIDsToRemoveArray, from: myRoomID) { error in
    guard error == nil else {
        print("Error removing users from \(myRoom.name): \(error.localizedDescription)")
        return
    }
    let userIds = userIDsToRemoveArray.joined(separator: ", ")
    print("Removed users \(userIds) from \(myRoom.name)")
}

Joining a Room

To join a room you need to provide either the room's ID or a room object that contains the relevant ID. You could have obtained such a room object if you made a request to get the list of joinableRooms for a given user.

If you want to join a room using the room's ID then you'd use the following code:

1
2
3
4
5
6
7
currentUser.joinRoom(roomId: someRoomID) { error in
    guard error == nil else {
        print("Error joining room with ID \(someRoomID): \(error.localizedDescription)")
        return
    }
    print("Joined room with ID: \(someRoomID)")
}

If you want to join a room using an appropriate room object then you'd use the following code:

1
2
3
4
5
6
7
currentUser.joinRoom(someRoom) { error in
    guard error == nil else {
        print("Error joining room: \(error.localizedDescription)")
        return
    }
    print("Joined room \(someRoomID)")
}

Leaving a Room

To leave a room you need a reference to the room object that represents the room the user will leave. Here we have a room object called myRoom.

1
2
3
4
5
6
7
currentUser.leaveRoom(myRoom) { error in
            guard error == nil else {
                print("Error leaving room \(myRoom.name): \(error.localizedDescription)")
                return
            }
            print("Left room \(myRoom.name)")
        }

If you've kept a reference to the numerical ID for the room you wish to have the user leave then you can also use the following code:

1
2
3
4
5
6
7
currentUser.leaveRoom(id: myRoomId) { error in
    guard error == nil else {
        print("Error leaving room with ID \(myRoomId): \(error.localizedDescription)")
        return
    }
    print("Left room with ID \(myRoomId)")
}

Deleting a Room

Just like leaving a room you need a reference to the room object that represents the room that the user wishes to delete. Here we again have a room object called myRoom.

1
2
3
4
5
6
7
currentUser.delete(room: myRoom) { error in
    guard error == nil else {
        print("Error deleting room \(room.name): \(error.localizedDescription)")
        return
    }
    print("Deleted room \(room.name)")
}

All other users that are memebers of myRoom (and connected to the Chatkit servers) will receive an event that informs them that the room has been deleted. No further updates to the room can be made and, upon subsequent connections by any user who was a member of the room at the point of its deletion, the room will no longer appear in the rooms property of the PCCurrentUser object.

Note that when a user deletes a room all of the associated messages will be deleted as well.

Getting Joinable Rooms

To fetch a list of the rooms that a user is able to join you can use the following code:

1
2
3
4
5
6
7
8
currentUser.getJoinableRooms { joinableRooms, err in
    guard err == nil else {
        print("Error getting joinable rooms: \(err!)")
        return
    }

    // do something with the joinableRooms
}

The rooms returned will be a list of the public rooms of which the currentUser is not a member of.

PCRoomDelegate

The PCRoomDelegate object you set for a room will receive events when there are relevant updates. Please be aware that if you're updating the UI from any of the methods below then you'll need to ensure that you perform the updates on the main thread.

These are the delegate functions that you can implement along with when they get called:

  • func newMessage(message: PCMessage) - a new message has been added to the room
  • func userStartedTyping(user: PCUser) - a user started typing in the room
  • func userStoppedTyping(user: PCUser) - a user stopped typing in the room
  • func userJoined(user: PCUser) - a user joined the room
  • func userLeft(user: PCUser) - a user left the room
  • func userCameOnline(user: PCUser) - a user came online
  • func userWentOffline(user: PCUser) - a user went offline
  • func usersUpdated() - theusers property of thePCRoom object has been updated
  • func cursorSet(cursor: PCCursor) - a new cursor has been set for a member of the room
  • func error(error: Error) - an error occurred in relation to room-specific events

Messages

Every message belongs to a room and has an associated sender, which is represented by a PCUser object.

Messages themselves are represented by PCMessage objects in the SDK. They have the following properties accessible:

  • id (Int) - the ID assigned to the message by the Chatkit servers
  • text (String) - the text content of message
  • sender (PCUser) - the user who sent the message
  • room (PCRoom) - the room to which the message belongs
  • createdAt (String) - the timestamp at which the message was created
  • updatedAt (String) - the timestamp at which the message was last updated

In the following examples a room object named myRoom is used.

Sending a Message

To send a message to a room:

1
2
3
4
5
6
7
currentUser.sendMessage(roomId: myRoomId, text: "Hi there!") { message, error in
    guard error == nil else {
        print("Error sending message to \(room.name): \(error.localizedDescription)")
        return
    }
    print("Sent message to \(myRoom.name)")
}

Sending a Message With an Attachment

Messages can also have attachments. These can be used to attach files, such as images, videos, PDFs, or any other file you'd like to attach, or you can provide a plain link as an attachment if the attachment is already stored somewhere else.

Here's an example of sending a message with an attachment:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let imageName = Bundle.main.path(forResource: "dog", ofType: "jpg")
let imageURL = URL(fileURLWithPath: imageName!)

currentUser.sendMessage(
    roomId: roomId,
    text: "My message text",
    attachmentType: .fileURL(imageURL, name: "dog.jpg")
) { messageId, err in
    guard err == nil else {
        print("Error sending message \(err!.localizedDescription)")
        return
    }
    print("Successfully sent message with ID: \(messageId!)")
}

There are currently 3 different attachmentTypes supported, as described in the PCAttachmentType enum:

  • .fileData(_: Data, name: String): Use this if you have your file as Data. The name parameter is the name that the file will be given when it is stored by our servers.
  • .fileURL(_: URL, name: String): Use this if you have your file as Data. The name parameter is the name that the file will be given when it is stored by our servers.
  • .link(_: String, type: String): Use this if you have a file stored elsewhere that you would like to attach to a message without it being uploaded to and stored by the Chatkit servers. The type parameter currently needs to be one of "image", "video", "audio", or "file"

Here's an example of using the .link(_: String, type: String) attachment type:

1
2
3
4
5
6
7
8
9
10
11
currentUser.sendMessage(
    roomId: roomId,
    text: "My message text",
    attachmentType: .link("https://i.giphy.com/RpByGPT5VlZiE.gif", type: "image")
) { messageId, err in
    guard err == nil else {
        print("Error sending message \(err!.localizedDescription)")
        return
    }
    print("Successfully sent message with ID: \(messageId!)")
}

Receiving New Messages

The PCRoomDelegate function you will need to implement to receive new messages is newMessage.

1
2
3
func newMessage(message: PCMessage) {
    print("Received message: \(message.text) from \(message.sender.debugDescription)")
}

Receiving New Messages With Attachments

PCMessages have an optional attachment property of type PCAttachment?. The PCAttachment type looks like this:

1
2
3
4
5
public struct PCAttachment {
    public let fetchRequired: Bool
    public let link: String
    public let type: String
}

If fetchRequired is true then it means that the attachment is stored on the Chatkit servers and you need to make a request to the Chatkit API to link that will work for displaying / downloading. To do this you can use the fetchAttachment function that has been added to the PCCurrentUser class. You use that like this:

1
2
3
4
5
6
7
8
currentUser.fetchAttachment(attachmentLink) { fetchedAttachment, err in
    guard err == nil else {
        print("Error fetching attachment \(err!.localizedDescription)")
        return
    }

    print("Fetched attachment link: \(fetchedAttachment!.link)")
}

You can then use the fetchedAttachment.link to download the file, if you so wish. You can either use your own download mechanism of choice or you can use the provideddownloadAttachment function. Usage of it looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
currentUser.downloadAttachment(
    fetchedAttachment.link,
    to: myChosenDestination,
    onSuccess: { url in
        print("Downloaded successfully to \(url.absoluteString)")
    },
    onError: { error in
        print("Failed to download attachment \(error.localizedDescription)")
    },
    progressHandler: { bytesReceived, totalBytesToReceive in
        print("Download progress: \(bytesReceived) / \(totalBytesToReceive)")
    }
)

Here myChosenDestination is an object of type PCDownloadFileDestination. This is a type based on Alamofire's DownloadFileDestination. It lets you specify where you'd like to have the download stored (upon completion).

One option for creating a PCDownloadFileDestination is to use the PCSuggestedDownloadDestination function, which is again based on an Alamofire construct: DownloadRequest.suggestedDownloadDestination. You can provide it a PPDownloadOptions object which determines whether or not the process of moving the downloaded file to the specified destination should be allowed to remove any existing files at the same path and if it should be able to create any required intermediate directories. This is expressed as an OptionSet with the following options:

  • .createIntermediateDirectories
  • .removePreviousFile

Here's an example of using PCSuggestedDownloadDestination:

1
2
3
4
5
6
7
8
9
10
11
12
13
currentUser.downloadAttachment(
    fetchedAttachment.link,
    to: PCSuggestedDownloadDestination(options: [.createIntermediateDirectories, .removePreviousFile]),
    onSuccess: { url in
        print("Downloaded successfully to \(url.absoluteString)")
    },
    onError: { error in
        print("Failed to download attachment \(error.localizedDescription)")
    },
    progressHandler: { bytesReceived, totalBytesToReceive in
        print("Download progress: \(bytesReceived) / \(totalBytesToReceive)")
    }
)

Typing Indicators

Sometimes it's useful to be able to see if another user is typing. If this is the case for your app then you can send typing events to the Chatkit servers that will be delivered to other users in the relevant room.

Triggering a Start / Stop Typing Event

If we assume that you have a UITextView where your user will be typing then you would do the following to send an event to indicate that the current user had started typing:

1
2
3
override func textViewDidChange(textView: UITextView) {
    currentUser.typing(in: myRoom)
}

By default, if the typing() function isn't called again after 3 seconds then the typing indicator will timeout and a typing_stop event will automatically be sent to the Chatkit servers.

If you want to change the length of the timeout then you can pass in a value for the timeoutAfter parameter, which is a TimeInterval:

1
currentUser.typing(in: myRoom, timeoutAfter: 1.5)

Note that there is a minimum value of 1 second (1.0) for the timeout.

If you want to manually control the typing indicator events then you can manually call startedTypingIn() and stoppedTypingIn() on the PCCurrentUser object:

1
2
3
4
5
currentUser.startedTypingIn(room: myRoom)

// some time later

currentUser.stoppedTypingIn(room: myRoom)

Receiving Typing Indicator Events

The PCRoomDelegate functions you will need to implement to receive events when a user starts or stops typing are userStartedTyping and userStoppedTyping. Here it's assumed that you'll have access to the myRoom variable.

1
2
3
4
5
6
7
func userStartedTyping(user: PCUser) {
    print("User \(user.name) started typing in room \(myRoom.name)")
}

func userStoppedTyping(user: PCUser) {
    print("User \(user.name) stopped typing in room \(myRoom.name)")
}

User Presence

After a user successfully connects to the Chatkit, the SDK establishes a separate subscription that is reserved for user presence interactions.

As soon as the subscription is established with the Chatkit service, the user is considered online. All users that are members of at least one of the same rooms as that user will receive an update notifying them that the user has come online. The same process occurs when a user goes offline.

The presence updates are only delivered to users who also have an active presence subscription, or in other words, to users who are online themselves. Upon successful connection a user receives an initial state of the presence of all of the users with whom they share a room membership. Similarly, if a user joins a room where there are members who they didn't previously share a room membership with then the client will receive an event that informs it of the presence states of the relevant users.

This combination of initial state payloads and realtime updates means that every client is able to stay completely up-to-date with the presence state of the users that they share a room membership with.

Receiving Presence Updates

These are the delegate functions that you'd implement to receive events of user presence changes when implementing them as part of a PCChatManagerDelegate or a PCRoomDelegate implementation:

1
2
3
4
5
6
7
func userCameOnline(user: PCUser) {
    print("User \(user.name) came online")
}

func userWentOffline(user: PCUser) {
    print("User \(user.name) went offline")
}

Read Cursors

Read cursors track how far a user has read through the messages in a room. Each read cursor belongs to a user and a room -- represented by a PCCursor object with the following properties:

  • type (PCCursorType) - the type of the cursor object, currently always
  • position (Int) - the message ID that the user has read to
  • room (PCRoom) - the room that the cursor refers to
  • updatedAt (String) - the timestamp when the cursor was last set
  • user (PCUser) - the user that the cursor belongs to

PCCursorType is an enum that currently only has one case:

1
2
3
public enum PCCursorType: Int {
    case read
}

Set a Read Cursor

Call setReadCursor on the PCCurrentUser object. Takes a position (message ID), room (a PCRoom object), and a completion handler.

1
2
3
4
5
6
7
8
9
10
currentUser.setReadCursor(
    position: 123,
    roomId: myRoom.id
) { error in
    guard error == nil else {
        print("Error setting cursor: \(error!.localizedDescription)")
        return
    }
    print("Succeeded in setting cursor")
}

Receiving Cursor Updates

To receive updates to read cursors for a room, define the cursorSet function on the PCRoomDelegate when subscribing to a room. cursorSet should be a function that takes a PCCursor object. e.g.

1
2
3
func cursorSet(cursor: PCCursor) {
    print("Cursor set for \(cursor.user.displayName) at position \(cursor.position)")
}

Accessing a User's Own Read Cursors

A user's own read cursors are accessible by using the readCursor function on PCCurrentUser. The read cursors stored by the SDK will be kept up to date as new cursor values are set.

1
let myCursor = try? currentUser.readCursor(roomId: myRoom.id)

Accessing Other Users' Read Cursors

You can access the cursors of the other members of a room again by using the readCursor function on PCCurrentUser. You must provide the ID of the user whose cursor you would like to access.

1
2
3
4
let anotherCursor = try? currentUser.readCursor(
    roomId: myRoom.id,
    userId: anotherUser.id
)

Did you find this document useful?

We are always striving to create the most accurate and informative docs as possible. If there is something especially wrong (or right) here then please let us know.