Messaging

Messages are JSON content containers that can weight up to 100KB and will be synchronized among all channel users in real-time. If a message requires larger binary data (such as when sending files), we recommend to upload the data to another cloud storage service, such as AWS S3, and store the URL to the content in the message data.

In addition the JSON message type, the SDK also provides support for common text and image message types. These additional types are built on top of the standard JSON message layer.

In case of image messages, the SDK freely provides a cloud storage service that will process and store all images uploaded: you don't need to setup and manage a seperate cloud storage service for this common case.

All messaging methods are contained in a EkoMessageRepository class. Before calling any messaging methods, you must ensure to first instantiate a repository instance using the EkoClient instance you created on setup.

let messageRepository = EkoMessageRepository(client: client)

Sending Messages

All message sending methods are designed to be robust enough to work under any network conditions. When you send any message, that message will automatically be put into a queue in case of unstable network conditions. Once the SDK reconnects to the server, it will automatically resend all queued messages.

Additionally, sent messages are always returned in message queries, even before they have been delivered to the server. This provides the user with a fluid messaging flow: when a user sends a message, that sent message will appear in the message stream right away (instead of waiting until it has been confirmed by the server). To check or display the current status of message delivery, use the syncState property in the message model.

Text Message

To send a standard text message, call the following method along with the channelId and the message content:

messageRepository.createTextMessage(withChannelId: "channel1", text: "Hello World!")

Image Message

To send an image message, you must pass in a valid UIImage instance instead. The SDK will resize and process the image object before sending it to the server. You can also pass in an optional caption as part of the message, this caption will be accessible under the data property in the message model, under the caption key.

let image = UIImage()
...
messageRepository.createImageMessage(withChannelId: "channel1",
image: image,
caption: "Hello Image!",
fullImage: false)

Use the fullImage parameter to request to upload the image at its original resolution. Note that this can drastically reduce the speed of message sending, depending on the original image size. If the fullImage is set to false, then the uploaded image size will be up to 1500x1500 pixels.

For more information on Image sizes, please have a look at our documentation on:

File Message

To send a file message, you must pass in an NSData instance of the selected file and file name for the file. The default file name is file. The SDK will check the size of the data whether it is exceeding the limitation or not before sending it to the server. If the size of the data is more than the limit than the callback will return an Error object with the Dictionary of the error information that you can parse it and show an error message.

let data = Data()
...
messageRepository.createFileMessage(withChannelId: "channel1",
data: data,
filename: "myfile.pdf",
caption: "some caption"
tags: nil,
parentId: nil)

Viewing File Message

Server will automatically generate the filename for you under the fileName key on the message data property based on the file name that you put. It is better for you if you can handle the file size beforehand as well, currently our limitation is less than 1 GB.

Audio Message

You can send small voice clips as a message.EkoMessageRepository provides createAudioMessage: method to send voice clips to other users. It accepts voice clips as NSData instance.

messageRepository.createAudioMessage(withChannelId: "channel-id", audio: <Data>, fileName: "audio-file-name.m4a", parentId: nil, tags: [])

Custom Message

To send a custom message, you must pass in a Dictionary instance. Server will populate this as a JSON format. Later on, you must parse it by yourselves depends on the key and value that you have set before.

// Sending an integer
let data = ["anykey": 10]
// Sending an array
let data = ["anykey": [10, 14]]
// Sending a dictionary of an array of integer
let data = ["anykey": ["anykey": [10, 12]]]
...
messageRepository.createCustomMessage(withChannelId: "channel1", data: data)

Viewing Custom Message

A custom message will be sent as a JSON format. If you wish to show the message, remember to parse the value. You are responsible to parse your own custom type data.

Message Query

To query for a list of all messags in a channel:

let messagesCollection: EkoCollection<EkoMessage> = messageRepository.messages(withChannelId: channelId, reverse: false)
self.messagesNotification = messagesCollection.observe { [weak self] _, _ in
// refresh tableview with latest message datak
self?.tableview.reloadData()
}

This method will return a LiveObject of all messages in the specified channel. You can observe the LiveObject in order to update your view whenever you receive new messages.

Get single message for some message

To get specific message:

let messageObject = messageRepository.getMessage(messageId!)
messageObject?.observe({ message, error in
// Do something with the message
})

Message First Fetch

While the SDK will always return messages in chronological order, developers can ask for the messages to be returned starting from the oldest first, or the newest first.

On a typical messaging application, the messages are fetched from the latest (newest) first, and then more older messages are explored.

let messagesCollection = messageRepository.messages(withChannelId: "123", reverse: true)
...
// When necessary load more (older) messages
messageCollection.loadPrevious()

There are other situations where fetching the oldest messages is preferred, for example for archiving purposes or in community forums.

let messagesCollection = messageRepository.messages(withChannelId: "123", reverse: false)
...
// When necessary load more (newer) messages
messageCollection.loadNext()

Message Filtering

Like for channels, we also have various ways to obtain messages that only match specific criteria:

  • the includingTags and excludingTags parameters let you filter messages based on the tags set (or not set) in each message

  • the filterByParentId and parentId parameters let you filter messages by their relationship:

    • when filterByParentId is set to false, any message will match.

    • when filterByParentId is set to true and no parentId is passed, query for all messages without a parent.

    • when filterByParentId is set to true and a parentId is passed: query for all messages with the parentId as parent.

// queries for messages tagged as "games", and not tagged as "staff-only"
let messagesCollection = messageRepository.messages(withChannelId: "123",
includingTags: ["games"],
excludingTags: ["staff-only"],
reverse: false)
// queries for messages without a parent
let messagesCollection2 = messageRepository.messages(withChannelId: "123",
filterByParentId: true,
parentId: nil,
reverse: false)
// queries for children messages of a message with id "999"
let messagesCollection3 = messageRepository.messages(withChannelId: "123",
filterByParentId: true,
parentId: "999",
reverse: false)
// queries for messages without a parent and tagged as "games", and not tagged as "staff-only"
let messagesCollection4 = messageRepository.messages(withChannelId: "123",
includingTags: ["games"],
excludingTags: ["staff-only"],
filterByParentId: true,
parentId: nil,
reverse: false)

Message Comment

For commenting same as messages, we can observer the comment on the message from EkoMessageRepository and observing the message with specific channelId, and force the filterByParentId to true, and inject the parentId of the message. (See message filtering section)

For creating a comment, we can pass the parentId to the message creation.

// queries for messages tagged as "games", and not tagged as "staff-only"
let commentsCollection = messageRepository.messages(withChannelId: "123",
filterByParentId: true,
parentId: "999",
reverse: false)
// Create a comment
messageRepository.createTextMessage(withChannelId: channelId,
text: text,
parentId: parentId)

Viewing Message

Text Message

The following method can return a live object of specific EkoMessage based on messageId. Since it is a live object remember to observe its changes on top of the token.

let messageId = "12345"
let messagesObject = messageRepository.getMessage(messageId)
messageObject?.observe({ message, error in
...
})

Image Message

Images in messages are not downloaded automatically when the client receives the message. In order to save bandwidth, images should be downloaded only when they're about to be displayed. A typical implementation for when to trigger the download would be in the tableView:cellForRowAtIndexPath: method. You can download the image using downloadImage: method from same EkoMessageRepository class. You need to pass the EkoMessage instance and the method provides the appropriate image depending upon the sync state of message.

repo.messageRepo.downloadImage(for: <EkoMessage>, size: .medium) { (imageData, error) in {
// handle image data here
}

Please use the appropriate image size depending on your usecase in order to save bandwidth.

  • small is used for image thumbnails, with a maximum image size of 160 pixels per dimension. For example, this should be used for small previews in an image gallery that displays a large amount of images in a grid.

  • medium is used for standard image display, with a maximum image size of 600 pixels per dimension. It's sized to take up about half of a mobile phone's screen size.

  • large is used for full screen image display, with a maximum image size of 1500 pixels per dimension.

  • full is used to get the original image. This size is only valid if the image is uploaded with the property fullImage set to YES. If a full sized image is not available, a large sized image will be returned instead.

The completionHandler can be called multiple times as the SDK will attempt to fetch a local cached version of the image first. If a lower resolution version of the image is cached, it will return that image, while also attempting to fetch the correct size from the server. As such, please make sure the logic implemented in the completionHandler is idempotent.

File/Audio Message

EkoMessageRepository provides downloadFile: method to download the file present in message. The same method can be used to download the audio present in audio message.

Messages Reaction

Reaction Query

To query for a list of all reaction on the specific message in a channel, right now to get the list of reactions is controlled by EkoMessageRepository. We can observe with the same way as observing message, the only thing difference is sending messageId as the parameter. The operation will return an instance EkoCollection of EkoMessageReaction that contains reaction name, message id, reactor id and reactor display name whom reacts with the reaction and the type of the reaction.

let reactionsCollection: EkoCollection<EkoMessageReaction> = messageRepository.allMessageReactions(withMessageId: message.messageId)
self.reactionsNotification = reactionsCollection.observe { [weak self] _, _ in
// refresh tableview or the view
self?.tableview.reloadData()
}

This method will return a LiveObject of all reactions of specific message in the specified channel. You can observe the LiveObject in order to update your view whenever you receive new reaction on that message.

Add Reaction

For adding a new reaction, we can use EkoMessageReactor to perform its operation. Initialize the reactor with the client and message and specify the reaction name.

let messageReactor = EkoMessageReactor(client: client, message: message)
messageReactor?.addReaction(withReaction: reaction, completion: { success, error })

Remove Reaction

For removing a new reaction, we can use EkoMessageReactor to perform its operation. Initialize the reactor with the client and message and specify the reaction name.

let messageReactor = EkoMessageReactor(client: client, message: message)
messageReactor?.removeReaction(withReaction: reaction, completion: { success, error })

Editing Messages

A special EkoMessageEditor class is also provided for you to perform actions on messages you've sent or received. These actions include editing and deleting an existing message, as well as marking a message as being read by you.

To start, first instantiate a EkoMessageEditor instance with the EkoClient instance you created on setup, as well as a valid messageID.

let editor = EkoMessageEditor(client: client, messageId: "message1")

Edit and Delete

You can only perform edit and delete operations on your own messages. Once the operation is complete, the message's editedAtDate will be set to the current time. This allows you to provide UI to the user to inform the user of specific messages that has been edited, if needed. An optional completion block can be provided to notify you of operation success.

// edit current message with new text
editor.editText("New edited text") { (success, error) in
...
}
// edit custom message with new data
editor.editCustomMessage(data) { (success, error) in
}
// delete current message
editor.delete { success, error in
...
}