Over the past 15 years, since the introduction of the first iPhone, software development has undergone a significant transformation. With the increasing prevalence and expanding capabilities of smartphones, users are shifting their preference towards accessing software services on mobile devices instead of traditional desktops or laptops. This shift is driven by the unique features offered by smartphones, such as geolocation, biometric authentication, and motion sensing, many of which are only recently being adopted by desktop platforms. In certain demographics, smartphones and similar mobile devices have even become the primary means of software consumption, entirely replacing computers.
Companies have recognized and responded to this trend by prioritizing mobile app development. Applications across various domains, including financial brokerage (Robinhood), social media (Instagram), and ride-hailing (Uber), are embracing a mobile-first development approach. Desktop applications, if offered, often serve as secondary companions to the primary mobile app.
For full-stack developers, adapting to these evolving trends is essential. Fortunately, numerous mature and well-supported technologies are available to help web developers leverage their skills for mobile development. This article will delve into three such technologies: Cordova, Ionic, and React Native. React.js, a widely used framework for front-end web development, will serve as the core development technology. While the focus will be on developing an iPhone application, it’s important to note that these technologies are cross-platform and can be compiled for the Android platform as well.
Application Overview
The goal is to build an application that harnesses Natural Language Processing (NLP) to process and curate Twitter feeds. Users will be able to select a set of Twitter handles, retrieve their latest updates using a Twitter API, and categorize the tweets based on sentiment and topic. The application will then present the tweets to the user, allowing them to view them by sentiment or topic.
Back End
The back-end development will precede the front-end and will be kept intentionally simple. It will utilize basic, readily available sentiment analysis and part-of-speech tagging, complemented by some data cleaning to address dataset-specific issues. The open-source NLP library TextBlob will be employed, and the results will be served using Flask.
NLP Concepts: A Brief Introduction
For those unfamiliar with natural language analysis, terms like Sentiment Analysis and Part-of-speech tagging might seem foreign. NLP encompasses technologies that analyze and process human language data. While a broad field, there are common challenges. Human language, unlike programming or numerical data, is less structured and highly contextual. This, coupled with the inherent complexities of language, makes traditional data analysis techniques difficult to apply directly.
Sentiment Analysis focuses on deciphering the emotional tone of a text passage. Although human emotion is subjective, sentiment analysis has significant commercial potential. Applications include classifying product reviews, gauging the mood of emails or speeches, and categorizing song lyrics.
Part-of-speech tagging (POS tagging) aims to identify the grammatical role of words within a sentence. This is more challenging than it appears, as a word’s role can change based on context. Thankfully, sophisticated off-the-shelf models are now readily available in various programming languages.
Flask, TextBlob, and Tweepy
The NLP back end will utilize Flask to build a lightweight server, TextBlob for natural language processing, and Tweepy to interact with the Twitter API. Obtaining a developer key from Twitter is a prerequisite for retrieving tweets.
While more sophisticated back-end implementations with advanced NLP techniques are possible, simplicity will be prioritized for this project.
Back-end Code
Let’s begin coding the back end.
First, install the necessary packages:
| |
Next, let’s write the code for the core functionality.
Create a Python script named server.py and import the required libraries:
| |
Define some helper functions:
| |
With the helper functions in place, combine everything into a couple of functions:
| |
The download_analyze_tweets function can now be executed with a list of desired Twitter handles.
Running the following code:
| |
should produce results similar to:
| |
Now, construct a simple Flask server. In an empty file named server.py, add the following code:
| |
After starting the server, a POST request can be sent to it using an HTTP client. Passing {"accounts": ["@NASA", "@SpaceX"]} as a JSON argument should return data similar to the Twitter analysis code.
Due to networking nuances in phone emulators, deploying the API is recommended. Otherwise, when running on an emulator, requests should be directed to <Your Computer IP>:5000 instead of localhost:5000. Deploying the code allows for requests to a specific URL.
Numerous deployment options exist. For a straightforward, free debug server requiring minimal setup, PythonAnywhere is a good choice and should handle this server out of the box.
With the back-end server complete, let’s move on to the front end, starting with Cordova.
Apache Cordova Implementation
Cordova Overview
Apache Cordova enables web developers to target mobile platforms. Utilizing the web browser capabilities of smartphones, Cordova packages web application code into a native application container. However, Cordova is more than a simple web browser. Through its API, developers can access various smartphone-specific features, including offline support, location services, and device cameras.
For this application, React.js will be used as the JS framework and React-Bootstrap as the CSS framework. Bootstrap, being responsive, already supports smaller screens. Once the application is written, Cordova will be used to compile it into a web application.
App Configuration
The setup process for the Cordova React app is unique. As developer Shubham Patil explains in a Medium article, it involves setting up both a React development environment (using React CLI) and a Cordova development environment (using Cordova CLI), and then merging them.
Begin by executing the following commands in the code folder:
| |
Once the setup is complete, the contents of the React app’s public and src folders should be moved to the Cordova app. Next, in the package.json file, copy the scripts, browser list, and dependencies from the React project. Additionally, add "homepage": "./" to the root of package.json to ensure compatibility with Cordova.
Modify the public/index.html file to work with Cordova. Copy the meta tags from www/index.html and the script located at the end of the body tag (responsible for loading Cordova.js).
Next, modify src/index.js to detect if it’s running within Cordova. If so, the render code should be executed within the deviceready event handler. Otherwise, render immediately.
| |
Finally, set up the deployment pipeline by adding the following definition to the config.xml file:
<hook type="before_prepare" src="hooks/prebuild.js" />
Then, create the prebuild.js script:
| |
This script triggers the React build process, placing the build folder in the correct location before the Cordova build begins, automating the deployment.
Now, test the app:
| |
This should launch the React app in the browser. Add Cordova:
| |
The React app should now be running in the emulator.
Router and Package Setup
Install the required packages:
| |
Set up the routing and a simple global state object to be shared by all components. In a production application, a state management system like Redux or MobX would be preferable, but for simplicity, this project will keep it basic.
In App.js, configure the route as follows:
| |
This introduces two routes, Input and Display. The curatedTweets variable is passed to Display, while setCuratedTweets is passed to Input. This allows the input component to set the curatedTweets variable, which is then accessible to Display for rendering.
Create the component files. Under /src, create a folder named /src/components. Within /src/components, create another folder called /src/components/input and two files inside it: input.js and input.css. Repeat this for the Display component: create /src/components/display and add display.js and display.css.
Create stub components like so:
| |
And similarly for Display:
| |
With the wireframing complete, the app should run. Now, let’s focus on coding the Input page.
Input Page
Functionality Outline
The Input page should allow users to input and edit the Twitter handles they wish to pull data from. A mechanism for users to signal completion is also needed. Once signaled, the curated tweets should be fetched from the Python API, and the app should navigate to the Display component.
File Setup
Import the necessary React Router library component withRouter to enable navigation functionality, along with the required React Bootstrap Components:
| |
Define a stub function for Input. Input receives the setCuratedTweets function and needs the ability to navigate to the display route after setting the curated tweets. Therefore, it will take setCuratedTweets and history (for navigation) from the props.
| |
Wrap the component with withRouter in the export statement at the end of the file to provide access to the history API:
| |
Data Containers
Set up the data containers using React Hooks. Since the useState hook has already been imported, add the following code to the Input component’s body:
| |
This creates containers and modifiers for handles (storing the list of handles) and handleText (storing the content of the input textbox).
Next, let’s code the UI components.
UI Components
The UI will be simple. One Bootstrap row will contain the input textbox and two buttons: one to add the input content to the handles list and another to trigger the API call. Another Bootstrap row will display the list of handles using the Bootstrap list group. Here’s the code:
| |
In addition to the UI components, implement the three UI event handlers for data changes. The getPull event handler (responsible for the API call) will be implemented in the next section.
| |
Now, let’s implement the API call.
API Call
The API call involves taking the desired handles, sending them to the Python API in a POST request, and storing the resulting JSON data in the curatedTweets variable. If successful, the app should programmatically navigate to the /display route. Otherwise, log the error to the console for debugging.
Here’s the code:
| |
At this point, the app should be functional. Add a couple of handles and send a request to the API.
Now, let’s move on to coding the sentiment page.
Sentiment Sorted Mode
Because the Python API already sorts tweets by sentiment, the sentiment page is straightforward.
Functionality Outline
A list interface will display the tweets. Additionally, a couple of navigational components will be included: one to switch to topic grouping mode and another to return to the Input page.
Start by defining the SentimentDisplay sub-component within the display.js file.
SentimentDisplay Component
The SentimentDisplay component will receive the curatedTweets object and display the sentiment-sorted tweets in a list. React-Bootstrap simplifies this:
| |
Add some styling. In display.css, add the following and import it:
| |
Now, display the SentimentDisplay component. Modify the Display function as follows:
| |
Take this opportunity to code the navigational components. Two buttons are needed: “Back to edit” and “Topic Group Mode.”
Implement these buttons in a separate Bootstrap row above the SentimentDisplay component:
| |
Running the app and pulling tweets from a few handles should yield a visually appealing result.
Topic Grouping Mode
Now, implement the slightly more complex Topic Grouping Mode.
Functionality Outline
The goal is to display all noun phrases as an accordion list, rendering the tweets containing each noun phrase when expanded.
Implementing the Switch to Topic Grouping Mode
Begin by creating a stub component:
| |
Add logic to control which component is displayed. In the main Display component, add the following lines:
| |
Then, modify the JSX to incorporate this logic:
| |
The Topic Group Display stub should now appear when toggled.
The TopicDisplay Component
Code the TopicDisplay component, which will leverage the Bootstrap Accordion List. The implementation is fairly straightforward:
| |
Running the app should now display the Topic Display.
The app is now complete and ready to be built for the emulator.
Running the App in the Emulator
Cordova makes running the app in the emulator simple:
| |
The app should now appear in the emulator. Thanks to Bootstrap’s responsive design, the web app adapts to the iPhone’s width, resulting in a polished appearance.
With the Cordova app complete, let’s explore the Ionic implementation.
Ionic-React Implementation
Ionic Overview
Ionic is a web component library and CLI toolkit that simplifies hybrid application development. Initially built on AngularJS and Cordova, Ionic now offers React.js components and supports Capacitor, a platform similar to Cordova.
Ionic distinguishes itself by providing web components that closely resemble native mobile interfaces. These components automatically adapt their look and feel to the operating system, enhancing the native experience. Additionally, Ionic offers various build tools to streamline application deployment (though this is outside the scope of this article).
This implementation will utilize Ionic’s React components for the UI, leveraging some JavaScript logic from the Cordova section.
App Configuration
First, install the Ionic tools:
| |
Navigate to the project folder and use the Ionic CLI to create a new project folder:
| |
Enter the newly created folder:
| |
Run the blank app:
| |
The app is now set up. Before defining routes, note that Ionic initializes the project using TypeScript. While TypeScript is not strictly necessary, it offers some beneficial features and will be used in this implementation.
Router Setup
This implementation will use three routes: input, sentimentDisplay, and topicDisplay. This is to leverage Ionic’s transition and navigation features and because accordion lists are not readily available in Ionic’s component library. While custom implementations are possible, this tutorial will stick to the provided Ionic components.
The App.tsx file should already contain the basic route definitions.
Input Page
Functionality Outline
This implementation will largely mirror the Bootstrap implementation, with a few key differences. TypeScript will be used, bringing type annotations to the code. Ionic components will be used, offering a similar style to Bootstrap but with OS-sensitive styling. Navigation will be handled dynamically using the history API, but accessing it will differ slightly due to Ionic Router.
Setting Up
Create a stub input component. Create a folder named input under pages and a file named Input.tsx within it. Inside Input.tsx, define a React component:
| |
In App.tsx, modify the component to:
| |
Refreshing the app should now display the Input stub component.
Data Containers
Create data containers for inputted Twitter handles and the current input box content. Using TypeScript requires adding type annotations to the useState invocation:
| |
Create a data container to hold the API return values. Since this data needs to be shared across routes, define it in App.tsx. Import useState from React in App.tsx and modify the app container function as follows:
| |
An editor with syntax highlighting (e.g., Visual Studio Code) might flag CuratedTweets because its interface is undefined. Create a folder named interfaces under src and a file named CuratedTweets.tsx within it. Define the CuratedTweets interface as follows:
| |
The app now understands the API return data structure. Import the CuratedTweets interface in App.tsx. App.tsx should now compile without errors.
Pass the setCuratedTweets function to the Input component and make it aware of this function.
Modify the Input route in App.tsx as follows:
| |
The editor should now flag that Input is unaware of the new prop. Define it in Input.tsx.
Import the CuratedTweets interface and define the ContainerProps interface as follows:
| |
Finally, modify the Input component definition:
| |
With the data containers defined, move on to building the UI components.
UI Components
Create an input component and a list display component using Ionic’s containers.
Import the necessary library components:
| |
Replace the stub component with an IonInput wrapped in an IonGrid:
| |
Note that the event listener is onIonChange instead of onChange. Otherwise, it should look familiar.
In a browser, the app’s appearance might differ from the Bootstrap version. However, switching to emulator mode should make the UI clearer. It will look even better when deployed on a mobile device.
Add “Add to list” and “Pull API” buttons using IonButton. Change the input’s IonCol size to 8 and add the buttons with columns:
| |
While adding buttons, implement their event handlers.
The handler for adding a Twitter handle to the list is straightforward:
| |
Implement the API call in the next section. For now, add a stub function for onPullClicked:
| |
Create the component to display the list of inputted handles. Use IonList within a new IonRow:
| |
Each list item displays the handle and a delete button within its own IonGrid. Implement the deleteClickedHandler:
| |
Saving at this point should display the Input page with all UI components. Handles can be added, deleted, and the API call button should be functional.
Move the inline styles to CSS. Create a file named input.css in the input folder and import it in Input.tsx. Add the following styles:
| |
Add className="input-button" to all IonButtons and className="handle-display" to the IonCol displaying the intended Twitter handle. Saving should result in a visually appealing layout.
API Call
The API call logic remains similar, except for accessing the history component for dynamic route changes. This will be done using the withHistory hook.
Import the hook:
| |
Implement the handler in the input component:
| |
Adding a Header
Enhance the UI with a header using Ionic’s header feature. This provides a more natural user experience, especially on mobile devices, where it simulates the native OS’s header.
Modify the component import:
| |
Wrap the UI in an Ionic page with a header:
| |
The app should now have a more polished look.
Sentiment Sorted Page
Functionality Outline
The sentiment sorted page will resemble the Bootstrap version but will use TypeScript and Ionic components. The Topic Display will be implemented as a separate route to leverage Ionic’s navigation features on mobile. Therefore, this page needs the ability to navigate to the Topic Display route.
Route Setup
Create a new folder named sentimentsorted and a file named SentimentSorted.tsx within it. Export a stub component:
| |
In App.tsx, import the component:
| |
Add the route:
| |
A TypeScript error will occur because SentimentSorted doesn’t expect the curatedTweets props. Address this in the next section.
UI Components
Define the container’s props:
| |
Modify the stub display:
| |
Everything should now compile.
The display is straightforward, consisting of an IonList with display components:
| |
Saving and pulling tweets using the input component should display them in a list.
Add navigation buttons to the IonGrid:
| |
Implement switchToInput using the history API:
| |
Implement ToggleDisplayType:
| |
The SentimentDisplay component is now complete. Before implementing the Topic Display Page, create the component for displaying all topics.
Topic Groups Component
Add a topic list display option and conditionally display it. Break out the Sentiment Display list. Rename SentimentDisplay to Display and extract the sentiment display list:
| |
Note the use of a class definition from the CuratedTweets interface. These components only need a subset of the CuratedTweets object. The topic list is similar:
| |
Implement conditional display in the Display Component:
| |
Ensure to change the default export. Now, implement the Topic Display Page.
Topic Display Page
Functionality Outline
The topic display page resembles the sentiment display but filters tweets based on the topic specified in the route parameter.
Route Setup
Create a page folder named topicdisplay and a TopicDisplay.tsx file within it. Write a stub component and import it into App.tsx. Set up the routes:
| |
Implement the UI component.
UI Components
Define the ContainerProps definition:
| |
Retrieve the topic from the URL pathname using the history API. Import useHistory, instantiate the history API, and extract the topic from the pathname. Also, implement the switch back functionality:
| |
With the tweets for the specific topic available, displaying them is simple:
| |
Save and run. The app should be functional.
Running the App in the Emulator
Run a few Ionic commands to add the mobile platform and copy the code, similar to the Cordova setup:
| |
The app should now appear in the emulator.
React Native Implementation
React Native Overview
React Native takes a different approach compared to the web-based methods of Cordova and Ionic. It renders React code as native components, offering several advantages.
First, React Native integrates deeply with the underlying operating system, allowing developers to leverage new smartphone and OS-specific features not readily accessible through Cordova or Capacitor. Second, the absence of a web-based rendering engine generally results in faster performance compared to Cordova apps. Finally, the ability to integrate native components provides developers with finer-grained control over their applications.
This implementation will utilize logic from previous sections and employ NativeBase, a React Native component library, for UI development.
App Configuration
Install the necessary React Native components following the instructions here.
Once React Native is installed, initiate the project:
| |
Allow the setup script to run, creating the project folder. Navigate to the folder and run react-native run-ios to launch the example app in the emulator.
Install NativeBase, the component library:
| |
Install the React Native stack navigator:
| |
Link and install the native plugins:
| |
Router Setup
Use the installed stack navigator for routing.
Import the router components:
| |
Create a stack navigator:
| |
Modify the App component to use the stack navigator:
| |
An error will occur because Entry is undefined. Define a stub component to resolve this.
Create a components folder in the project, create a file named Entry.jsx within it, and add a stub component:
| |
Import the Entry component in the app. It should now build successfully.
Next, code the Input page.
Input Page
Functionality Outline
The implemented page will resemble previous versions but will utilize NativeBase components. Most JavaScript and React APIs, such as hooks and fetch, will remain the same.
The main difference lies in working with the navigation API.
UI Components
Import the necessary NativeBase components: Container, Content, Input, List, ListItem, and Button. These components have analogs in Ionic and Bootstrap React, and NativeBase’s design makes it intuitive for developers familiar with those libraries.
Import the components:
| |
Define the component:
| |
Implement the state and event handlers:
| |
Finally, implement the API call:
| |
This implementation mirrors the NativeBase version, except for navigation. The stack navigator provides a “navigation” prop to its components, enabling navigation between routes using .navigate. Data can also be passed to the target component using this mechanism.
Make Entry aware of the navigation component. Modify the component function declaration:
| |
Saving should now display the page.
Sentiment Sorted Page
Functionality Outline
The sentiment page will be implemented similarly to previous sections but with different styling and navigation library usage.
Since React Native lacks CSS, define a StyleSheet object or code styles inline. This implementation will create a global stylesheet to share styling across components.
The StackNavigator provides a built-in Back navigation button, eliminating the need for a custom implementation.
Route Setup
Route definition in StackNavigator is straightforward. Create a new Stack Screen and assign the component to it, similar to React Router.
| |
Create a stub component in components/SentimentDisplay.js:
| |
Import the component:
| |
Create the global stylesheet.
Global StyleSheet
Create a file named globalStyles.js. Import the StyleSheet component from React Native and define the styles:
| |
Now, code the UI.
UI Components
The UI component will look familiar, with the exception of how routes are handled. Use StackNavigator’s special props, navigation and route, to access the current application state and navigate to the topic display if the user chooses to view it.
Modify the component definition to access the navigation props:
| |
Implement application state reading and navigation functionality:
| |
Import the global styles:
| |
Import the components:
| |
Finally, add the component implementations:
| |
Save and test the app. The sentiment display should now be functional. Next, move on to the topic grouping page.
Topic Grouping Page
Functionality Outline
The Topic Display will use a handler builder to create a navigation function for navigating to the display page for a specific topic item. It will also define a page-specific stylesheet.
This section introduces TouchableOpacity, a React Native component that functions like a button.
Route Setup
Define the route:
| |
Create the stub component components/TopicDisplay.js:
| |
Import the component:
| |
UI Components
Import the necessary library functions:
| |
Import the global styles:
| |
Define custom styles:
| |
Define the navigation props:
| |
Define the data and action handlers, utilizing a handler builder (a function that returns another function):
| |
Finally, define the components, using TouchableOpacity with an onPress handler. TouchableTransparency could also be used, but TouchableOpacity’s click-and-hold animation is better suited for this application.
| |
The Topic Grouping Page should now be functional. Move on to the Topic Display Item Page.
Topic Display Item Page
Functionality Outline
The Topic Display Item Page will be straightforward, as all the intricacies have been covered in previous sections.
Route Setup
Add the route definition:
| |
Add the import statement:
| |
Create the stub component, this time importing the necessary NativeBase components and defining the route props:
| |
UI Components
The UI Component is simple, with no custom logic.
Implement the UI components:
| |
Save the changes. The app should now be fully functional.
Running the App
The app should already be running in the emulator from the previous steps.
Having your app running in the emulator is already taken care of since you’re using React Native. This convenience is a major advantage of React Native’s development environment.
With that, we’ve reached the end of the coding part of this article. Let’s recap what we learned about each technology.
Comparing the Technologies
Cordova: Pros and Cons
Cordova’s biggest strength is the remarkable speed at which a proficient web developer can build a functional and fairly polished app. Web development expertise translates well since you’re essentially coding a web app. The development process is straightforward and fast, and utilizing the Cordova API is intuitive and easy.
However, Cordova’s reliance on web components is its main drawback. Users anticipate a distinct user experience and interface design from mobile apps. When an application resembles a mobile website, the experience can be jarring. Furthermore, many standard app features, like transitions and navigation elements, require manual implementation.
Ionic: Pros and Cons
What impressed me most about Ionic was the abundance of mobile-centric features it provided out of the box. By using familiar web development patterns, I could create an app with a more native mobile feel compared to using Cordova and React-Bootstrap alone. Features like navigation animations, native-styled buttons, and a wide array of user interface elements greatly enhanced the user experience.
Ironically, Ionic’s strengths also contribute to its weaknesses. Firstly, anticipating the app’s behavior across different environments was occasionally tricky. A UI layout might appear differently across various platforms. Secondly, Ionic’s reliance on multiple underlying technologies made accessing some components challenging. Lastly, as a React developer, I found that Ionic-React, initially designed for Angular, had comparatively less documentation and support. However, the Ionic team is clearly attentive to React developers, delivering new features rapidly.
React Native: Pros and Cons
React Native provided a very smooth mobile development experience. Having a direct connection to the emulator eliminated uncertainty regarding the app’s appearance. Debugging was efficient due to the familiar web-based interface and the ability to leverage existing web development debugging techniques. The ecosystem also boasts a robust selection of tools and libraries.
React Native’s proximity to the native interface is its main disadvantage. Many DOM-based libraries are incompatible, necessitating the learning of new libraries and best practices. Styling, without CSS, felt less intuitive. Lastly, becoming acquainted with new components (like View instead of div, mandatory Text wrappers, and the differences between Buttons, TouchableOpacity, and TouchableTransparency) presents a learning curve for those new to React Native’s mechanics.
When to Use Each Technology
Given the distinct strengths and weaknesses of Cordova, Ionic, and React Native, each shines in specific contexts.
Cordova is ideal for existing web-first applications with a strong UI brand identity. It allows access to native features while reusing existing components and preserving the brand. Simple applications using responsive frameworks might require minimal changes for porting. However, the app might resemble a webpage more than a native app, and some expected mobile app components will demand extra coding. Therefore, Cordova is recommended for porting web-first projects to mobile.
Ionic is a strong choice for new app-first projects with a team proficient in web development. Its library facilitates the creation of near-native looking and feeling apps using existing web development skills. Web development best practices translate well, and CSS styling works flawlessly. Moreover, the mobile version will have a more native appearance than a responsive website. However, some React-Ionic-Native API integrations might require manual adjustments, potentially increasing development time. Therefore, Ionic is recommended for new projects aiming for significant code sharing between a mobile-capable web app and a dedicated mobile app.
React Native is worth considering for new applications with an existing native codebase. It’s also suitable for those familiar with React Native or when prioritizing the mobile experience over a hybrid approach. As a web developer primarily, I found the initial learning curve steeper compared to Ionic or Cordova due to differences in component structure and conventions. However, once those nuances are grasped, coding becomes quite smooth, especially with libraries like NativeBase. Considering the development environment and control over the app, React Native is recommended if your primary focus is a mobile application.
Future Topics
One aspect I didn’t have time to cover was accessing native APIs, such as camera, geolocation, or biometrics. One of mobile development’s greatest assets is the availability of a feature-rich API ecosystem not easily accessible from the browser.
In future articles, I’d like to explore how easy it is to build such native API-powered applications using various cross-platform technologies.
Conclusion
Today, we built a Twitter curation app using three cross-platform mobile development technologies. Hopefully, this provided valuable insights into each technology and inspired you to create your own React-based application.
You can find the code for this tutorial on GitHub.