Live Objects and Live Collections are observable data holders. The observer gets updated when there is a change of data in the local cache.
There are two main sources that can make changes to the object and collection:
When an object gets updated by an action from the current device.
Real-time events of the subscribed objects from the server.
For an in-depth explanation on how your SDK handles Live Objects and Live Collections, refer to the platform-specific subpages in this section.
By subscribing to a specific topic in the Real-time event system, users can ensure that they are receiving the most up-to-date information and notifications related to the observed object across devices.
Live Object allows users to observe the states of a single object within the app, such as a post, message, or channel. With Live Object, users can automatically receive notifications of any data updates related to the observed object, whether from the network or locally.
Live Object also provides additional states, such as isLoading
and error
, which can be helpful for understanding the status of the observed object and any potential issues that may arise.
Live Object API is designed around the observer design pattern, allowing users to subscribe to an object, receive notifications of any data updates, and unsubscribe when necessary. These updates are delivered via Snapshot, a captured state that is frozen and cannot be changed. Users can own and access these snapshots, using them to stay up-to-date with the latest data within the app.
It's worth noting that while data updates may occur internally within the SDK, these updates will not affect previous snapshots. The only way for users to see the latest data is by getting new snapshots delivered via Live Object updates.
Live Collection allows users to observe changes to an entire collection of data within their app in real-time. Live Collection works similarly to Live Object, with the main difference being that Live Collection notifies changes related to the entire collection rather than just a single object.
To use Live Collection, users can observe to changes on a specific collection within the SDK. The collection handler then receives a snapshot of changes since the last result, which includes three possible events:
the indices of the objects that were deleted
the indices of the objects that were inserted
the indices of the objects that were modified.
By subscribing to changes on a specific collection using Live Collection, users can stay up-to-date with the latest changes and updates to important collections within the app. This can be particularly helpful for managing large amounts of data or monitoring changes to frequently updated collections, such as messages or posts.
In Flutter SDK, we have a concept of Live Object and Live Collection. Live Object is represented by StreamSubscription<Object>
instance and LiveCollection is represented by an instance of LiveCollection
. LiveCollection
is a generic class that encapsulates any other object and notifies the observer whenever any property of encapsulated object changes. Live Object helps to observe changes in a single object whereas Live Collection helps to observe changes in a list of objects. For example: StreamSubscription<AmityPost>
or LiveCollection<AmityMessage>
.
SDK handles lots of data received from various sources. Data can be present in local cache. It might also be queried from the server or received from some real-time events. What this means is that the same data is constantly updating. The data that you are accessing at the moment can get updated by other sources and becomes out of sync. Live Object and Live Collection help in syncing the data so you will always get the most recent one. Whenever the data updates, you will be notified through helper methods in Live Object and Live Collection classes.
New data gets automatically collected everytime when there is an update and user need not refresh to get the recent data.
StreamSubscription<Object>
is a native flutter class that keeps track of a single object. It is a live object. In Flutter AmitySDK
, any object which provides Stream is a Live Object.
Examples:
This function help listen to Live Object. Whenever any property for the observed object changes, the listen
callback will be triggered.
LiveCollection
is a generic class that keeps track of a collection of objects. It is a Live Collection. In Flutter SDK, any object which is encapsulated by LiveCollection
class is a live collection.
Examples:
MessageLiveCollection
ChannelLiveCollection
LiveCollection
exposes these methods:
asStream
onError
These methods help observe a Live Collection. Whenever any property for any object within the collection changes, the observer is triggered, as well as observing any errors from the collection.
asStream
method can get triggered multiple times throughout the lifetime of the application as long as its associated Stream<List<Object>>
is retained in memory.
asStream
method will be called from the main thread so you can perform any UI update-related task within the listen
block itself.
If the requested data collection is stored locally on the device, the block will be called immediately with the local version of the data.
In parallel, a request is made to the server to fetch the latest version of the data. Once the data is returned, the listen
block will be triggered again.
Any future changes to the data from any sources can trigger in the listen
block.
AmityCollection
in SDK returns a maximum of pageSize
items per page. It has loadNext()
method to fetch more data. It also exposes hasNext
property to check if next page or previous page is present.
Once next page is available, the same listen
block gets triggered and you can access the collection as shown above. If you want to shrink the collection to the original first page, you can do so by calling reset()
method on the same collection.
One typical usage of LiveCollection is in ListView
. Below is an example of fetching a collection and updating its state it in a Widget
.
In the Amity TypeScript SDK, we have the concept of Live Object and Live Collection.
Live Object is represented by an instance of Amity Object. It helps to observe changes in a single object.
Live Collection is represented by an instance of Amity LiveCollection. It helps to observe changes in a list of objects.
For example: Amity.Post
or Amity.LiveCollection<Amity.Post>
SDK handles lots of data received from various sources. Data can be present in local cache. It may also be queried from the server or received from real-time event. What this means is that the same data is constantly updating. The data that you are accessing at the moment can get updated and become out of sync.
Live Object and Live Collection helps in syncing data so you will always get the most recent one. New data gets automatically collected everytime when there is an update and user need not refresh to get the recent data.
Live Collection is available for the following in user/community feeds:
Post Collection
Comment Collection
Reactions Collection
Followers/Following Collection
Channel Collection
Message Collection
Channel Member Collection
Community Collection
Community Category Collection
Community Objects
Community Members Collection
Live Collection is not supported for global feed and custom post ranking.
Although live objects were introduced prior to v6. All getter methods for singular objects (example getPost
) will now return a subscribe-able object.
This means that if an object gets updated and you have subscribed to real time events, the object will get updated automatically via real time events.
If for your use case you don't require any real time updates, you can unsubscribe immediately. For further information about Live Object, please visit #live-object page.
Although live collections were introduced prior to v6. All query methods for collection of objects (example getPosts
) will now return a subscribe-able collection.
This means that if an object in the collection gets updated and you have subscribed to real time events, the collection will get updated automatically via real time events.
If for your use case you don't require any real time updates, you can unsubscribe immediately. Similar to live objects above. For further information about Live Collection, please visit #live-collection page.
All data returned by the SDK are wrapped in the SDK's LiveObject API. The LiveObject API allows you to easily retrieve the queried data asynchronously, as well as subscribe to any new changes to the data.
Observing live changes to any object queries can be done by observing the dataUpdated
event on the Live Object:
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:
If the requested object data is stored locally on the device, the block will be called immediately with the local version of the data (you can verify this through the dataStatus
property).
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.
We recommend you to always call removeAllListeners()
whenever you are done observing an event to avoid any unnecessary callbacks.
The data provided by LiveObject is directly accessible via the model
property. The model
property is always kept up to date with the latest state changes; wheneverdataUpdated
event is fired, the model
property has already been updated.
If you want to exclusively display fresh data in your UI (without using the potientially out-of-date local data), you can do so by reading the object's dataStatus
property, which reflects the status of the callback data, and check that its value is set to fresh.
You can also use the object's 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 provide the loading state.
The LiveObject can also emit events for updates for dataStatus
as well as loadingStatus
. As with other events, please make sure to call removeAllListeners()
when you are done observing changes to these values in order to prevent memory leaks.
The LiveObject updates statuses and data in strict order and emits related events accordingly when an instance is created. Few different cases might occurs when you create a LiveObject instance:
Initial values:
loadingStatus = LoadingStatus.Loading
dataStatus = DataStatus.NotExist
model = undefined
Process received data:
emits loadingStatusChanged
emits dataStatusChanged
emits dataUpdated
Initial values:
loadingStatus = LoadingStatus.Loading
dataStatus = DataStatus.Local
model = localData
Process received data (same order):
emits loadingStatusChanged
emits dataStatusChanged
emits dataUpdated
- only if data is really different
loadingStatus = LoadingStatus.Loaded
dataStatus = DataStatus.Fresh
model = localFreshData
This is the list of LiveObject members and methods.
Members
model
= model that the live object should fetch
loadingStatus =
determine the current state of network operations being performed by the LiveObject
dataStatus =
reflects the status of the callback data
Methods
on(event, callback)
= listens to event
and executes callback
when event
is fired
once
= same as the on
method but can only be called once
removeAllListeners
= removes all listeners from on
dispose
= causes the LiveObject to be destroyed and cleaned up
Here is a sample code on how the get Post data.
The LiveObject API supports queries that return a list of objects, this is known as a LiveCollection. LiveCollection has the same methods and properties as its object counterpart, but contains a few other helper methods around pagination.
Pagination with LiveCollections is very simple: the collection offers a convenient nextPage
method that you can call which will automatically trigger a local cache lookup, a network call, and multiple LiveObject updates once new data is returned. Every collection starts with one page of 20 models. After nextPage()
is successful, the dataUpdated
event will be triggered with a new array combining both the old objects as well as 20 newly fetched objects.
The dataUpdated
event will be dispatched when the first set of data from the server is loaded. Calling nextPage
will load the next set of data. Once all data are loaded, the dataUpdated
event will once again be dispatched.
You can use the
nextPage
property to determine if you've scrolled to the end of the list. ThenextPage
property initially returnsfalse
until the first collection query is finished.
Lastly, if there is a need to shrink the list of objects exported back to only the first 20 records (for example, if you pass the LiveCollection object to a new view), you can simply call resetPage()
.
Similar to model
property of the LiveObject, the LiveCollection provides models
property what is basically is an array of LiveObject's model
objects. models
is mutable and always contains same data as one what returned by dataUpdated
event.
Both LiveObject and LiveCollection can be subscribed to the dataError
event which is fired every time an error happens during the data update process. In other words, every time the LiveObject or LiveCollection fails to get data from the server - this error will be emitted.
We recommend you to always call dispose()
whenever you are done working with any LiveObject/LiveCollection.
Dispose is a very important functionality of the LiveObject. It allows you to avoid memory leaks and keeps your application performant. What does dispose()
do:
unsubscribe all listeners attached to the LiveObject instance;
stop all internall observers related to the LiveObject instance;
clean up an internall buffer of the LiveObject instance;
After you call dispose()
on a LiveObject instance, dataStatus and loadingStatus switch to Error
.
In iOS AmitySDK, we have a concept of Live Object and Live Collection. LiveObject is represented by AmityObject
instance and LiveCollection is represented by an instance of AmityCollection
. These are generic classes which encapsulates any other object and notifies the observer whenever any property of the encapsulated object changes.
Live Object helps to observe changes in a single object whereas Live Collection helps to observe changes in a list of objects. For example: AmityObject<AmityPost>
or AmityCollection<AmityMessage>
.
SDK handles a lot of data received from various sources. Data can be present in local cache. It might also be queried from the server or received from some real-time events. This means, some data is constantly updating. The data that you are accessing at the moment can get updated by other sources and becomes out of sync.
Live Object and Live Collection helps in syncing these constantly updating data, so you will always get the most recent one. Whenever the data gets updated, you will be notified through helper methods in live object and live collection classes.
New data gets automatically collected everytime when there is an updation and user need not refresh to get the recent data.
Live collection is available for the following functionalities in user/community feeds:
Post Collection
Comment Collection
Reactions Collection
Followers/Following Collection
Channel Collection
Message Collection
Channel Member Collection
Community Collection
Community Members Collection
Both AmityObject
and AmityCollection
provide methods that help to observe changes in objects. The life cycle of observation is tied to its token. As soon as the token is invalidated or deallocated, observation ends.
AmityNotificationToken
is a simple object which keeps track of what is being observed. Each observation from Live Object or Live Collection is tied to its respective token. As soon as the token is invalidated or deallocated, observation ends. The token is declared within the scope of the class.
The token is used in combination with AmityObject
or AmityCollection
. We will explore it more in AmityObject and
AmityCollection
concepts.
AmityObject
is a generic class that keeps track of a single object. It is a live object. In iOS AmitySDK
, any object which is encapsulated by AmityObject
is a live object.
Examples:
AmityObject<AmityMessage>
AmityObject<AmityChannel>
AmityObject
class exposes the following methods:
observe
observeOnce
These methods help observe a live object. Whenever any property for observed object changes, the observer is triggered.
observe
method can be triggered multiple times throughout the lifetime of the application as long as its associated AmityNotificationToken
is retained in memory. observeOnce
method, on the other hand, can only be triggered once.
Both observe
and observeOnce
methods will be called from the main thread so you can perform any UI update-related tasks from within the observe block itself.
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 through the dataStatus
property of AmityObject
.
In parallel, a request is made to server to fetch the latest version of the data. Once the data is returned, the observe block will be triggered again.
Any future changes to that data from any sources can trigger observer.
Lifecycle: The life cycle of the observer is tied to its token. If the token is not retained, then the observer can get deallocated at any time and will not be called. Both observe
and observeOnce
block will be retained using token as shown below.
The AmityNotificationToken
provides a method called invalidate()
which can be used to invalidate the token anytime. As soon as you invalidate the token, your observation stops and observe block will no longer be triggered.
There are multiple ways to access data from AmityObject
. AmityObject
exposes the following properties:
dataStatus
: Indicates whether the data is fresh or local
loadingStatus
: Indicates if the data is being loaded from server or not
object
: The actual object that is being tracked or encapsulated by this AmityObject
Once you add an observer block, you can access both local or fresh data as shown below.
If you want to observe fresh object just once, you can check the data status and invalidate the token once you receive the fresh object.
For
observerOnce
method, if data is present locally, this observer will be triggered only once with that local data. If you are looking for fresh data, use theobserve
block and invalidate the token once fresh data is received as shown above.
If you only care about local data and do not want to observe anything, you can also access the object
property from AmityObject
directly.
While this is possible, we recommend accessing object from within theobserve
or observeOnce
block depending on your requirement.
AmityObject
can be tracked for their loading status by accessing the loadingStatus
property, which is of type AmityLoadingStatus
and it can have one of four possible values.
0 (notLoading): Indicates that the data is already fresh locally and does not need to be loaded.
1 (loading): Indicates that the client is currently loading the data from the server.
2 (loaded) - Indicates that the client has successfully loaded fresh data from the server and is up to date.
3 (error) - Indicates that the data is unable to load due from a specific error.
AmityCollection
is a generic class that keeps track of a collection of objects. It is a live collection. In iOS SDK, any object which is encapsulated by AmityCollection
class is a live collection.
Examples:
AmityCollection<AmityMessage>
AmityCollection<AmityChannel>
AmityCollection
exposes these methods:
observe
observeOnce
These methods help to observe a live collection. Whenever any property for any object within the collection changes, the observer is triggered.
observe
method can get triggered multiple times throughout the lifetime of the application as long as it's associated AmityNotificationToken
is retained in memory. observeOnce
, on the other hand, can only be triggered once.
Both observe
and observeOnce
method will be called from the main thread so you can perform any UI update related task within the observe block itself.
If the requested data collection is stored locally on the device, the block will be called immediately with the local version of the data. This can be verified through the dataStatus
property of AmityCollection
.
In parallel, a request is made to the server to fetch the latest version of the data. Once the data is returned, the observe block will be triggered again.
Any future changes to the data from any sources can trigger observer.
Lifecycle: The life cycle of the observer for AmityCollection is also tied to its token. If the token is not retained, the observer can get deallocated at any time and will not be called. So, both observe
and observeOnce
block should be retained. You can refer to the section in AmityObject
about retaining and invalidating a token.
Unlike most databases, AmityCollection
does not return all data in an array. Instead, data is 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 necessary.
AmityCollection
also exposes a count
property which determines the number of objects present in a collection.
With these two public interfaces, you can create a robust list UI for your use case. Similar to AmityObject
, AmityCollection
also exposes dataStatus
and loadingStatus
property.
Let's look at the typical flow when accessing a collection data.
If you want to observe only fresh or local collection, you can access it using thedataStatus
property and invalidate the token once you have accessed your desired collection data.
For
observerOnce
method, if data is present locally, this observer will be triggered only once with that local data. If you are looking for fresh data, useobserve
block and invalidate the token once fresh data is received as shown above.
Observer also provides you with the AmityCollectionChange
object which contains indexes of what is added, deleted, or modified in a collection. You can also refer to these properties to update the UI for the list.
While SDK provides liveCollection.object(at:)
to access a single item, you might often find the need to iterate through all objects in the collection. The SDK has a convenient way to do this using .allObjects()
.
Using the above method is the same with this logic:
AmityCollection
in SDK returns a maximum of 20 items per page. It has nextPage()
and previousPage()
method to fetch more data. It also exposes hasNext
and hasPrevious
property to check if next page or previous page is present.
Once next page is available, the same observe
block gets triggered and you can access the collection as shown above. If you want to shrink the collection to the original first page, you can do so by calling resetPage()
method on the same collection.
One typical usage of Live Collection is in UITableView
. Below is an example of fetching a collection and displaying it in a tableview.
AmityObject and AmityCollection are now observable object with its properties marked with @Published
annotation. Now you can use live object & live collection directly within your SwiftUI views.
Since AmityObject
& AmityCollection
are observable object, it can be used as an ObservedObject within SwiftUI views. We recommend to create small view which observes AmityCollection & AmityObject as ObservedObject as shown in code sample below.
Live Collection - SwiftUI
Since the properties are published, If you are using Combine Framework, you can also subscribe to changes on those properties.
Live Object - Combine
Live Collection - Combine
ℹ️ States of live collection & live object are published before snapshots so that you can compare the state from within subscriber.
#1: LiveCollection is not updated when used from inside of another observable class.
AmityCollection
& AmityObject
is an ObservableObject
. When this live collection or live object is embedded inside another Observable Object, SwiftUI cannot observe the changes in snapshot occurring within Live collection & Live object. As a result, there might be a situation where you see your view is not getting updated even when snapshot(s) are updated. This is a common problem with nested observable object in SwiftUI.
To solve this issue we recommend to create a specific view which observes AmityCollection
& AmityObject
as @ObservedObject
as shown in code example.
#2: Published property still returns old values.
Since the properties of AmityCollection & AmityObject are marked with @Published
annotation, the publishing of changes occurs in the property’s willSet
block, meaning that any subscribers will receive an update before the property is changed. This behaviour can easily lead to unexpected bugs.
For more details, please refer to https://developer.apple.com/documentation/combine/published
Live Objects are supported in the Android SDK with RxJava Data Streaming
The library is being used in Android development to achieve Live Object and Live Collection behavior.
It is a Java VM implementation of ReactiveX, a library for composing asynchronous and event-based programs by using observable sequences. The building blocks of RxJava are Observables and Subscribers. Observable is used for emitting items and Subscriber is used for consuming those items.
You can visit for more information.
SDK handles lots of data received from various sources. Data can be present in local cache. It might also be queried from the server or received from some real-time events. What this means is that same data is constantly updating. The data that you are accessing at the moment can get updated by other sources and becomes out of sync.
Rx3 Data Stream helps in syncing the data so you will always get the most recent one. Whenever the data updates, you will be notified through Flowable Objects and Flowable Collection.
New data gets automatically collected everytime when there is an updation and user need not refresh to get the recent data.
Live Collection is available for the following functionalities in user/community feeds via APIs:
For any specific errors that's handled in PagingData please visit the web page below to properly handle its errors
Post Collection
Comment Collection
Reactions Collection
Followers/Following Collection
Channel Collection
Message Collection
Channel Member Collection
Community Collection
Community Members Collection
To retrieve data from the RxStream, we need to subscribe to the Stream(Flowable/Single/Completable) by defining subscribing and observing threads.
In the RxJava3 framework we have these different types of objects that can be observed:
Flowable - emits a stream of elements
doOnNext
doOnError
Single - emits exactly one element
doOnSuccess
doOnError
Completable - emits a “complete” event, without emitting any data type, just a success/failure
doOnComplete
doOnError
To get channel data, below is a sample code.
The Amity Android SDK comes equipped with asynchronous and data stream capabilities, powered by RxJava3 by default. However, for those who prefer to use Kotlin's Coroutines, the SDK can be seamlessly integrated with the following Kotlin extension functions, allowing for easy interoperability between Amity Android SDK functions and Kotlin Coroutines functions.
By using the .await()
method, it enables the conversion of Completable
and Single<T>
functions of the Amity Android SDK into suspend
functions.
By using the .asFlow()
method, it enables the conversion of Flowable<T>
functions of the Amity Android SDK into Flow
functions.
Amity Android SDK seamlessly integrates with Jetpack Compose UI, allowing you to take full advantage of the modern UI toolkit provided by Jetpack Compose. You can effortlessly incorporate our SDK into your Jetpack Compose-based projects to enhance your app's social experience. This compatibility ensures that you can leverage the power of Jetpack Compose while benefiting from the features and capabilities our SDK provides.
In Jetpack Compose, integrating data from a `Flow<PagingData<T>>` source into your UI is made easy through the `collectAsLazyPagingItems()` function. This function allows you to seamlessly paginate and display items within your Composable functions.
To start using it, add compose paging dependency in your project app level build.gradle
file.
Then in your Composable functions, you can collect from flow and display data, and also can observe the load state.
By using collectAsState()
method, it can deliver asynchronous data updates to your Compose UI components.
Live Object | @Published | Remarks |
---|---|---|
Live Collection | @Published | Remarks |
---|---|---|
Data Stream uses all standard .
dataStatus
✅
-
loadingStatus
✅
-
snapshot
✅
New
error
✅
-
object
❌
Deprecated
dataStatus
✅
-
loadingStatus
✅
-
snapshots
✅
New
error
✅
New