You can find the complete source code of the project here. All the different steps are tagged in the git repository, so you can easily check out the app every step of the way.
Let’s get started!
The Xcode Project
Now we just need to add a basic UI: Open
Main.storyboard, add a
UITextField to the top of the view and add a
UILabel underneath. Add a few constraints so that the textfield is pinned to the top, to the left and to the right and make the label occupy the rest of the space. Choose a nice big font for the label because we want to use it to display emojis; I used 134 points. Connect both the textfield and the label to
sentimentLabel outlets, respectively. Also connect the textfield’s
Editing Changed event to a
textDidChange action in the
ViewController. You can see what the app should look like by checking out the tag
step1 from the repo.
Walking the Line
Next, we will add a new Cocoa Touch class named
JSContext is associated with a
Everything is being set up in the
JSContext we’ve created, ready to be called later on. The snippet simply defines an
analyze function that will return a random value between -5 and 5. We will later replace that with code that calls the actual sentiment npm package. That package uses a score to represent the sentiment: below zero is negative (the lower the worse) and above 0 is positive (the higher the better).
toInt32 on the returned
JSValue. We know that we are getting an integer back from the
analyze function, so we can safely make that conversion. We then create an
Int from that value because we don’t want to hand around a specifically sized 32-bit integer in our code.
Finally, we also have a small helper method that returns an emoji for a given sentiment score. The complete class looks like this:
Let’s hook it up in the
Check out the tag
Inside the JS directory we will run
npm init --yes to create a
package.json file with some default settings. Feel free to open it and edit the author, name and description, but it’s not really necessary. The important thing is that this file will contain the names and versions of the npm packages that our app will use. Let’s add those right now. Run
npm install sentiment --save to install the
sentiment package and to write that dependency to the
package.json file. Next run
--save-dev option here because we only need this package as a development and not as a runtime dependency. If you checkout your project on a different machine, all you have to do is run
npm install inside of the “JS” directory to install the dependencies.
sentiment package. Create a
index.js file with this content:
Finally we have to configure webpack to build and bundle the app. Webpack is an impressive tool that can analyze which files and packages your project imports and then bundle all of it up into a single file (or – if you prefer – multiple files). Create a new file called
webpack.config.js right inside the “JS” directory with this content:
This is a minimal webpack configuration: The
entry section can be compared to targets in Xcode. We have one target called
Sentimentalist and the file that webpack should use as a starting point to find all the dependencies that need to be included in the final bundle is
output section specifies that the bundle should be written to the
dist directory, that is should be called
Sentimentalist.bundle.js and that the bundle should be accessible as a global variable also named
Sentimentalist. This is just the tip of the iceberg of what webpack can do, but it’s enough for our example. Be sure to checkout the documentation so you can master webpack.
To save us from having to type a few extra characters each time we want to build the application, add this line to the
”scripts” section of “package.json”:
"build": "webpack --progress --colors”
Now let’s see if it all works. Run
npm run build. You should see an output like this:
Check out the tag
step3 to see what the app should look like at this point.
Now we just need to integrate the
Sentimentalist.bundle.js into our iOS app.
Bringing it all back home
In Xcode, right-click on the Sentimentalist group in the Navigator and select “Add Files to Sentimentalist…”. Navigate to the “JS/dist” directory and select “Sentimentalist.bundle.js”: That will add the file to the project and also make sure that it is copied to the app bundle as part of the build process.
Now all that’s left to do is to use this bundle in the
SentimentAnalyzer class. Replace the
let jsCode… line with this one:
Finally we need to dig a little deeper to get to our new
Sentimentalist. Within that our class that contains the static
analyze method is called
Analyzer. This is what the Swift
analyze method needs to look like:
Run the app and try out a few sentences and watch the emoji change!
Check out the tag
step4 to see what the app should look like at this point.
init method we simply declare a Swift closure with the name
nativeLog. We then let the JSContext know about that method by setting it via
setObject:forKeyedSubscript:. The fact that we need to add
@convention(block) to the closure and cast the Swift string explicitly to
Let’s switch back to
index.js and make use of the native logging function. To play it safe we check if the
nativeLog function is defined and then we simply call it:
npm run build again and then Build & Run in Xcode: As you type you will see the log messages show up in Xcode’s console!
Checkout the tag
step5 in the git repo to see the app at this state.
Is it fast?
Yes. Well, it depends. It is very fast for probably all but the most performance critical operations. In
step6 in the git repo you see a performance test being added to the Xcode project. When it is run on my 2017 iPad (not a Pro), it measures an average performance of a little under 4 milliseconds for a call to the
analyze method until the callback with the score is called. That’s pretty fast! When targeting a refresh rate of 60 frames per second, you have 16ms per frame before you start dropping frames. That means we could call our
analyze method 4 times per frame and still achieve 60fps. Not bad!
So. . . what should I use this for?