Implementing a skill rating over time graph section in SUSI Skill CMS

In SUSI.AI skill ratings is an invaluable aspect which greatly helps the users to know which skills are performing better than the rest and are more popular than the others. A robust skill rating system for the skills was developed recently which allows the users to rate skills as per their experience and thus data like average rating, total number of ratings is available but there was no provision to see the rating history or how the skills rating has changed over time, this could be an important aspect for users or developers to know what changes to the skill made it less/more popular. An API is developed at the server to retrieve the ratings over time data, we can use these details to render attractive components for a better visual understanding of how the skill is performing and get statistics like how the skill’s rating has changed over time. About the API Endpoint : /cms/getRatingsOverTime.json Parameters model group language skill After consuming these params the API will return the number of times a skill is called along with the date on which it is called. We use that data as an input for the line chart component that we want to render.  Fetching data from the server and storing in the application state Make an AJAX call to the server to fetch the data from the URL which holds the server endpoint, on successfully receiving the data we do some formatting with the timestamp that comes along the data to make it more convenient to understand and then we call a saveRatingOverTime function which saves the data received from the server to the application state. let ratingOverTimeUrl = `${urls.API_URL}/cms/getRatingsOverTime.json`; skillUsageUrl = skillUsageUrl + '?model=' + modelValue + '&group=' + this.groupValue + '&language=' + this.languageValue + '&skill=' + this.name; // Fetch the skill ratings over time $.ajax({ url: ratingOverTimeUrl, dataType: 'json', crossDomain: true, success: function(data) { if (data.ratings_over_time) { const ratingData = data.ratings_over_time.map(item => { return { rating: item.rating, count: item.count, timestamp: parseDate(item.timestamp) .split(' ') .slice(2, 4) .join(' '), }; }); self.saveRatingOverTime(ratingData); } }, error: function(e) { console.log(e); self.saveRatingOverTime(); }, });   Save the skill usage details in the component state. // Save ratings over time data in the component state saveRatingOverTime = (ratings_over_time = []) => { this.setState({ ratings_over_time, }); };   Send the received data as props to the Skill Rating component and render it. <SkillUsageCard skill_usage={this.state.skill_usage} /> Implementing the UI Importing the packages for rendering the chart in the Skill Ratings component. import { XAxis, YAxis, Tooltip, LineChart, Line, Legend, ResponsiveContainer } from 'recharts';   Display a small heading for the section in the ratings card and Render a Responsive container component which will form a parent component for out Chart which will be rendered when the ratings over time data received in the props is not empty. <div className="sub-title" style={{ alignSelf: 'flex-start' }}> Rating over time </div> {this.props.ratings_over_time.length ? ( <div> <ResponsiveContainer height={300} width={ window.innerWidth < 660 ? this.state.width : this.state.width * 1.5 } debounce={1} > ... </ResponsiveContainer>…

Continue ReadingImplementing a skill rating over time graph section in SUSI Skill CMS

Integrate prettier with lint-staged and ESLint for consistent code style throughout the project

SUSI Skill CMS presently use ESLint to check for code linting errors, the ESLint rules are written in a separate .eslintrc file which lives at the project root. The project didn’t follow any best practices for react apps and the rules were weak therefore a lot of bad/unindented code was present in the project which takes a lot of time to fix manually, not to mention there was no mechanism to auto-format the code while committing. Also, code reviews take a lot of time to discuss code styles and fixing them. Prettier comes to the rescue as it’s a code formatter which provides a ton of options to achieve the desired well-formatted code. Prettier enforces a consistent code style across your entire codebase because it disregards the original styling by parsing it away and re-printing the parsed code with its own rules Add prettier as a development dependency npm install prettier --save-dev --save-exact   Similar to how we write ESLint rules in a separate .eslintrc file, we have a .prettierrc file which contains rules for prettier but since we already have ESLint so we run prettier on top of ESLint to leverage functionalities of both packages, this is achieved by using eslint-plugin-prettier and eslint-config-prettier which exist for ESLint. These packages are saved as devDependencies in the project and “prettier” as a plugin is added to .eslintrc file and recommended prettier rules are extended by adding “prettier” in .eslintrc file. Install the config and plugin packages npm i eslint-plugin-prettier eslint-config-prettier --save-dev   To run prettier using ESLint, add the prettier to ESLint plugins and add prettier errors to ESLint rules. // .eslintrc { "plugins": ["prettier"], "rules": { "prettier/prettier": "error" } }   Extending Prettier rules in ESLint // .eslintrc { ... "extends": ["prettier"] ... }   5856 linting errors found which were undetected initially (SUSI Skill CMS). Add a .prettierrc file with some basic formatting rules for now like enabling single quotes wherever applicable and to have trailing comma at the end of JSON objects. // .prettierrc { "singleQuote": true, "trailingComma": "all", "parser": "flow" }   Add a format script in package.json so that user can format the code whenever required manually too. // package.json "scripts": { ... "format": "prettier --write \"src/**/*.js\"", ... },   Since we want to prevent contributors from committing unindented code we used lint-staged, a package which runs tasks on a set of specified files. We achieve this by adding a set of tasks in the lint-staged object and then run lint-staged as a pre-commit hook using husky. Install lint-staged as a development dependency which will be visible in package.json file. npm i lint-staged --save-dev   Add tasks for lint-staged as an object in package.json, we add a lint-staged object in the pacage.json file and grab all .js files in the project and run eslint, prettier over them and then we finally run git add to stage the changes done by prettier. // package.json "lint-staged": { "src/**/*.js": [ "eslint src --ext .js", "prettier --write", "git add" ] }   Call lint-staged…

Continue ReadingIntegrate prettier with lint-staged and ESLint for consistent code style throughout the project

Display Skill Usage of the Past Week in a Line Chart

Skill usage statistics in SUSI.AI is an important aspect which greatly helps the skill developers know what is more engaging for the users and the users know which skills are more popular than the others and are being used widely. So data like this should be interactively available on the clients. An API is developed at the server to retrieve the skill usage details, we can use these details to render attractive components for a better visual understanding of how the skill is performing and get statistics like on which days the skill has been active in particular.   About the API Endpoint : /cms/getSkillUsage.json Parameters model group language skill After consuming these params the API will return the number of times a skill is called along with the date on which it is called. We use that data as an input for the line chart component that we want to render. Creating a Skill Usage card component Import the required packages from corresponding libraries. We are using recharts as the library for the charts support and thus we import several components required for the line chart at the top of the Skill Usage component. // Packages import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { LineChart, Line, XAxis, YAxis, Tooltip, Legend } from 'recharts'; import { Paper } from 'material-ui';   Create a SkillUsageCard component which is enclosed in a Paper component from material ui library to give it a card like look and then we render a Line chart inside it using appropriate data props which are received from the skill page component, we set a height and a width for the chart, Add an X-Axis, Y-Axis, Tooltip which shows up on hovering over points on the graph, legends to describe the line on the chart and then finally a line with several styling and other props. class SkillUsageCard extends Component { render() { return( <Paper className="margin-b-md margin-t-md"> <h1 className='title'> Skill Usage </h1> <div className="skill-usage-graph"> <LineChart width={600} height={300} data={this.props.skill_usage} margin={{top: 5, right: 30, left: 20, bottom: 5}}> <XAxis dataKey="date" padding={{right: 20}} /> <YAxis/> <Tooltip wrapperStyle={{height: '60px'}}/> <Legend /> <Line name='Skill usage count' type="monotone" dataKey="count" stroke="#82ca9d" activeDot={{r: 8}}/> </LineChart> </div> </Paper> ) } }   Add prop validation at the end of the file to validate the coming props from the skill page component to validate that correct props are being received. SkillUsageCard.propTypes = { skill_usage: PropTypes.array }   Export the component so it can be used in other components. export default SkillUsageCard;   Fetch the data for the component from the API in the skill page component where the skill usage component will be rendered. First set the API url and then make an AJAX call to that URL and once the data is received from the server pass that received data to a saveSkillUsage function which does the simple task of saving the data to the state and passing the saved data as a prop to the skill usage component. In case the call fails…

Continue ReadingDisplay Skill Usage of the Past Week in a Line Chart

Rendering a Uniform StaticAppBar Component across all SUSI Web Clients on all Routes.

The Problem - We have three SUSI Web Clients namely Webchat Skills CMS Accounts And it’s important to keep the design guidelines in sync across all the clients, StaticAppBar is a component which forms the header of all the pages and thus it is important to keep it uniform in all clients which was clearly missing before. There is also a lot of code duplication of the AppBar component (in accounts app) since it is used on all the pages so our approach will be to prepare a single component and render it on all routes. Tackling the problem - Since the StaticAppBar component is present on all the clients we simply make the menu items uniform across all the clients and apply a check on those menu items on which are a premium feature or should appear only once the user is logged in. Building blocks of the StaticAppBar component AppBar SUSI logo on the left end Drop down hamburger menu on the right Here’s how the JSX for the StaticAppBar component in CMS looks like, it uses an AppBar component from the material-ui library and has several props and styling as per the requirement. <AppBar className='topAppBar' id='appBar' title={<div id='rightIconButton' ><Link to='/' style={{ float: 'left', marginTop: '-10px',height:'25px',width:'122px' }}> <img src={susiWhite} alt='susi-logo' className='siteTitle' /></Link></div>} style={{ backgroundColor: colors.header, height: '46px', boxShadow: 'none', margin: '0 auto', }} iconStyleRight={{ marginTop: '-2px' }} iconElementRight={<TopRightMenu />} />   TopRightMenu is a function that returns JSX for the hamburger dropdown and is rendered in the AppBar as depicted below. It is a conditional menubar meaning some menu items are only rendered when the user is logged in and thus this helps cover those features which should only be available to logged in users. After that we use a popover component which shows up when the 3 dots or the expander on the top right is clicked. Almost all of the components in material ui has a style prop so styling is easy for the components moreover the workflow for the popover click goes like once the expander is clicked a boolean state variable named showOptions is toggled which in turn toggles the opening or closing state of the Popover as per the open prop. let TopRightMenu = (props) => ( <div onScroll={this.handleScroll}> <div> {cookies.get('loggedIn') ? (<label style={{color: 'white', fontSize: '16px', verticalAlign:'super'}}> {cookies.get('emailId')} </label>) : (<label> </label>) } <IconMenu {...props} iconButtonElement={ <IconButton iconStyle={{ fill: 'white' }}><MoreVertIcon /></IconButton> } targetOrigin={{ horizontal: 'right', vertical: 'top' }} anchorOrigin={{ horizontal: 'right', vertical: 'top' }} onTouchTap={this.showOptions} > </IconMenu> <Popover {...props} animated={false} style={{ float: 'right', position: 'relative', marginTop: '46px', marginLeft: leftGap }} open={this.state.showOptions} anchorEl={this.state.anchorEl} anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }} targetOrigin={{ horizontal: 'right', vertical: 'top' }} onRequestClose={this.closeOptions} > <TopRightMenuItems /> {cookies.get('loggedIn') ? (<MenuItem primaryText='Botbuilder' containerElement={<Link to='/botbuilder' />} rightIcon={<Extension />} />) : null } <MenuItem primaryText='Settings' containerElement={<Link to='/settings' />} rightIcon={<Settings />} /> {cookies.get('loggedIn') ? (<MenuItem primaryText='Logout' containerElement={<Link to='/logout' />} rightIcon={<Exit />} />) : (<MenuItem primaryText='Login' onTouchTap={this.handleLogin} rightIcon={<LoginIcon />} />) } </Popover> </div> </div> ); Handling the conditional display of menu items based on the…

Continue ReadingRendering a Uniform StaticAppBar Component across all SUSI Web Clients on all Routes.

Appending a rating section of SUSI SKILL CMS to the skill page

Ratings is an essential component of skills which provides the developers an insight into how the SUSI Skill is functioning and how to further improve it which ultimately leads to great user experience so this was the motivation to allow users to be able to rate skills, once the rating system is implemented we need to show some statistics like average rating, total users who have rated the skills etc on the skill page for each skill, and this will also enable users to get top rated skills and thus users can get to use the best skills rated by the community. So we implemented a rating section to SUSI SKILL CMS Implementation Server - Two APIs were implemented by the analytics team on the server which allows the user to rate skill and fetch rating for each skill. To rate the skill (Sample) /cms/getSkillRating.json?model=general&group=Knowledge&language=en&skill=aboutsusi&callback=pc&_=1525446551181   To get the ratings data for any skill (Sample) /cms/fiveStarRateSkill.json?model=general&group=Knowledge&language=en&skill=aboutsusi&stars=3&callback=p&_=1526813916145   CMS - When visiting any skill make an ajax call to the server to fetch the skill data for the visited skill. The call takes in the URL from which we have to fetch data from and of course a datatype which is jsonp since server returns data in the JSON format, when the request succeeds we save the received rating to the application state and in the case or any errors we log the error for developers to debug. // Fetch ratings for the visited skill $.ajax({ url: skillRatingUrl, jsonpCallback: 'pc', dataType: 'jsonp', jsonp: 'callback', crossDomain: true, success: function (data) { self.saveSkillRatings(data.skill_rating) }, error: function(e) { console.log(e); } }); Save the fetched data to the application state, this data saved in the state will be used in several components and graph present on the ratings section. saveSkillRatings = (skill_ratings) => { this.setState({ skill_ratings: data }) } Plug in the data received to the Bar chart component to visualize how ratings are divide. Import the required components on the top of the file from the recharts library which provides us with several interactive charts. import {BarChart, Cell, LabelList, Bar, XAxis, YAxis, Tooltip} from 'recharts'; Plug the data to the BarChart component through the data prop and render them to the page, this data is coming from the application state which we saved earlier. After that we define keys and styling for the X-Axis and Y-Axis and an interactive tooltip which shows up on hovering over any bar of that chart. We have 5 bars on the chart for each star rating all of different and unique colors and labels which appear on the right of each bar. <div className="rating-bar-chart"> <BarChart layout='vertical' width={400} height={250} data={this.state.skill_ratings}> <XAxis type="number" padding={{right: 20}} /> <YAxis dataKey="name" type="category"/> <Tooltip wrapperStyle={{height: '60px'}} /> <Bar name="Skill Rating" dataKey="value" fill="#8884d8"> <LabelList dataKey="value" position="right" /> { this.state.skill_ratings .map((entry, index) => <Cell key={index} fill={ ['#0088FE', '#00C49F', '#FFBB28', '#FF8042', '#FF2323'][index % 5] }/>) } </Bar> </BarChart> </div> Display the average rating of the skill along with the stars Import the stars component from the react-ratings-declarative component.…

Continue ReadingAppending a rating section of SUSI SKILL CMS to the skill page

Multiple Languages Filter in SUSI.AI Skills CMS and Server

There are numerous users of SUSI.AI globally. Most of the users use skills in English languages while some prefer their native languages. Also,there are some users who want SUSI skills of multiple languages. So the process of fetching skills from multiple languages has been explained in this blog. Server side implementation The language parameter in ListSkillService.java is modified to accept a string that contains the required languages separated by a comma. Then this parameter is split by comma symbol which returns an array of the required languages. String language_list = call.get("language", "en"); String[] language_names = language_list.split(","); Then simple loop over this array language by language and keep adding the the skills’ metadata, in that language into the response object. for (String language_name : language_names) { // fetch the skills in this language. } CMS side implementation Convert the state variable languageValue, in BrowseSkill.js, from strings to an array so that multiple languages can be kept in it. languageValue: ['en'] Change the language dropdown menu to allow selection of multiple values and attach an onChange listener to it. Its value is the same as that of state variable languageValue and its content is filled by calling a function languageMenuItems(). <SelectField multiple={true} hintText="Languages" value={languageValue} onChange={this.handleLanguageChange} > {this.languageMenuItems(languageValue)} </SelectField> The languageMenuItems() function gets the list of checked languages as a parameter. The whole list of languages are stored in a global variable called languages. So this function loops over the list of all the languages and check / uncheck them based on the values passed in the argument. It build a menu item for each language and put the ISO6391 native name of that language into the menu item. languageMenuItems(values) { return languages.map(name => ( <MenuItem insetChildren={true} checked={values && values.indexOf(name) > -1} value={name} primaryText={ ISO6391.getNativeName(name) ? ISO6391.getNativeName(name) : 'Universal' } /> )); } While the language change handler gets the values of the selected languages in the form of an array, from the drop down menu. It simply assigns this value to the state variable languageValue and calls the loadCards() function to load the skills based on the new filter. this.setState({ languageValue: values }, function() { this.loadCards(); });  References Material UI Docs - https://material-ui.com/api/select/

Continue ReadingMultiple Languages Filter in SUSI.AI Skills CMS and Server

Implementing Five Star Rating UI in SUSI iOS

Five-star rating system introduced in SUSI to rate skills. SUSI enable the user to rate skills between 1 to 5 star. The five-star rating system is the best way to get feedback from the user. It also helps the developer for further development. Ratings help to better understand individual preferences and present a more personalized user experience. The user feedback helps products understand whether or not the content is valuable and improve offerings over time. This can benefit products with and without sophisticated personalization. Let’s see how the five-star rating system is implemented in SUSI iOS. Average ratings displayed near the Try It button - It shows the average rating of a particular skill. Enable user to submit the rating of any skill between 1-star to 5-star. The only logged-in user can submit the ratings for skills. Rating chart that display number of rating for each star (1 to 5), the right labels of chart bars shows the number of users rated for a particular star with the percentage. Average and total ratings for particular skills is also displayed near the bar chart. Thumbs-up and thumbs-down ratings removed from the skill detail screen and replaced with 5-star ratings. Implementation of Rating Chart For the rating chart, we are using TEAChart class, which enable us to present rating data on bar charts. Setting colors for bar chart: We are using Google's Material Design color for rating bars colors. let barChartColors = [ UIColor.fiveStarRating(), UIColor.fourStarRating(), UIColor.threeStarRating(), UIColor.twoStarRating(), UIColor.oneStarRating() ] Assigning colors to bars: barChartView.barColors = barChartColors Assign Data to the bars: // Sample data barChartView.data = [5, 1, 1, 1, 2] Set background color and bar spacing: barChartView.barSpacing = 3 barChartView.backgroundColor = UIColor.barBackgroundColor() Final Output - Resources - Material Design: https://material.io/design/ SUSI iOS Link: https://github.com/fossasia/susi_iOS

Continue ReadingImplementing Five Star Rating UI in SUSI iOS

Making Tree View in SUSI botbuilder

SUSI botbuilder enables you to create your own bot which you can integrate in your own website. While creating the skill for the bot, you often need to visualize the dialog flow. The Tree View helps you to visualize the conversation flow. The queries from the user and the related responses from the bot can easily be understood by seeing the tree. Installing package We will be using react-orgchart to make the tree. React-orgchart offers minimum basic implementation of the tree and we can easily customise the nodes. To install the package, type the following command in your terminal: npm i react-orgchart --save   Using the Orgchart In the TreeView.js file, we import the orgchart and render it. The tree data and node component is passed to the OrgChart component. Tree data stores the format and conversations in the tree and the node component represents the rendering of the individual node. We can customise the node according to our needs. const MyNodeComponent = ({node}) => { return ( 'initechNode'>{node.type==='bot' && '#4285f5' style={styles.icon}/>}{node.type==='user' && } { node.name } ); }; <OrgChart tree={this.props.treeData} NodeComponent={MyNodeComponent} />   Generating the Tree Data The data for the tree is generated by the code view. As user adds or edit skills in the code view, the skill language is interpreted and converted to a tree data structure. The generateTreeData() function in Build.js file does this job. //generation of skills from the code view for (let i = 0; i < lines.length; i++) { let bot_response = null; let line = lines[i]; if (line && !line.startsWith('::') && !line.startsWith('!') && !line.startsWith('#')) { let user_query = line; while (true) { i++; if (i >= lines.length) { break; } line = lines[i]; if (line && !line.startsWith('::') && !line.startsWith('!') && !line.startsWith('#')) { bot_response = lines[i]; break; } } let obj = { user_query, bot_response, }; skills.push(obj); } }   Thus, we are separating the skills in the code view and separating the bot and user response. A sample Tree data looks like: const treeData = { name: 'Welcome!', children: [ { name: 'User query 1', type: 'user', children: [ { name: 'Answer for the user query', type: 'bot', } ] }, { name: 'User query 2', type: 'user', children: [ { name: 'Answer for the user query', type: 'bot', } ] }, { name: 'User query 3', type: 'user', children: [ { name: 'Answer for the user query', type: 'bot', } ] } ] }   Result: Resources React-orgchart: https://www.npmjs.com/package/react-orgchart Passing functions to components: https://reactjs.org/docs/faq-functions.html Using props in react: https://reactjs.org/docs/render-props.html

Continue ReadingMaking Tree View in SUSI botbuilder

Create custom theme for SUSI chatbot web plugin

SUSI web plugin bot provides the features of SUSI as a chat window which users can plugin to their websites. They just need to copy the generated javascript code into their website and the SUSI bot plugin will be enabled. The user can customize the theme of the chatbot. This blog explains how the feature of applying custom theme is implemented. Accepting user’s input The react component of our concern is BotBuilderPages/Design.js. We use material-ui-color-picker component to accept user’s choice of color. <ColorPicker className='color-picker' style={{display:'inline-block',float:'left'}} name='color' defaultValue={ this.state[component.component] } onChange={(color)=> this.handleChangeColor(component.component,color) } />   Similarly, there is a text field which accepts the url of an image. The values are stored in the component’s state variables. Storing settings in server The design settings are stored in the text format: This forms part of the skill’s text file. When the user clicks on “save and deploy” button, the complete skill gets send to the server through the following API: let settings = { async: true, crossDomain: true, url: urls.API_URL + '/cms/' + (this.state.updateSkillNow ? 'modifySkill.json' : 'createSkill.json'), method: 'POST', processData: false, contentType: false, mimeType: 'multipart/form-data', data: form, }; $.ajax(settings) .done(function(response) { // successfully saved in server });   In the server, the skill is saved in susi_private_skill_data directory. Also, the design and configuration settings are stored in chatbot.json file. Applying the settings to the bot Now, in the susi-chatbot.js file, the custom theme settings are applied to the bot. The function getTheme() fetches the theme settings from the server via an ajax request. Then the function applyTheme() is executed which applies the theme to the chatbot. $(".susi-sheet-content-container").css("background-color",botbuilderBackgroundBody); if(botbuilderBodyBackgroundImg){ $(".susi-sheet-content-container").css("background-image","url("+botbuilderBodyBackgroundImg+")"); }   Similarly, other theme variables are applied as well. Thus we have customised the theme of the SUSI chatbot plugin. Result: Resources SUSI Botbuilder page: https://skills.susi.ai/botbuilder Material UI color picker: https://www.npmjs.com/package/material-ui-color-picker Event handlers in JavaScript: https://en.wikibooks.org/wiki/JavaScript/Event_handling About React setState: https://reactjs.org/docs/state-and-lifecycle.html  

Continue ReadingCreate custom theme for SUSI chatbot web plugin

Adding Support for Displaying Images in SUSI iOS

SUSI provided exciting features in the chat screen. The user can ask a different type of queries and SUSI respond according to the user query. If the user wants to get news, the user can just ask news and SUSI respond with the news. Like news, SUSI can respond to so many queries. Even user can ask SUSI for the image of anything. In response, SUSI displays the image that the user asked. Exciting? Let’s see how displaying images in the chat screen of SUSI iOS is implemented. Getting image URL from server side - When we ask susi for the image of anything, it returns the URL of image source in response with answer action type. We get the URL of the image in the expression key of actions object as below: actions: [ { language: "en", type: "answer", expression: "https://pixabay.com/get/e835b60f2cf6033ed1584d05fb1d4790e076e7d610ac104496f1c279a0e8b0ba_640.jpg" } ]   Implementation in App - In the app, we check if the response coming from server is image URL or not by following two methods. One - Check if the expression is a valid URL: func isValidURL() -> Bool { if let url = URL(string: self) { return UIApplication.shared.canOpenURL(url) } return false } The method above return boolean value if the expression is valid or not. Two - Check if the expression contains image source in URL: func isImage() -> Bool { let imageFormats = ["jpg", "jpeg", "png", "gif"] if let ext = self.getExtension() { return imageFormats.contains(ext) } return false } The method above returns the boolean value if the URL string is image source of not by checking its extension. If both the methods return true, the expression is a valid URL and contain image source, then we consider it as an Image and proceed further. In collectionView of the chat screen, we register ImageCell and present the cell if the response is the image as below. Registering the Cell - register(_:forCellWithReuseIdentifier:) method registers a class for use in creating new collection view cells. collectionView?.register(ImageCell.self, forCellWithReuseIdentifier: ControllerConstants.imageCell) Presenting the Cell using cellForItemAt method of UICollectionView - The implementation of cellForItemAt method is responsible for creating, configuring, and returning the appropriate cell for the given item. We do this by calling the dequeueReusableCell(withReuseIdentifier:for:) method of the collection view and passing the reuse identifier that corresponds to the cell type we want. That method always returns a valid cell object. Upon receiving the cell, we set any properties that correspond to the data of the corresponding item, perform any additional needed configuration, and return the cell. if let expression = message.answerData?.expression, expression.isValidURL(), expression.isImage() { if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ControllerConstants.imageCell, for: indexPath) as? ImageCell { cell.message = message return cell } } In ImageCell, we present a UIImageView that display the image. When cell the message var, it call downloadImage method to catch and display image from server URL using Kingfisher method. In method below - Get image URL string and check if it is image URL Convert image string to image URL Set image to the imageView func downloadImage() { if…

Continue ReadingAdding Support for Displaying Images in SUSI iOS