Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
An easy-to-integrate module that delivers high-performance messaging services
From travel-loving retirees for cruise lines to hiking enthusiasts for camping equipment brands, there are groups and individuals everywhere seeking to find others with similar interests. Provide a place for your customers to join communities centered towards your brand and what it represents to boost customer loyalty and retention, build a positive association, as well as allow them to feel a deeper connection to your brand.
The Chat SDK for iOS is delivered as a binary .xcframework file
Install: Homebrew
Paste that in a macOS Terminal or Linux shell prompt; see more instruction at https://brew.sh
Install: git-lfs using: brew install git-lfs
Starting from version 4.3, the use of the .xcframework will reduce the required number of installation steps and also support the use of same binary, even when the Xcode version changes. We also include an additional framework as a dependency: Realm.
In order to properly install the SDK, you must make sure to import and link both frameworks. XCFrameworks for both realm & our Chat SDK are bundled together for your convenience, this can be downloaded using the link below:
Drag EkoChat.xcframework
and Realm.xcframework
to your project's Embedded Binaries
. Make sure that Copy items if needed
is selected and click Finish. Also switch the Embed section as Embed & Sign
.
Carthage is a decentralised dependency manager that builds your dependencies and provides you with binary frameworks. To integrate the Amity Chat SDK, add the following line of code to your Cartfile
.
To integrate the Amity Chat SDK into your Xcode project using CocoaPods, specify the following line of code in your Podfile
:
Please check if you have git-lfs installed and clear cocoapod cache before running install again
To clear cache please go to ~/Library/Caches/Cocoapods/ and remove Amity SDK folder - you should be able to run a clean install afterward
If this doesn't work, please do visit the cocoapod Github repo for further resolutions.
Here are the steps to migrate from the previous version of our SDK to the new .xcframework
Remove and unlink EkoChat.framework
and Realm.framework
from your project.
Remove custom Run Script Phase
that is required for the version prior to v4.3. strip-frameworks.sh
is no longer required, since .xcframework
automatically handles the fat binary issue.
Copy and link EkoChat.xcframework
and Realm.xcframework
to your project. And set Embed & Sign
.
This section outlines how you can set-up your project for success and begin using our Chat SDK for iOS application.
Before using the Chat SDK, you will need to create a new SDK instance with your API key. Please find your account API key via the Admin Panel. If you have trouble finding this, you can send our support team an email at developer@amity.co
In order to use any Chat SDK feature, you must first register the current device with an userId
. A registered device will be tied to the registered userId
until the device is either proactively unregistered, or until the device has been inactive for over 90 days. A registered device will receive all the events messages belonging to the tied user.
An optional displayName
can be provided, which will be used in standard push notifications (related to user's actions, such as when the new message is sent).
The
displayName
is set only on the first time the device is registered, please follow your platforms necessary directions if you would like to rename this to something else.
When the user logs out, you should explicitly unregister the user from the SDK as well. This prevents the current device from receiving unnecessary and/or restricted data.
Each user can be registered, at the same time, to an unlimited number of devices. Amity's Chat SDK will automatically synchronize the user data across all registered devices. We will also automatically unregister any device that has not been connected to the server for more than 90 days.
When a device is unregistered due to inactivity, the SDK data on the device will be reset. You will need to re-register this device in order to connect to server again.
If you have any logic or UI around the connection status, you can observe the connectionStatus
property on the EkoClient
instance.
Since the Chat SDK automatically manages the network connection and queue up any requests in cases of bad connection, there should be little need to attach additional logic to this status. However the user may want to know the exact network status to determine if their actions will be performed in real-time, therefore this status is exposed.
Our Sample app adopts an open source framework that highlights how Amity Social Cloud SDK's can be implemented into application builds pragmatically.
With real life use-cases, we guide you through ways you can get started with building stellar applications for yourself and your clients and their users
In iOS, LiveObjects are represented by the EkoObject
and EkoCollection
classes. EkoObject
represents live updates on one model, while EkoCollection
represents live updates on a collection of models.
Observing live changes to any EkoObject
can be done via a simple observe block:
In this example the block observes the data of the currently authenticated user and prints out the displayName
. The observe block can be called multiple times throughout the lifetime of the application (as long as its associated EkoNotificationToken
is retained in memory):
If the requested object data is stored locally on the device, the block will be called immediately with the local version of the data (this can be verified this through the EkoObject
property dataStatus
).
In parallel, a network request for the latest version of the data is fired, once the network returns the data, the observe block will be called again with the updated data.
Any future changes to the data (whenever the user changes its displayName
on another device, for example) can trigger additional callbacks.
Any UI update logic can be performed within the observe blocks, which will always be called in the main thread: this way, you can ensure that your UI displays the most up-to-date version of the state at all times.
In case you'd like to operate exclusively with fresh data, without using the potentially out-of-date local data, you can make sure to do so by reading the
EkoObject
dataStatus
property, which reflects the status of the callback data, and check that its value is set to fresh.You can also use the
EkoObject
loadingStatus
property to determine the current state of network operations being performed by the LiveObject. This is useful for any UI element that needs to communicate the loading state.
The EkoNotificationToken
is a token that is used to control the lifecycle of a LiveObject. The observeWithBlock
callback will be triggered only if the notificationToken
is retained in memory: once it is released, the observe block will no longer be called, and its memory will be automatically released. Hold a strong reference to any EkoNotificationToken
whose block you would like still to be called.
Beside the
observeWithBlock
method, aobserveOnceWithBlock
method is also available, which will trigger its callback only once, regardless of thedataStatus
of the callback and the associated token retainment (the token still needs to be retained to fire once).To stop receiving
observeWithBlock
updates, release the associatedEkoNotificationToken
or callinvalidate
method on the token itself.
An EkoCollection
instance is given for any queries that return a list of objects. EkoCollection
has the same change observation interface as EkoObject
, but contains a few other helper methods around data access that allows better integration with collection views like UITableView
.
Unlike most databases, EkoCollection
does not return all data in an array. Instead, data are fetched one-by-one using the objectAtIndex:
method. This allows the framework to store most of the actual result on disk, and load them in memory only when absoutely necessary. Additionally, the objectAtIndex:
API fits perfectly into UITableView
's tableView:cellForRowAtIndexPath:
. Together with the count
property, a typical tableView
integration looks like this:
EkoCollection
offers both a nextPage
and previousPage
methods which will trigger a local cache lookup, a network call, and multiple LiveObject updates once new data is returned. After the method call is successful, the number of records returned by EkoCollection
will be increased by the additional number of new records. For the typical use case of infinite scroll, you can call nextPage
directly in the scroll view delegate method:
Lastly, if there is a need to shrink the list of models exposed back to original first page, when passing the EkoCollection
object to a new view for example, you can do so by calling resetPage
into the collection itself.
Amity SDK's do not store or manage any user data. This means that you do not have to import or migrate existing user profiles into the system, user management should be handled by your application code. Instead, every user is simply represented by a unique userID
, which can be any string that uniquely identifies the user and is immutable throughout its lifetime.
A database primary key would make an ideal
userID
. Conversely, something like username or emails is not recommended as those values may change overtime.
If you wish to assign additional permissions for a user, for example moderation privileges or different sending limits, you can provide an array of roles to assign to this user. Roles are defined in the admin panel and can be tied to an unlimited number of users. Once again, Amity does not store or manage any user data, which means you do not have to import or migrate existing users into the system. It also means that Amity cannot provide user management functionalities like lists of users, or limit actions of certain users (e.g. user permissions). Instead, these functionalities should be handled by the rest of your application's capabilities and your server.
Though the SDK does not store and should not be responsible for the handling User profile data for your application; We do provide tools to make some surface-level queries and searches for existing user accounts. With the help of our EkoUserRepository
class, you would be able to list all the users, search for list of users whose display name matches your search query and get EkoUser
object from user id.
Each User consists of a userId
and displayName
. The userId
is immutable once the account is created, however the displayName
can be updated at all times. EkoUserRepository
provides a convenient method userForId(_:)
which map user id to particular EkoUser
object. It returns a live EkoObject<EkoUser>
which you can observe too. It accepts one parameter userId
which is the userId of the user.
EkoUserRepository
provides a convenient method getAllUsersSortedBy(_:)
to fetch all users. This method will return EkoCollection<EkoUser>
. You can observe for changes in collection, similar to message or channel. The method accepts EkoUserSortOption
enum as a parameter. The list can be sorted by displayName
, firstCreated
or lastCreated
.
The code above will provide you with list of users which are sorted by displayName
.
EkoUserRepository
contains another convenient method searchUser(_:_:)
which allows you to query for any particular user using their display name. It provides you with a collection of EkoUser
whose display name matches with your search query. It accepts two parameters. The first one is the display name that you want to search for and the second one is sort option which is EkoUserSortOption
enum.
The code above will provide you with the list of users which matches with display name "abc".
EkoClient
class provides a method hasPermission(_:)
which allows to check if the current logged in user has permission to do stuffs in given channel.
Our channels enable developers to implement different types of chat messaging capabilities into their applications easily
Users can create and join channels where they will be able to participate and chat with other users. A channel can support up to 300,000 members and can contain an unlimited number of messages. Any message exchanged in the channels will be pushed to all other members of the channel in real-time.
Amity's Chat SDK supports the creations of 4 types of chat channels. Each type is designed to match a particular use-case for chat channels. Here's a table showing what features each channel offers:
The community channel is our default channel type and can be discovered by all users and admins. It acts as a public chat channel that showcases all of the features that our SDK's have to offer.
Typical use cases:
Team collaboration
Online gaming
Celebrity fan club
Live streaming
Any type of public chat
Live channels offers the ability for users and admins to create channels with exclusive memberships. The live channel is identical to our Community channel in features with the caveat that users will not be able to discover the channel when querying for all channels unless they are already a member of it. However users and admins can still invite other users to join the channel.
Typical use cases:
Healthcare
Project Discussion
Any type of private chat
Community and Live channel types can use our SDK moderation tools:
Message and user flagging
Muting/Unmuting users
Banning/Unbanning users from channel
Profanity filters
Whitelisted URLs
User rate-limiting
All broadcast channels are visible on the Amity Social Cloud Console.
The Broadcast channel is heavily adopted by corporate users who constantly promote or advertise their products, or make the announcement to drive awareness. Unlink other channel types, broadcast channels only allow admin users to send messages from Console, and everyone else in the channel will be under read-only mode.
Since this is a one-way communication channel, a tailored moderation tools are provided as well, for instance, users won't be able to flag message / user in the channel.
Typical use cases:
Marketing & Advertising
School / Government Announcements
Conversation channels are NOT visible on the Amity Social Cloud Console.
The Conversation channel is our solution to 1-on-1 messaging. Unlike the other channel types, a Conversation channel can be created simply by knowing the userId of the user we want to converse with. Users can start conversations with any other user and only they will be able to see their conversation.
There are no moderation tools for Conversation channels, users will be able to converse freely with no oversight!
Typical use cases:
Hospitality
Financial Consultancy
Customer Support
When a user joins a channel, they are able to observe and chat with other users in that channel. They are also automatically considered a member of that channel. The Chat SDK provides the ability to view which users are currently in the channel as well as invite other users to join the channel.
Each channel is identified by a unique channelId
, which is any string that uniquely identifies the channel and is immutable through its lifetime. When creating channels, you can specify your own channelId
, or leave it to Amity's Chat SDK to automatically generate one for you.
It's important to note that, createChannel:
guarantees that the requested channel is a new channel, whereas joinChannel:
will attempt to join an existing channel. If there is a requirement to create a new channel, then use of createChannel:
then call joinChannel:
. Lastly calling getChannel:
only gives you back the channel LiveObject, but it won't make the current user join said channel.
Channel management methods are contained in a EkoChannelRepository
class. Before being able to call any channel method, you must initialize a repository instance using the EkoClient
instance you created during setup:
EkoChannelRepository
provides createChannel()
method to create a new channel. It supports creating of 3 types of channels Community
, Live
and Conversation
. Each channel type has specific builder classes which helps you to create that particular channel. Build your channel information first and then create the particular channel.
The above code creates a channel and notifies you through the observer block. It first instantiates the EkoChannelRepository
, a class that contain all channel related methods. Then it calls createChannel:
to obtain the LiveObject and observe it in order to obtain the final channel model.
The
EkoNotificationToken
returned by theobserve:
is saved inself.channelToken
, a strongly referenced property. This is needed in order to prevent the observe block from being released. You should note that this block can get called multiple times, when the underlying data for the channel updates. If you don't want to get notified, you can callchannelToken.invalidate()
. As soon as the token gets invalidated, observer is automatically removed from that channel.In the case that there is already an existing channel with the same
channelID
, the LiveObject will notify you with anerror
.The
channelId
parameter increateChannel:
can benil
: when this happens, the SDK will generate a uniquechannelId
for this channel, ensuring no unique ID conflicts.3 channel types can be created through SDK i.e
Community
,Live
andConversation
. Creation ofPrivate
andStandard
type has been removed. Creation ofBroadcast
channel type is not supported through the SDK. But for query,channelCollection:
method supports all channel types includingBroadcast
,Private
andStandard
.
The Chat SDK provides a convenient createConversation:
method which creates a conversation channel type. You can use either createChannel
or createConversation
method directly as well.
Conversation channel is unique based on its membership. When creating conversation the system will check if channel with the same membership already exists, if such channel already exists the system will return existing channel instead of creating a new one.
joinChannel:
is an idempotent method, this means it can be called multiple times throughout the lifecycle of the application, and you can expect this method to always return the same channel. Because of this, you can also use joinChannel:
any time you need to fetch a channel, even if you know the user may already be in the channel.
In the case where you'd like to fetch a channel's data without joining, the getChannel:
method can be used:
EkoChannelRepository
provides a way to query list of channels using channelCollection()
method. It returns a EkoCollection
of all the matching channels available. This live collection returned will automatically update and notify you on any channel modifications as well.
SDK provides with 7 builder classes. EkoStandardChannelQueryBuilder
, EkoPrivateChannelQueryBuilder
, EkoByTypesChannelQueryBuilder
, EkoBroadcastChannelQueryBuilder
, EkoConversationChannelQueryBuilder
, EkoCommunityChannelQueryBuilder
and EkoLiveChannelQueryBuilder
. These builder classes should be used to construct a query and then used alongside channelCollection method to fetch list of channels.
Depreciated:
channelsForFilter()
,channelsForFilter(_:includingTags:excludingTags:)
method to query channels is now depreciated. SDK now does not support creation of Private & Standard channels but still supports query usingEkoStandardChannelQueryBuilder
EkoPrivateChannelQueryBuilder
.
If you use a UITableView
or UICollectionView
to display channel list data, the ideal location to reload table data is directly in the observe block of the LiveObject that you are displaying, as shown in the example above.
You can filter channels by various criteria such as includingTags, excludingTags, includeDeleted channels etc. All these filters are available in QueryBuilder classes for channel.
Metadata is a general purpose data store that is automatically synchronized to all the channel members. It is meant as an elegant mechanism to store contextual information about a specific channel. The data can be any number of JSON key value pairs up to 100 kb. Example use cases include:
Conversation title or cover photo
Global conversation settings
Metadata is implemented with last writer wins semantics: multiple mutations by independent users to the metadata object will result in a single stored value. No locking, merging, or other coordination is performed across multiple writes on the data.
To set metadata, call the setMetadataForChannel:
method:
The completion block will be triggered with the outcome of the request. The latest metadata of the channel is always exposed as part of the metadata
property on the channel model.
Every channel contains an optional displayName
property. This property is mainly used to identify the channel in push notifications, but it is also exposed to the application via EkoChannel
object.
You can set a channel's displayName
with the following method:
An optional completion callback is available to inform you on whether the request has succeeded or not.
All participation related methods in a channel falls under a separate EkoChannelParticipation
class. Before calling any participation methods, you must ensure to first instantiate a repository instance using the EkoClient
instance you created on setup and a valid channelId
:
The participation membership provides a list of all members in the given channel as a LiveObject.
You can also get a list of channel members with specific roles, membership types etc. EkoChannelParticipation
class provides membershipsForFilter:
method which accepts filter
, sortBy
and roles
as parameter.
The participation membership also provides classes to add and remove members, as well as removing yourself as a member of the channel (leaving the channel).
The EkoChannelRepository
object exposes an totalUnreadCount
property that reflects the number of messages that the current user has yet to read. This count is the sum of all the unreadCount
channels properties where the user is already a member.
To let the server know when the current user is reading one channel, hence resetting that channel unreadCount
to zero, the participation membership exposes the startReading
and stopReading
methods.
You can call both methods as much you'd like, the SDK takes care of multi-device management: therefore a user can read multiple channels, from one or multiple devices at the same time. In case of an abrupt disconnection (whether because the app was killed, or the internet went down etc.) the SDK backend will automatically call the stopReading
on the users behalf.
EkoChannelModeration
class provides various methods to moderate the users present in any given channel. You can ban/unban/mute users, assign roles or remove it from user.
You can check your permission in channel using hasPermission(permission:forChannel:_:)
method from EkoClient.
The connectionStatus
flag supports , use NSObject
's observe methods to be notified whenever this status changes
Name | Data Type | Description | Attributes |
|
| The id of this user |
|
| A list of user's roles |
|
| The display name of the user |
|
| The number of users that have flagged this user |
|
| The metadata of the user |
|
| A hash for checking internally if this user was flagged by the user |
|
| The date/time the user was created at |
|
| The date/time the user was updated at |
Channel Type | Discoverable by | Message sending privileges | Moderation access |
Community | All users and admins | Users and admins | All Moderation tools |
Live | Only members and admins | Users and admins | All Moderation tools |
Broadcast | All users and admins | Admins | Admin Moderation tools |
Conversation | Only members | Users | No Moderation tools |
Moderation is an important feature for building a safe community that encourages user participation and engagement.
Amity’s customer centric nature ensures that security needs are kept at the forefront of the work we do. Our purpose has been to continuously develop features that are safe and ready to use. We power our moderators with tools to control and impose permissions that make their applications a safer place, for all users. We put the utmost importance on giving power to our clients to implement protocols that keep their applications healthy, safe and compliant.
Interactions are more fun when you can express yourself! Let users react using emojis, stickers, or thumbs up to messages.
To query for a list of all reactions on the specific message in a channel, one must utilise EkoMessageRepository
. We can observe this the same way as when we observe messages, the only thing difference is you will have to use messageId
as the parameter. The operation will return an instance EkoCollection
of EkoMessageReaction
that contains the reaction name, message id, reactor id and reactor display name of the user who reacted to the message.
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.
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.
In order to remove a new reaction, we can use EkoMessageReactor
. Initialize the reactor with the client, message and specify the reaction name.
This page highlights the steps you will need to follow to begin integrating chat messaging into your products
The message payload is always the same regardless of which Development Kit the user is using. Users also have a choice on what type of message they want to send.
Amity supports the sending and receiving of 5 types of messages. They are Text, Image, Audio, File, and Custom.
Amity will automatically optimize the image and when queried, will return the image in small, medium and large sizes. If the image is marked as isFull
on upload, the original size of the image can also be returned.
When an image is uploaded, it is automatically resized into multiple sizing options. The size of the image is determined by its longest dimension (in pixels) with the aspect ratios being unchanged.
The maximum file size of an image cannot exceed 1 GB.
When a user is observing messages, the message will always appear in this format.
Messages are JSON content containers that can weigh 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 separate 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.
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 incase of any unforeseen unstable network conditions. Once the SDK reconnects to the server, it will automatically resend all the 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 for your application, use the syncState
property in the message model.
Every message sending method returns you a live object. This live object should be observed until message has been synced with the server.
Initiate the messaging with the following scripts, depending on your platform of choice.
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.
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 thefullImage
is set tofalse
, then the uploaded image size will be up to 1500x1500 pixels.
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.
Viewing File Message
The server will automatically generate a filename for you under the
fileName
key on the message data property, based on the file name that you choose to use. We advised that you handle the file size issues beforehand, as currently our limitation is set to less than 1 GB.
You can send small voice clips as a message.EkoMessageRepository
provides createAudioMessage:
a method to send voice clips to other users. It accepts voice clips as a NSData
instance.
To send a custom message, you must pass in a Dictionary
instance. The server will populate this as a JSON
format. Later on, you must parse it by yourselves depending on the key and value that you set earlier on.
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.
To query for a list of all messages in a channel:
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.
To get a specific message:
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.
There are other situations where fetching the oldest messages is preferred, for example for archiving purposes or in community forums.
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.
For commenting a message, we can observe the comment on the message from EkoMessageRepository
and observe 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.
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.
Images in messages are not downloaded automatically when the client receives a message. In order to save bandwidth, images should be downloaded only when they're about to be displayed. A typical implementation to trigger the download would be in the tableView:cellForRowAtIndexPath:
method. You can download an image using downloadImage:
method from the same EkoMessageRepository
class. You need to pass the EkoMessage
instance and the method provides the appropriate image depending upon the sync state of message.
Please use the appropriate image size depending on your use-case 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 thecompletionHandler
is idempotent.
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.
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 of the user and the reaction used.
This method will return a LiveObject of all the reactions of a specific message in the specified channel. You can observe the LiveObject in order to update your view whenever you receive a new reaction on that message.
For adding a new reaction, we can use EkoMessageReactor
to perform its operation. Initialize the reactor with the client and message, don't forget to specify the reaction name as well.
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.
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
.
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.
When a user is banned in a channel, they are removed from a channel and no longer able to participate or observe messages in that channel.
Moderators can ban and unban users. When a user is banned in a channel, they are forcebly removed from channel and may no longer participate or observe messages in that channel. All their previous messages in the channel will also be automatically deleted.
A user that has been banned from a channel can not rejoin the channel until they have been unbanned.
As well as the banning and unbanning of users, admins also have the ability to global ban a user. When a user is globally banned, they can no longer access Amity's network and will be forcebly removed from all their existing channels. All the globally banned user's messages will also be deleted.
The globally banned user can not access Amity's network again until they have been globally unbanned.
Banning users is a more heavy handed moderation method. When a user is banned, all its messages are retroactively deleted, it will be removed from the channel, and it will not be allowed to join the channel again until he is explicitly unbanned.
There is a separate unban call to unban a user:
Note: This feature does not work with Broadcast
and Conversation
channels. Calling banUsers()
or unBanUsers()
on these channels will result in an error.
When a user is muted, they can not send messages in a channel.
Moderators can mute and unmute users. When a user is muted, they cannot send messages in a channel. However muted users will still be allowed to observe messages in a channel. The status of being muted is indefinite but is only applied at the channel level.
When a user is muted, all messages sent by that user to that channel will be rejected. This method is useful for preventing certain users from sending inappropriate messages, but still allowing them to participate in the conversation in a read-only manner. The timeout property allows you to make the timeout temporary, or permanent by until unset by passing in -1
.
If you want to permanently mute a user, pass in
-1
as the mutePeriod. The user will stay muted until you explicitly unmute that user.
To unmute an user, call unmuteUsers:
:
Rate limiting a channel controls the speed of messages that is published to all concurrent clients in the channel
This method is useful when there is a large amount of messages going through the channel, which can make the message stream hard to follow. Setting a rate limit enables the SDK to queue up messages once the amount of message in a specified window
exceeds the defined limit
, allowing a slower stream of messages to be published to the user at the expense of adding more latency (because newer messages will be sent to the queue first and not delivered until all previous queued messages are delivered).
There is an internal limit of 1000 messages that can be queued by the rate limit service, if more than 1000 messages are queued up, the system may skip publishing the older messages in order to make room for newer messages. We believe this is the preferred behavior for users, as users will most likely want to see newer messages in a real-time conversation instead of waiting for a significant amount of time for old messages to be published to them first.
Note that the SDK permanently stores all messages it receives in the system before the rate limit comes into effect: in the case of a large spike of incoming messages, even if a message did not get published to a user in real-time, that user can still scroll up to see message history and see that past message.
The above method enables a rate limit of 5 messages every 60 seconds: once there are more than 5 messages sent, from any user, within 60 seconds, those messages will be queued on the server and not published to other channel members until 60 seconds have passed. The rate limit will last as long as the period specified in the method call: in the example above the rate limit will be active for 10 minutes (600 seconds).
If you would like to have a permanent rate limit, call the method above with a period of -1 seconds.
In order to disable the rate limit, simply call remoteRateLimitWithCompletion:
:
Name
Data Type
Description
Attributes
messageId
string
The id of this message
Content
parentId
string
The messageId
of the parent of this message
Content
childrenNumber
integer
The number of messages with parentId
of this message
Content
channelId
string
The name of the channel this message was created in
Content
userId
string
The name of the user this message was created by
Content
type
string
The message type
enum*: text
custom
image
file
tags
Array.<string>
The message tags
Content
data
Object
The message data (any text will be stored in text
key)
text
: Text message
isDeleted
boolean
The message has been marked as deleted
Content
channelSegment
integer
The sequence number of a message in channel
Content
createdAt
date
The date/time the message was created at
Content
updatedAt
date
The date/time the message was updated at
Content
editedAt
date
The date/time the message was edited at
Content
flagCount
integer
The number of users that have flagged this message
Content
hashFlah
Object
A hash for checking internally if this message was flagged by the user
Content
reactions
Object
The reaction data (stored as a reactionName and counter key/value pair)
Example: { like
: 1, dislike
: 2 }
reactionsCount
integer
The total number of reactions on this message
Content
myReactions
Array.<string>
A list of user's reactions on this message
Content
Error objects can be returned to you via LiveObjects, callbacks, or clientErrorDelegate
. All the errors returned by the SDK come in form of an NSError with domain Eko
. The possible error codes are listed in a public EkoErrorCode
enum: each case is named after its error and they're pretty self explanatory.
UnauthorizedError: 400100
ItemNotFound: 400400
BadRequestError: 400000
Conflict: 400900
ForbiddenError: 400300
PermissionDenied: 400301
UserIsMuted: 400302
ChannelIsMuted: 400303
UserIsBanned: 400304
NumberOfMemberExceed: 400305
ExemptFromBan: 400306
MaxRepetitionExceed: 400307
BanWordFound: 400308
LinkNotAllowed: 400309
GlobalBanError: 400312
BusinessError: 500000
Unknown: 800000
InvalidParameter: 800110
MalformedData: 800130
ErrorQueryInProgress: 800170
ConnectionError: 800210
When an error is returned as a result of an action from your side (e.g. trying to join a channel), the action is considered completed and the SDK will not execute any additional logic.
The EkoClient
includes a clientErrorDelegate
property that can be set to an error handler delegate class on your application. This error delegate gives you a chance to be notified of errors that can potentially break the functionality of the SDK. The SDK logic is usually robust enough to automatically handle most errors, as such, only unrecoverable errors are exposed through this delegate (for example, if the login session was invalidated).
We recommend you to always handle these errors in a production app by gracefully disabling messaging functionality in the event of an error.
flag and unflag a message
While having moderators surveying your chats is great, it doesn't scale well. A way to overcome this is to let your users do the work for your moderators. By letting users flag other users or specific messages, the work of moderators is significantly reduced and democratized, thus allowing administrators to only respond to issues when deemed critical or absolutely necessary.
In order to flag a message, create an instance of EkoMessageFlagger
first:
The EkoMessageFlagger
lets you flag and unflag a message, and it also exposes a asynchronous way to check whether the current user has flagged the given message already:
To flag a message, call the following method:
To unflag a message, call the following method:
The User can also check if they have previously flagged the message before by calling the following asynchronous method:
Remember that each
EkoMessageFlagger
is tied to one specific message.
Flag and Unflag a channel user
While having moderators surveying your chats is great, this doesn't scale well. A way to overcome this is to let your users do the work for your moderators: by letting users report other users or specific messages, the work of moderators goes from scanning each message in each channel to investigate each user report (to both users and messages) and react only when deemed necessary.
In order to flag a user, create an instance of EkoUserFlagger
first:
The EkoUserFlagger
lets you flag and unflag a user, and it also exposes a asynchronous way to check whether the current logged-in user has already flagged the given user or not:
To flag a user, call the following method:
To unflag a user, call the following method:
Push notifications play a central role on your app user engagement.
With this solution live events are sent from Amity's servers to your servers: once an event lands on your server, you have full power and control to do whatever you feel is best with each notification; including editing/removing/stopping the notification before it reaches your users devices.
In this scenario there's no iOS SDK involvement needed, the whole notification process is managed on your end.
With this solution the notifications will be triggered and delivered to your users directly by Amity's servers. There's nothing that the iOS client has to do in order to display the notification to your users. Amity's servers will prepare for you a notification that can be directly displayed to the user as and when it's received.
As Amity's servers are responsible for choosing the content of the push notification, you can expect your users to receive the following notifications for different kind of events:
Event: New channel has been created and the user has been added among other members. Push Notification Title: %s
(%s
= New Channel display name) Push Notification Body: You're now member of %s!
(%s
= New Channel display name)
Event: A new user has joined a channel. Push Notification Title: %s
(%s
= user display name) Push Notification Body: %1s has joined %2s
(%1s
= user display name, %2s
= channel display name)
Event: A new message has been received in a channel where the user is already a member. Push Notification Title: %1s (%2s)
(%1s
= user display name, %2s
= channel display name) Push Notification Body: %s
(%s
= message text body if text message, Image Message
if image message, Special message
otherwise)
A new push notification will be sent to a specific user when:
A new message is sent in a channel of the user who is already an existing member of it.
A new channel is created and the user is among the listed members of the channel on creation.
A new member joins a channel of the user who is already an existing member of it.
Registering your app for push notification will require a registered EkoClient
instance (necessary to know which user is associated with this device) and a push notification token.
Amity's Development Kit does not manage:
user-facing requests for push notifications and authorizations
the creation and refreshing of push notification tokens
It's up to your app to take those steps and pass the notification token to the SDK.
We recommend to observe the completion block outcome to assure of a successful registration.
If the device was previously registered with this or another user, the previous registration is invalidated as soon as this new request is received, which means that the device will always receive notifications of up to one user.
Unlike the registration, unregistering for push does not require the EkoClient
instance to be associated with any user, therefore you can unregister the device from receiving push notifications as soon as the EkoClient
has been initialized with a valid API key.
The unregistration allows to pass an optional userId
:
if a valid userId
is passed, Amity's backend will stop sending push notifications to this device only if the currently active push notification associated with this device is also associated with that user. No action is taken otherwise.
if no userId
is passed, Amity's backend will stop sending push notifications to this device.
You can register and unregister as many times as you'd like, however please remember that we use the "Last write wins" strategy.
The SDK has three levels of notifications and in order for it to be sent, a notification has to pass throughout all three levels.
Network Level: (via Admin Panel) turning off notifications at this level effectively disable push notifications altogether for all of your customers.
User Level: (via client) A user can choose to enable/disable the notifications that it receives on the device (this is an absolute option: enable all or disable all). Please note that this setting is per user, not per device: regardless of which device sets this toggle, the new preference will take effect in all the devices where the user is logged in.
Channel Level: (via client) A user can choose to enable/disable notifications for a specific channel (where is member of). Again, this preference is per user, not per device.
In order to get and set the user level push notifications preference, we use the object EkoUserNotificationsManager
, obtained from the current EkoClient
:
For channel preferences, we use the ChannelLevelPushNotificationManager
instead, obtained via an instance of EkoChannelRepository
:
Swift
This page contains an overview of all relevant changes made to the Amity Chat SDK modules and the latest version releases
Introduce a completion parameter to registerDeviceWithUserId(:_)
in EkoClient
None
Fixed issue where categories property of community is updated incorrectly when fetching list of community categories.
None
Fix socket connection state that causes feed cannot be loaded
Fix issue where token does not update correctly
Fix issue where collection is not triggered when there is empty result
Fix edited date does changed after the comment get updated
None
Fix issue where custom messages are not parsed correctly
Fix crash when setting up custom avatar for in EkoClient
class.
None
N/A
Add EkoUserNotification
and EkoUserNotificationModule
class for supporting user notification settings
Add EkoCommunityNotification
and EkoCommunityNotificationEvent
class for supporting community notification settings
Add notificationManagerForCommunityId:
method in EkoCommunityRepository
class to provide EkoCommunityNotificationsManager
Add EkoRoleFilter
and EkoRoleFilterType
; class and enum for defining what roles would be affected by notification setting
Add displayName
option in EkoCommunitySortOption
enum to sort list of communities alphabetically.
Fix issue where dataStatus
for EkoObject
was not reflected correctly.
Add EkoCommunityFeedSortOption
enum.
sortBy
parameter type is changed to EkoCommunityFeedSortOption
for getCommunityFeed:
method in EkoFeedRepository
class.
Add EkoCommunityMembershipSortOption
enum.
sortBy
parameter type is changed to EkoCommunityMembershipSortOption
for getMemberships:
method in EkoCommunityParticipation
class.
Note: If you have been using enum in short form i.e
.displayName
, these breaking changes would not cause any effect.
Fix issue where post was not getting removed from global feed when deleted
Fix issue where non members could search for private communities locally
Comment in error state can be hard deleted by deleteComment()
api in EkoCommentEditor
Fix issue where deleted comment was being shown from cache
SDK is now distributed as xcframework. .dsym files are also included inside the framework. Please refer to installation docs for more info
Add new message type i.e audio. Please refer to Message documentation
EkoMediaRepository
has been depreciated. Please migrate to downloadImage:
& downloadFile:
method present in EkoMessageRepository
.
Add roles
properties in EkoCommunityMembership
class
Add hasPermission:
methods in EkoClient to check permissions for current user.
New Channel Types: Community
& Live
. Channel type Private
and Standard
has been removed. Please refer to channel documentation for more details
Older createChannel:
methods has been removed. Use new method with builder instead.
type
parameter is removed from joinChannel
method.
Older createConversation:
method has been removed. Use new createChannel
or new method with builder instead.
Add EkoCommentRepository, EkoCommentEditor and EkoCommentFlagger class. Complete documentations are on Comment section.
Add EkoReactionRepository model support reactions.
Add EkoReaction class to provide reaction info.
Support Xcode 11.7
Fix bug on membership
property on EkoChannelUserModel
which has wrong result
Fix bug on isEdited
property on EkoMessage
which has an unconsistent result
No need to add zlib
integration anymore
Support Cocoapod and Carthage installation
Resolve crash when trying to access editedAt
timestamp for message
Add isEdited
property on EkoMessage
to check if message has been edited
Resolve allow display name nil
and empty string on registerDevice
Support Xcode 11.6
Resolve bug on conversation channel filtering
Add getAllUsersSortedBy
method to fetch list of users in EkoUserRepository
.
Add searchUser
method to search list of users with their display name in EkoUserRepository
.
Add new UI for showing list of Users.
Refactor UI to show two tabs, one for list of users and another for channels/chat.
Support getMessage
by messageId
from EkoMessageRepository
using getMessage(messageId)
function.
Add new 'UI' for select the channel type for query.
Add new 'UI' for re-select the channel type on More
page.
Fix 'UI' for create new channel type.
Support query
all channel
types which is Standard
, Private
, Broadcast
, Conversation
, and combination.
Support creating 2 channel
types which is Standard
and Private
.
New API for creating a conversation
is embedded into EkoChannelRepository
which is createConversation
.
Add new function for retrieving the channelCollection
object by query
it through new Builder
pattern. Consists of 5 type of Builder
which is EkoStandardChannelQueryBuilder
, EkoPrivateChannelQueryBuilder
, EkoByTypesChannelQueryBuilder
, EkoBroadcastChannelQueryBuilder
and EkoConversationChannelQueryBuilder
. Each builder has their own configuration settings based on their requirements.
New getter of channelCollection
on EkoChannelRepository
that will return the EkoChannelQueryBuilder
following by the channel type then call query
method, observe the changes using EkoNotificationToken
.
Support private
and standard
channel type for createChannel
.
New enum for creation which is EkoChannelCreateType
.
New method helper inside EkoChannel
for conversion between EkoChannelCreateType
into EkoChannelType
if needed.
channelsForFilter
and channelsForFilter:includingTags:excludingTags
is deprecated function.
joinChannel
only works for join existing channel, it doesn't work for join while creating a channel simultaneously.
Support getMessage
by messageId
from EkoMessageRepository
using getMessage(messageId)
function.
Add new 'Like' UI on each message for react of Like and Unlike to test the optimistic UI.
Change implementation of EkoMessageReactor
so it able to retain the process of previous reactor.
Add optimistic UI on the Reaction
implementation.
Give safeguard for RLMRealm
access of the properties in each object.
Protect setDisplayName
of EkoClient
to be able sent with empty userId
.
Set EkoObject
to be nil in any case if model not exist or invalidated.
Protect joinChannel
parameters attribute with nonnull
protection to guarantee the channelId
is not null
value.
Make sure if the realmModel
is invalidated
then it changes the dataStatus
to be notExist
and set the EkoObject
to be nil
and continue the network request process.
Add new 'Setting' section in the settings page for updating the user meta data.
Add new 'User Meta data' section inside about page for showing the user meta data.
Add setUserMetadata
function inside EkoClient
object for updating the current user meta data.
Fix crash that happens if show reaction with empty display name.
Fix reactions model is not auto removed when someone removes the reaction.
Fix and enhance query system to clear reactions cache on first query.
Fix handle case when display name is empty.
Add support for Xcode 11.3.1.
Support query all reactions.
New method for observing all reaction which is allMessageReactionsWithMessageId
within a message on EkoMessageRepository
for retrieving all of reactions inside a specific message.
New properties on EkoMessageReaction
which is reactorId
and also reactorDisplayName
for getting the user id of the reactor along with the display name.
Add action sheet for displaying all of reactions the single message.
Support add and remove reactions.
New class EkoMessageReaction
and EkoMessageReactor
.
Add Message reaction feature, a reaction can be added or removed from a message with EkoMessageReactor
.
New properties on EkoMessage
which is reactions
data and also reactionsCount
and myReactions
.
Add action sheet for adding and removing reactions on the message.
Add action sheet for displaying all of own reactions on the message.
Change update data endpoint.
Support update custom message.
Sample App Changes
Add action sheet for editing or deleting the message by clicking on the selected message cell.
Support Custom message type:
A message of this type can be created with a Dictionary
data that following a JSON
format that the developers are responsible to parse it by themselves.
This object is included in the data
property of the message.
Support File message type:
A message of this type can be created with an NSData
of the choosen file. File could not exceed than 1 GB.
Add upload file button to choose file from the device.
New file cell view for the file message type.
Parse custom message from server into a text cell.
Add new custom view for sending the custom message, limit to only sent a key and value as a String
.
Fix handling of deleted images
Add support for Xcode 11 and Swift 5.1
Message tagging support:
Messages can now be sent along with customized tags.
Messages can now be filtered by tags (both including and excluding).
Tags can also be set on any message.
Every message has a new tags
property.
Message sorting support:
Messages can now be fetched from the oldest first, or the newest first.
Message parenting support:
Messages can now be connected on creation by passing a new optional parentId
value.
Note how a message child can also become a parent itself, without any limit.
The parentId
must be passed on the child message creation and cannot be changed afterwards.
Every message has a new optional parentId
and childrenNumber
property.
Improve background transaction performances
Remove AFNetworking dependency
Memory improvements
Enhance code documentation
Add full message tags support
Add full message order support
Add full message parenting support
Add example of message parenting usage with a special comment screen:
join a channel with tag “comments
” to see the alternative experience
Enhance api key management
Display push notifications even when app is in foreground
Reset badge count upon opening the app
Enhance project structure
EkoMessageRepository
replaces the function messages(withChannelId:)
with messages(withChannelId:reverse:)
, along with many other new options such as messages(withChannelId:includingTags:excludingTags:reverse:)
, messages(withChannelId:filterByParentId:parentId:reverse:)
.
Be aware that all the functions above are convenience function to the complete messages(withChannelId:includingTags:excludingTags:filterByParentId:parentId:reverse:)
function.
Add Push Notification toggles:
users can now choose to mute selected channels or stop receiving notifications completely from any device.
The new settings, documented in the Notifications section, are per user, not per single device.
Adopt new push notification toggles.
Refactor various settings in new screens throughout the app.
Add Push Notification feature:
developers can call register and unregister for push notification directly from the EkoClient
instance. Read more about the feature in the new Notifications section.
Several internal performance and memory enhancements
Add notifications support (note: your implementation will work with Eko Notifications only in Production, not in sandbox/development)
Add new GlobalBanError
:
when a user is banned globally is automatically unregistered and the EkoChat sdk will be on a clean state. This event is sent to the EkoClient
's clientErrorDelegate
EkoChannel
has a new lastActivity
property
Fix register/unregister issue:
you can now use the same EkoClient to log in and log out multiple times with different users.
Improve documentation
Add possibility to login/logout with different users
ChannelListTableViewController
now displays channels types
Add possibility to choose channel type when creating a new channel
Several minor improvements everywhere
EkoChannelRepository
replaces the function channelsForFilter(:tags:)
with channelsForFilter(:includingTags:excludingTags:)
internal Realm objects are no longer exposed:
from this version there's no longer need for migrations when upgrading the SDK
actively purging Channel Memberships when new data comes from the server:
a new EkoChannelMembership
collection observation is needed in order to to trigger this
update channel filtering functionality with both including and excluding tags
remove channel videos
improve set channel tags UI/UX
improve MembershipListTableViewController
improve EkoMessagesTableViewController
EkoChannelRepository
replaces the function channelsForFilter(:tags:)
with channelsForFilter(:includingTags:excludingTags:)
remove markReadWithCompletion
function from EkoMessageEditor
. Use EkoChannelParticipation
start/stop reading functions instead
change channelsForFilter
behaviour:
a channel is now matched when it contains ANY tag listed in includingTags
, and contains NONE of the tags listed in excludingTags
. Previously a channel would match only if it contained ALL the listed tags. If you would like the old behaviour, please create your own filter on top of this method.
EkoChannelMembership
now behaves correctly
add Channel Membership List View
multiple minor improvements
Xcode 10.2 and Swift 5 support
Xcode 10.1 and iOS 12.1 SDK support
add isFlagByMeWithCompletion:
method in both EkoUserFlagger
and EkoMessageFlagger
EkoUserFlagger
and EkoMessageFlagger
now require, respectively, a EkoUser
and a EkoMessage
on initialisation
improve flag feature
add back iOS 9 support
new Flag feature:
flags are reported to the Admin panel for moderator to see
EkoMessage
and EkoUser
expose a new NSUInteger
flagCount
property
Introducing EkoMessageFlagger
and EkoUserFlagger
objects, both of which have two methods:
flagWithCompletion:
unflagWithCompletion:
EkoChannel
's tags
property is now indexed (which results in slower writes, bigger realm database, and much faster searches)
remove EkoMessage
's EkoMessageEditor
shortcut: this way the EkoMessage is a pure model. If you'd like this feature back, please write your own extension/category/wrapper around it.
Add Flag feature in Sample App
remove completion blocks from EkoChannelParticipation
's start/stop reading methods
EkoChannelRepository
createChannel
requires a new tags
parameter (read the below for more info)
EkoChannelRepository
channelsForFilter
requires a new tags
parameter (read the below for more info)
automatically call startRead
upon re-gaining connection with the server if the connection was ever lost (the SDK will keep track of the channels currently being read until the app is killed)
improve Realm migration logic
introducing EkoChannel
's tags
property:
this property is an optional array of strings that the developer can use for whatever purpose.
Via the EkoChannelRepository
you can now:
create a channel with tags via the createChannel:displayName:type:metadata:users:
method
query/filter channels by tags via the channelsForFilter:tags:
method
set tags on existing channels (each call will overwrite the previous channels tags) via the setTagsForChannel:tags:completion:
method
improve channels cleanup on startup: at every startup, the data of channels where the user is not a member is deleted
internal fixes and enhancements
implement channel tags usage in sample app
implement channels filtering in sample app
Xcode 10 support
add possibility to get channels based on the user membership (all channels, channels where user is member, channels where user is not a member) via the EkoChannelRepository
's channelsForFilter:
add membership
property in EkoChannelMembership
object: the possible values are member
, none
(not member), and banned
;
expose the device user membership
above also in the associated EkoChannel
object via the new currentUserMembership
property;
expose totalUnreadCount
in the EkoChannelRepository
new live reading feature:
every user can now start and stop reading a channel (via the EkoChannelParticipation
's startReadingWithCompletion
/stopReadingWithCompletion
methods), this will automatically update the unread count for said channel when receiving new messages for that channel.
implement unread count
, both total and per channel, and membership type
usage in sample app
internal fixes and enhancements
Internal changes (for gathering statistics on sdk version usage)