How to build Experiences into your products using the new Monterosa / Interaction SDK v2
Please contact our sales team if you're interested in access to the Monterosa / Interaction SDK v2.
If you want to move quickly and integrate our pre-existing Experiences into your existing app or site, we recommend starting with Embed Experiences using the SDK.
Monterosa / Interaction SDK offers you more control of the Experience, being able to create your own interactive Elements, achieve further customisation of the UI, and creating an Experience that better fits your product so that you can better engage your users. This is achieved by providing access to the information flowing through our platform using InteractKit, allowing you to rapidly build your own Experience, while leveraging the scalability, speed and content management capabilities of the Monterosa / Interaction Cloud.
Use Cases include:
Customise an existing Experience with your own UI –create your own look and feel for votes, quizzes and predictions and more and add these to your existing native or web apps. For example creating a custom layout for a Player Rater.
Create your own Experience from the start –If you have a voting, quiz or prediction concept that isn't an existing Experience you can create it using the SDK to create a new application. For example creating a new predictor for a sport we don't support yet.
Create your own new experience and use it as a template –If you want to build a web experience and provide this experience to all users within you organisation, you can build it in the Javascript SDK and store this in your Experiences tab for all your teams to re-use whenever they wish. Monterosa internal teams and development partners use this to help them support their customers at scale.
This guide primarily focusses on customising existing experiences, with further guides coming soon that help you create a fresh Experience in the platform.
Core SDK concepts
Before starting, we recommend reviewing Core Concepts to understand how Monterosa / Interaction Cloud works in more detail.
From a development perspective, we have a hierarchy of concepts as follows:
Project: a Project matches the Studio Project, and allows you to access the data you enter there, becoming a configuration and management environment for your App.
Event: an Event is a representation of a live event in the real world, such as a football match, a concert, the airing of a TV-show, or even the month of June. It is characterised by its start and end times, and contains a set of Elements on the Timeline. It is "owned" by the Project it belongs to. Events are part of a Schedule, and can be manually started, scheduled or manipulated using the Control API for example via sports data feeds.
Element: an Element is a bi-directional interactive component that is triggered to appear at a certain time in the event. The Element will contain the required information needed to specify its appearance, its content, and the way it behaves, as well as offering answer functionality so you can gather responses from your audience. Elements also are used for non-interactive content like Articles, Videos, or Images, amongst many others.
App: the platform serves up interactive, real-time web and native applications known in the platform as simply Apps. Using Build you can create your own App, associating it with one or more Projects for management and deployment. Apps are defined using a collection of configuration files, known collectively as the App Spec. You can create and customise your own App Spec whilst building an Experience for it.
Experience: Experiences are Apps which have been made available within Studio for self-service deployment via the Experiences tab. If you are building a Javascript App then it can become an Experience available to your teams to deploy in the self-service Experiences workflow from Studio. Native mobile apps are not yet supported in the Experiences workflow, but you can still develop your own native mobile apps.
Fields: Projects, Events and Elements each have an associated set of fields. These are key-value pairs where the set of keys available to customise and the type of data they'll contain is configured in the App Spec, whilst the value is configured via Studio. Thanks to this configurability, you can create infinite number of distinct Projects, Events and Elements.
Once the initial setup is done, you will need to add InteractKit as a dependency on your codebase, as InteractKit is the library of our SDK that provides access to bi-directional data flows. You can add InteractKit as follows:
If you use Swift Package Manager, add the following GIT repository URL as a new package dependency in Xcode:
You can use the platform's dynamic configuration for application settings that are loaded at runtime. You can also subscribe to changes in these settings. These may include styling options, metadata or feature toggling. Within Studio, these settings are found in the Project > Setup > Experience tab and are specified in your App Spec.
In order to use this capability, you will need to load the Project and obtain data from its fields as follows:
funcloadProject() {// Interact will already be initialised at this stage Interact.defaultCore.getProject(completion: { [weak self] result inguardlet self = self else { return } do { self.display(project:try result.get()) } catch {// Treat the error } })}funcdisplay(project: Project) {// You can use project fields in your UI by fetching them like so:let id = project.idlet myField = project.fields["my_field"]}
funloadProject() { Core.default!!.interact.getProject { it.onSuccess {display(project = it) } it.onFailure {// Treat the error, `it` is a throwable } }}fundisplay(project: Project) {// You can use project fields in your UI by fetching them like so:val id = project.idval myField = project.fields["my_field"]}
You can be notified of any update on the project by using this snippet:
// Called whe the project fields are updatedconstunsubscribeOnProjectFieldsUpdated=onProjectFieldsUpdated( project, () => { console.log(project) });// Called when the project listings are updatedconstunsubscribeOnProjectListingsUpdated=onProjectListingsUpdated( project, () => { console.log(project) });// Called when an event is publishedconstunsubscribeOnEventPublished=onEventPublished( project, (event) => { console.log(event) });
classMyProjectUpdateDelegate:ProjectUpdateDelegate {funcdidPublishEvent(project: Project, event: Event) {// Code when an event is published }funcdidRemoveEvent(project: Project, event: Event) {// Called when an event is removed }funcdidUpdateProjectFields(project: Project) {// Called when the project fields change }}let myDelegate =MyProjectUpdateDelegate()project.add(listener: myDelegate)// When we no longer need to be notified about project changes.project.remove(listener: myDelegate)
classMyProjectUpdateListener : ProjectUpdateListener {overridefunonEventPublished(project: Project, event: Event) {// Code when an event is published }overridefunonEventRemoved(project: Project, event: Event) {// Called when an event is removed }overridefunonProjectFieldsUpdated(project: Project) {// Called when the project fields change }}val myListener =MyProjectUpdateListener()project.add(myListener)// When we no longer need to be notified about project changes.project.remove(myListener)
Localise your application
If you are supporting multiple languages, you may also want to specify which language to use in your application. You can do so through the Project using the following snippet:
Read through our localisation guide to learn how to setup multiple languages in Studio
Getting and displaying Events
In many cases, you'll want to display a list of events for your users to select one, or at the very least you'll want to fetch the data of a single event in order to contextualise the Elements the user is seeing.
While you may not always want to process the currently active Event, this is the most common approach to selecting which Event is the focal point. The snippet below illustrates how to retrieve the list of all available Events and identify one that is currently in the active state.
You can use the state of the Event to filter and locate the Event that suits your needs. There are three states an event can be in:
Active: When the Event is currently live
Upcoming: When the Event is available, but it's start date is in the future
Finished: When the Event has concluded
asyncfunctiondisplayActiveEvent() {try {// If left unspecified, the `getEvents` function would use// the project you setup in the SDK by calling `configure()`constevents=awaitgetEvents();constfirstActiveEvent=events.find(({ state }) => state ===EventState.Active);const {id,name,endAt, fields: { my_field: eventCustomField, }, } = firstActiveEvent;console.log(firstActiveEvent, eventCustomField); } catch (e) {console.error('Something went wrong!', e); }}
funcdisplayActiveEvent(inproject: Project) {// If left unspecified, the `getEvents` function would use // the project you setup in the SDK by calling `configure()` project.getEvents { result indo {let events =try result.get()// You can find an active event by checking it's stateguardlet firstActiveEvent = events.first(where: { $0.state == .active })else {return }// You can fetch some of its properties// And display them in your UI as you see fitlet id = firstActiveEvent.idlet myField = firstActiveEvent.fields["my field"]let eventName = firstActiveEvent.namelet eventFinishDate = firstActiveEvent.endAt } catch {// Treat the error } }}
fundisplayActiveEvent(project: Project) {// If left unspecified, the `getEvents` function would use // the project you setup in the SDK by calling `configure()` project.getEvents { it.onFailure {// Treat the error, `it` is a throwable } it.onSuccess {val firstActiveEvent = it.firstOrNull { it.state == EventState.ACTIVE }// You can fetch some of its properties// And display them in your UI as you see fitval id = firstActiveEvent?.idval myField = firstActiveEvent?.fields?.get("my field")val eventName = firstActiveEvent?.nameval eventFinishDate = firstActiveEvent?.endAt } }}
getEvents will return the list of events sorted so that the most recent event is first, and the oldest is last
Additionally, you can be notified of any updates to the Event using this snippet:
// Called when an element is published to an eventconstunsubscribeOnElementPublished=onElementPublished(event, (element) => { console.log(element) });// Called when an element is revoked from the eventconstunsubscribeOnElementRevoked=onElementRevoked( event, element => { console.log(element) });// Called when the event is updatedconstunsubscribeOnEventUpdated=onEventUpdated( event, () => { console.log(event) });// Called when the event state changesconstunsubscribeOnEventState=onEventState( event, (state) => { console.log(state) });
classMyEventUpdateDelegate:EventUpdateDelegate {funcdidReceiveUpdate(event: Event) {// Called when the data in the event changes }funcdidChangeState(event: Event) {// Called when the state of the event changes }funcdidPublishElement(event: Event, element: Element) {// Called when an element is published }funcdidRevokeElement(event: Event, element: Element) {// Called when an element is revoked }}let myDelegate =MyEventUpdateDelegate()event.add(listener: myDelegate)// When we no longer need to be notified about event changes.event.remove(listener: myDelegate)
classMyEventUpdateListener : EventUpdateListener {overridefunonEventUpdated(event: Event) {// Called when the data in the event changes }overridefunonEventStateChanged(event: Event) {// Called when the state of the event changes }overridefunonElementPublished(event: Event, element: Element) {// Called when an element is published }overridefunonElementRevoked(event: Event, element: Element) {// Called when an element is revoked }}val myListener =MyEventUpdateListener()event.add(myListener)// When we no longer need to be notified about event changes.event.remove(myListener)
Alternatively, you can also obtain the Event by its ID. This is useful if you want to attach a given known Event to specific information in your platform. For instance, you could create an Event for a given football match, and then link all the articles relevant to that match to the Event, so that you display match details, or interactive Elements relevant to that match within the article.
The following snippet showcases how to get an Event from an ID, which we'll assume in this case is being provided by your API.
import { getEvent } from"@monterosa-sdk/interact-kit";constevent=awaitgetEvent('<event-id>'); // can be null
let event = project.getEvent(byId: '<event-id>')// can be null
project.getEvent('<event-id>') { result -> result.onSuccess { event ->//event found } result.onFailure { //event not found }}
Displaying Elements
Within an Event, content creators will be publishing Elements – bi-directional content units that have certain properties and behaviours built in, as defined in the App Spec. In general, there's two types of Elements, interactive Elements, that allow your users to interact with the content by providing answers, and seeing how other people have responded, and non Interactive Elements.
For example, you can define a non interactive Element to be an information unit, like a blog post, a message, or a link to embed. Meanwhile you can define an interactive Element to behave like a poll, containing a question, a number of possible answer options and a duration.
An Event may include multiple Elements. For example if you create an Event for a soccer game, an Element might be a result prediction appearing before the game starts and another that triggers whenever a goal is scored.
Non Interactive Elements
We'll first start by showcasing how to use non interactive Elements in your application.
The snippet below retrieves the array of Elements already existing within the Event and illustrates how you can use its contentType to differentiate each Element type. In the example, we determine a single type of Element being received - goalScored.
asyncfunctiondisplayElements(event) {try {constelements=awaitgetElements(event);for (constelementof elements) {switch (element.contentType) {case'goalScored':displayGoalScored(element);break; } } } catch (e) {console.error('Something went wrong!', e); }}functiondisplayGoalScored(element) {// In this function we know the element is a goal scored, so we can // equally make some assumptions about what data will be available.// As you can see below, given the control you have over the App // Spec, you can provide via Studio as much data as you want to support,// in this example the goal scorer name, number, and the amount of goals// they scored today, so you can add some extra fanfare// in your UI when they score a hat trick!const { fields: {goalScorerName,goalScorerNumber,goalScorerGoalsToday, }, } = element// Draw the UI based on your UI framework.}
funcdisplayElements(inevent: Event) { event.getElements { [weak self] result inguardlet self = self else { return }do {let elements =try result.get() elements.forEach { element inswitch element.contentType {case"goalScored": self.displayGoalScored(element)default:// Element type not recognised.return } } } catch {// Treat the error } }}funcdisplayGoalScored(_element: Element) {// In this function we know the element is a goal scored, so we can // equally make some assumptions about what data will be available.// As you can see below, given the control you have over the App // Spec, you can provide via Studio as much data as you want to support,// in this example the goal scorer name, number, and the amount of goals// they scored today, so you can add some extra fanfare// in your UI when they score a hat trick!let goalScorerName = element.fields["goalScorerName"]let goalScorerNumber = element.fields["goalScorerNumber"]let goalScorerGoalsToday = element.fields["goalScorerGoalsToday"]// Draw the UI based on your UI framework.}
fundisplayElements(event: Event) { event.getElements { it.onFailure {// Treat the error, `it` is a throwable } it.onSuccess { it.forEach { element ->when (element.contentType) {"goalScored"->displayGoalScored(element)else-> {// element type not recognised } } } } }}fundisplayGoalScored(element: Element) {// In this function we know the element is a goal scored, so we can // equally make some assumptions about what data will be available.// As you can see below, given the control you have over the App // Spec, you can provide via Studio as much data as you want to support,// in this example the goal scorer name, number, and the amount of goals// they scored today, so you can add some extra fanfare// in your UI when they score a hat trick!val goalScorerName = element.fields["goalScorerName"]val goalScorerNumber = element.fields["goalScorerNumber"]val goalScorerGoalsToday = element.fields["goalScorerGoalsToday"]// Draw the UI based on your UI framework.}
getElements will return a list of elements sorted so that the most recent element is first, and the oldest is last.
Additionally, you can subscribe to updates to a given Element using the following snippet:
// Called when the results of an interactive element changeconstunsubscribeOnElementResults=onElementResults( element, (results) => { console.log(results) });// Called when the element is updatedconstunsubscribeOnElementUpdated=onElementUpdated( element, () => { console.log(element) });// Called when the state of the element is updatedconstunsubscribeOnElementStateChanged=onElementStateChanged( element, () => { console.log(element) });
classMyElementUpdateDelegate:ElementUpdateDelegate {funcdidChangeState(element: Element) {// Called when the element state is updated }funcdidReceiveResults(element: Element) {// Called when the results of the element are updated }funcdidReceiveUpdate(element: Element) {// Called when the element is updated }}let myElementUpdateDelegate =MyElementUpdateDelegate()element.add(listener: myElementUpdateDelegate)// When we no longer need to be notified about element changes.element.remove(listener: myElementUpdateDelegate)
classMyElementUpdateListener : ElementUpdateListener {overridefunonElementStateChanged(element: Element) {// Called when the element state is updated }overridefunonElementResults(element: Element) {// Called when the results of the element are updated }overridefunonElementUpdated(element: Element) {// Called when the element is updated }}val myListener =MyElementUpdateListener()element.add(myListener)// When we no longer need to be notified about element changes.element.remove(myListener)
Interactive Elements
Interactive Elements get fans involved and make your experiences more rewarding. They have the following properties, which extend non-interactive Elements:
State: open or closed state, where open means the user can still interact with the Element. The timespan where the user can vote can be obtained through the duration property.
Question and AnswerOptions: The Element usually will have a question associated with it for the user to answer by interacting with some potential answers. Through the App Spec you can specify what data is included in either the Question and AnswerOptions. This enables you to create rich questions and answers which could include videos, images, links to other content on your application, ...
Results: Results bring a sense of belonging into your users interaction, by letting your users know how their peers are answering a question. You will receive these values both as a total vote count as well as a percentage of the total votes.
The snippet below updates the previous one to add a new type of element, a poll:
funcdisplayElements(inevent: Event) { event.getElements { [weak self] result inguardlet self = self else { return }do {let elements =try result.get() elements.forEach { element inswitch element.contentType {case"poll": self.displayPoll(element)case"goalScored": self.displayGoalScored(element)default:// Element type not recognised.return } } } catch {// Treat the error } }}funcdisplayPoll(_element: Element) {let question = element.question!["text"]let questionImageURL = element.question!["imageURL"]let answers = element.answerOptions!.map { answerOption inreturn answerOption["text"]as!String }let pollCustomField = element.fields["poll custom field"]let state = element.state// Draw the poll}
fundisplayElements(event: Event) { event.getElements { it.onFailure {// Treat the error, `it` is a throwable } it.onSuccess { it.forEach { element ->when (element.contentType) {"poll"->displayPoll(element)"goalScored"->displayGoalScored(element)else-> {// element type not recognised } } } } }}fundisplayPoll(element: Element) {val question = element.question!!["text"]val questionImageURL = element.question!!["imageURL"]val answers = element.answerOptions!!.map { it["text"] }val pollCustomField = element.fields["poll custom field"]val state = element.state;// Draw the poll}
Answering an Interactive Element
Once you have displayed the answer options on screen, submit the user's answer using the answer()method.
In the snippet below we show how an answer can be submitted, alongside its error handling so you can contextualise to the user if anything goes wrong. We assume that the variable userAnswer contains the 0-based index of the answer option selected by the user.
functionsendAnswer(element, index) {try {// index should be e.g. 0 to the count of options available - 1answer(element, index);// Your answer was sent successfully } catch {if (e instanceofMonterosaError) {switch (e.code) {caseAnswerError.OptionIndexOutOfRange:// The index is outside the range of possible optionsbreak;caseAnswerError.AboveMaxVoteOptions:// You voted for more indexes than is allowedbreak;caseAnswerError.BelowMinVoteOptions:// You voted for fewer indexes than is allowedbreak;caseAnswerError.AboveMaxVotesPerUser:// You voted with a higher total value than is allowedbreak;caseAnswerError.AboveMaxVotesPerOption:// You voted with a higher value than is allowed in a single optionbreak;caseAnswerError.VotedOnNonInteractiveElement:// You tried to vote on a non interactive elementbreak;caseAnswerError.VotedOnClosedElement:// You voted on closed elementbreak;default:// Shouldn't occur, but be ready for it as more cases could be added in the future.break; } } else {// Shouldn't occur } }}
funcanswer(element: Element, withindex: Int) {do {try element.answer(with: index)// Your answer was sent successfully }catchlet err asElement.AnswerError where err == .optionIndexOutOfRange {// The index is outside the range of possible options }catchlet err asElement.AnswerError where err == .aboveMaxVoteOptions {// You voted for more indexes than is allowed }catchlet err asElement.AnswerError where err == .belowMinVoteOptions {// You voted for fewer indexes than is allowed }catchlet err asElement.AnswerError where err == .aboveMaxVotesPerUser {// You voted with a higher total value than is allowed }catchlet err asElement.AnswerError where err == .aboveMaxVotesPerOption {// You voted with a higher value than is allowed in a single option }catchlet err asElement.AnswerError where err == .votedOnNonInteractiveElement {// You tried to vote on a non interactive element }catchlet err asElement.AnswerError where err == .votedOnClosedElement {// You voted on closed element }catch {// Shouldn't occur }}
funanswer(element: Element, index: Int) {try { element.answer(index)// Your answer was sent successfully } catch (e: ElementVoteError){when (e.errorType) { ErrorType.OPTION_INDEX_OUT_OF_RANGE ->// The index is outside the range of possible options ErrorType.ABOVE_MAX_VOTE_OPTIONS ->// You voted for more indexes than is allowed ErrorType.BELOW_MIN_VOTE_OPTIONS ->// You voted for fewer indexes than is allowed ErrorType.ABOVE_MAX_VOTES_PER_USER ->// You voted with a higher total value than is allowed ErrorType.ABOVE_MAX_VOTES_PER_OPTION ->// You voted with a higher value than is allowed in a single option ErrorType.VOTED_ON_NON_INTERACTIVE_ELEMENT ->// You tried to vote on a non interactive element ErrorType.VOTED_ON_CLOSED_ELEMENT ->// You voted on closed elementelse-> {// Shouldn't happen but in case new errors // are added in the future. } } }}
Elements can be configured to allow for multiple answers through their App Spec using three values:
Maximum votes per user: This specifies how many votes a user can cast, either on the same option or on multiple options. E.g. a user can emit 5 votes for a given question, either all 5 on the same option, or spread through multiple options.
Maximum and Minimum options per vote: This specifies the minimum and maximum options a user must include in their vote. E.g. a user must pick at least 2 options and at most 3.
In some scenarios, you may wish to validate that a given answer is correct before attempting to submit it. For instance, this could be helpful if you want to highlight in red that the user's choice is not a valid response and accompany with some explanatory text on how to correct it.
To that effect, we offer you a method called validateAnswer() that lets you perform the same validations the answer() method performs, and reports any potential errors in the same manner.
// You can validate your answer at any point, so as to provide// feedback to your user about what went wrong:try {validateAnswer(element, index);} catch (e) {// The same error handling as was done when calling `answer`}
// You can validate your answer at any point, so as to provide// feedback to your user about what went wrong:do {try element.validateAnswer(index)}catch {// The same error handling as was done when calling `answer`}
try { element.validateAnswer(index)} catch (e: ElementVoteError) {// The same error handling as was done when calling `answer`}
Displaying the results of an Interactive Element
After the user selects their response to the Element, you may want to show them what other people are saying. The platform collects responses and periodically sends aggregated stats back to all connected clients. Here's how you receive those results:
functiondisplayPoll(element) {// ...const { results } = element;if (results ===null) {// We don't have yet results, so reflect the case on the UIreturn; }// We are able to show results to the userconstvoteCount=results.map((result) =>result.votes);constvotePercentage=results.map((result) =>result.percentage);}
funcdisplayPoll(_element: Element) {// ...let results = element.resultsiflet results = results {// We are able to show results to the userlet voteCount = results.map { $0.voteCount }let votePercentage = results.map { $0.votePercentage } } else {// We don't have yet results, so reflect the case on the UI }}
Displaying the correct option of an Interactive Element
You can also display to the user which of all the options is the correct option to select, which is a critical feature when building for instance a Quiz.
The following snippet showcases how we check if the user has already submitted an answer, and if so, we obtain the correct option. Notice that depending on how you configure your Element in the App Spec, the correct option may be revealed at different times, so it's possible that the user has answered, but the real answer is not yet received. This is common when building Prediction Elements.
if (element.userAnswer ===null) {return;}constcorrectOptionIndex=element.correctOption;// Highlight the correct option from your collection of options
guardlet answer = element.userAnswer else { returnnil }let correctOptionIndex = element.correctOption// Highlight the correct option from your collection of options
val correctOptionIndex =if(element.userAnswer ==null){null} else { element.correctOption }// Highlight the correct option from your collection of options
Reacting to live updates
Monterosa / Interaction Cloud is built with real-time interactivity at its core. Data changes are broadcast to connected users as soon as you publish content, or as values change.
To ensure your app displays constantly relevant data, we offer the possibility to register listeners that are notified when changes occur.
Reacting to Project updates
The following snippet demonstrates how you can react to changes in the Project. For examplea change in its configuration fields or if a new Event is created.
// Called whe the project fields are updatedconstunsubscribeOnProjectFieldsUpdated=onProjectFieldsUpdated( project, () => { console.log(project) });// Called when the project listings are updatedconstunsubscribeOnProjectListingsUpdated=onProjectListingsUpdated( project, () => { console.log(project) });// Called when an event is publishedconstunsubscribeOnEventPublished=onEventPublished( project, (event) => { console.log(event) });
classMyProjectUpdateDelegate:ProjectUpdateDelegate {funcdidPublishEvent(project: Project, event: Event) {// Code when an event is published }funcdidRemoveEvent(project: Project, event: Event) {// Called when an event is removed }funcdidUpdateProjectFields(project: Project) {// Called when the project fields change }}let myDelegate =MyProjectUpdateDelegate()project.add(listener: myDelegate)// When we no longer need to be notified about project changes.project.remove(listener: myDelegate)
classMyProjectUpdateListener : ProjectUpdateListener {overridefunonEventPublished(project: Project, event: Event) {// Code when an event is published }overridefunonEventRemoved(project: Project, event: Event) {// Called when an event is removed }overridefunonProjectFieldsUpdated(project: Project) {// Called when the project fields change }}val myListener =MyProjectUpdateListener()project.add(myListener)// When we no longer need to be notified about project changes.project.remove(myListener)
Reacting to Event updates
The following snippet demonstrates how you can react to changes in the Event, be it a change in their fields, state, or Elements being added or removed. For example, a new Poll being published during a live Event.
// Called when an element is published to an eventconstunsubscribeOnElementPublished=onElementPublished(event, (element) => { console.log(element) });// Called when an element is revoked from the eventconstunsubscribeOnElementRevoked=onElementRevoked( event, element => { console.log(element) });// Called when the event is updatedconstunsubscribeOnEventUpdated=onEventUpdated( event, () => { console.log(event) });// Called when the event state changesconstunsubscribeOnEventState=onEventState( event, (state) => { console.log(state) });
classMyEventUpdateDelegate:EventUpdateDelegate {funcdidReceiveUpdate(event: Event) {// Called when the data in the event changes }funcdidChangeState(event: Event) {// Called when the state of the event changes }funcdidPublishElement(event: Event, element: Element) {// Called when an element is published }funcdidRevokeElement(event: Event, element: Element) {// Called when an element is revoked }}let myDelegate =MyEventUpdateDelegate()event.add(listener: myDelegate)// When we no longer need to be notified about event changes.event.remove(listener: myDelegate)
classMyEventUpdateListener : EventUpdateListener {overridefunonEventUpdated(event: Event) {// Called when the data in the event changes }overridefunonEventStateChanged(event: Event) {// Called when the state of the event changes }overridefunonElementPublished(event: Event, element: Element) {// Called when an element is published }overridefunonElementRevoked(event: Event, element: Element) {// Called when an element is revoked }}val myListener =MyEventUpdateListener()event.add(myListener)// When we no longer need to be notified about event changes.event.remove(myListener)
React to Element updates
The following snippet demonstrates how you can be notified about changes in an Element. For example if the content creator changes a field after making a mistake.
// Called when the results of an interactive element changeconstunsubscribeOnElementResults=onElementResults( element, (results) => { console.log(results) });// Called when the element is updatedconstunsubscribeOnElementUpdated=onElementUpdated( element, () => { console.log(element) });// Called when the state of the element is updatedconstunsubscribeOnElementStateChanged=onElementStateChanged( element, () => { console.log(element) });
classMyElementUpdateDelegate:ElementUpdateDelegate {funcdidChangeState(element: Element) {// Called when the element state is updated }funcdidReceiveResults(element: Element) {// Called when the results of the element are updated }funcdidReceiveUpdate(element: Element) {// Called when the element is updated }}let myElementUpdateDelegate =MyElementUpdateDelegate()element.add(listener: myElementUpdateDelegate)// When we no longer need to be notified about element changes.element.remove(listener: myElementUpdateDelegate)
classMyElementUpdateListener : ElementUpdateListener {overridefunonElementStateChanged(element: Element) {// Called when the element state is updated }overridefunonElementResults(element: Element) {// Called when the results of the element are updated }overridefunonElementUpdated(element: Element) {// Called when the element is updated }}val myListener =MyElementUpdateListener()element.add(myListener)// When we no longer need to be notified about element changes.element.remove(myListener)