Tuesday, April 28, 2015

Instant Apps for the Apple Watch with the Wolfram Language

My goal with the Wolfram Language is to take programming to a new level. And over the past year we’ve been rolling out ways to use and deploy the language in many places—desktop, cloud, mobile, embedded, etc. So what about wearables? And in particular, what about the Apple Watch? A few days ago I decided to explore what could be done. So I cleared my schedule for the day, and started writing code.

My idea was to write code with our standard Wolfram Programming Cloud, but instead of producing a web app or web API, to produce an app for the Apple Watch. And conveniently enough, a preliminary version of our Wolfram Cloud app just became available in the App Store—letting me deploy from the Wolfram Cloud to both mobile devices and the watch.

A few lines of Wolfram Language code creates and deploys an Apple Watch app

To some extent it was adventure programming. The Apple Watch was just coming out, and the Wolfram Cloud app was still just preliminary. But of course I was building on nearly 30 years of progressive development of the Wolfram Language. And I’m happy to say that it didn’t take long for me to start getting interesting Wolfram Language apps running on the watch. And after less than a day of work—with help from a handful of other people—I had 25 watch-ready apps:

Icons for a number of our new Wolfram Language watch apps, on an iPhone ready for instant deployment to the Apple Watch

All of these I built by writing code in the Wolfram Programming Cloud (either on the web or the desktop), then deploying to the Wolfram Cloud, and connecting to the Apple Watch via the Wolfram Cloud app. And although the apps were designed for the Apple Watch, you can actually also use them on the web, or on a phone. There are links to the web versions scattered through this post. To get the apps onto your phone and watch, just go to this page and follow the instructions. That page also has all the Wolfram Language source code for the apps, and you can use any Wolfram Language system—Wolfram Programming Cloud (including the free version), Mathematica etc.—to experiment with the code for yourself, and perhaps deploy your own version of any of the apps.

My First Watch App

So how does it all work? For my first watch-app-writing session, I decided to start by making a tiny app that just generates a single random number. The core Wolfram Language code to do that is simply:

In[1]:= RandomInteger[1000]

For the watch we want the number to look nice and bold and big, and it might as well be a random color:

In[2]:= Style[RandomInteger[1000], Bold, 30, RandomColor[]]

We can immediately deploy this publicly to the cloud by saying:

In[3]:= CloudDeploy[Delayed[Style[RandomInteger[1000], Bold, 250, RandomColor[]], "PNG"], Permissions -> "Public"]

And if you go to that URL in any web browser, you’ll get to a minimal web app which immediately gives a web page with a random number. (The Delayed in the code says to delay the computation until the moment the page is accessed or refreshed, so you get a fresh random number each time.)

So what about getting this to the Apple Watch? First, it has to get onto an iPhone. And that’s easy. Because anything that you’ve deployed to the Wolfram Cloud is automatically accessible on an iPhone through the Wolfram Cloud app. To make it easy to find, it’s good to add a recognizable name and icon. And if it’s ultimately headed for the watch, it’s good to put it on a black background:

In[4]:= CloudDeploy[Delayed[ExpressionCell[Style[RandomInteger[1000], Bold, 250, RandomColor[]], Background -> Black], "PNG"], "WatchApps/RandomNumber", IconRules -> WordCloud[RandomInteger[10, 20]]]

And now if you go to this URL in a web browser, you’ll find a public version of the app there. Inside the Wolfram Cloud app on an iPhone, the app appears inside the WatchApps folder:

Deploy that RandomNumber app, and it will appear on your phone

And now, if you touch the app icon, you’ll run the Wolfram Language code in the Wolfram Cloud, and back will come a random number, displayed on the phone:

The RandomNumber app works fine on the phone, but of course is sized for the Apple Watch screen

If you want to run the app again, and get a fresh random number, just pull down from the top of the phone.

To get the app onto the watch, go back to the listing of apps, then touch the watch icon at the top and select the app. This will get the app listed on the watch that’s paired with your phone:

That's all it takes to get the app onto your watch

Now just touch the entry for the RandomNumber app and it’ll go to the Wolfram Cloud, run the Wolfram Language code, and display a random number on the watch:

And here it is running on the watch--it's that easy

 

Randomness Apps

It’s simple to make all sorts of “randomness apps” with the Wolfram Language. Here’s the core of a Coin Flip app:

In[5]:= RandomChoice[{image:heads, image:tails}]

And this is all it takes to deploy the app, to the web, mobile and watch:

In[6]:= CloudDeploy[Delayed[ExpressionCell[RandomChoice[{image:heads, image:tails}], Background -> Black], "PNG"], "WatchApps/CoinFlip", IconRules -> image:heads]

One might argue that it’s overkill to use our sophisticated technology stack to do this. After all, it’s easy enough to flip a physical coin. But that assumes you have one of those around (which I, for one, don’t any more). Plus, the Coin Flip app will make better randomness.

What about playing Rock, Paper, Scissors with your watch? The core code for that is again trivial:

In[7]:= RandomChoice[{image:rock, image:paper, image:scissors}]

There’s a huge amount of knowledge built in to the Wolfram Language—including, in one tiny corner, the knowledge to trivially create a Random Pokemon app:

In[8]:= EntityValue[EntityValue["Pokemon", "RandomEntity"], {"Image", "Name"}]

Here it is running on the watch:

Stats pop quiz: How many random displays will it take, on average, before you catch 'em all?

Let’s try some slightly more complex Wolfram Language code. Here’s a Word Inventor that makes a “word” by alternating random vowels and consonants (and often the result sounds a lot like a Pokemon, or a tech startup):

In[9]:= vowels = {"a", "e", "i", "o", "u"}; consonants = Complement[CharacterRange["a", "z"], vowels]; Style[StringJoin[Flatten[Table[{RandomChoice[consonants], RandomChoice[vowels]}, {3}]]], 40]

 

Watches Tell Time

If nothing else, one thing people presumably want to use a watch for is to tell time. And since we’re in the modern internet world, it has to be more fun if there’s a cat or two involved. So here’s the Wolfram Language code for a Kitty Clock:

In[10]:= ClockGauge[Now, PlotTheme -> "Minimal", GaugeMarkers -> {image:graycat, image:orangecat, None}, Background -> Black, TicksStyle -> White]

Which on the watch becomes:

You can has kitty clock...

One can get pretty geeky with clocks. Remembering our recent very popular My Pi Day website, here’s some slightly more complicated code to make a Pi Clock where the digits of the current time are displayed in the context where they first occur in pi:

In[11]:= pi = Characters[ToString[N[Pi, 65000]]]; time = Characters[DateString[{"Hour12", "Minute"}]]; pos = First[SequencePosition[pi, time]]; Style[Grid[Partition[Join[Take[pi, 14], Characters["..."], Take[pi, pos - {13, 1}], Style[#, Orange] & /@ Take[pi, pos], Take[pi, pos + {5, 4}]], 10], Spacings -> {0, 0}], 40, Background -> Black, FontColor -> White]

Or adding a little more:

And now you can know exactly what digit of pi any time of day begins at

Where Are You?

So long as you enable it, the Apple Watch uses GPS, etc. on its paired phone to know where you are. That makes it extremely easy to have a Lat-Long app that shows your current latitude and longitude on the watch (this one is for our company HQ):

In[12]:= Style[Column[{DMSString[Latitude[Here], {1, "NS"}], DMSString[Longitude[Here], {1, "EW"}]}], 30, White, Background -> Black]

I’m not quite sure why it’s useful (prove location over Skype?), but here’s a Here & Now QR app that shows your current location and time in a QR code:

In[13]:= BarcodeImage[StringJoin[DMSString[Here], "|", DateString[Now]], "QR"]

Among the many things the Wolfram Language knows a lot about is geography. So here’s the code to find the ten volcanoes closest to you:

In[14]:= v = GeoNearest["Volcano", Here, 10]

A little more code shows them on a map, and constructs a Nearest Volcanoes app:

In[15]:= GeoGraphics[{GeoPath[{Here, #}] & /@ v, GeoMarker[Here], GeoMarker[#, image:volcano-icon] & /@ v}, GeoRange -> 1.5 GeoDistance[Here, First[v]]]

Here’s the code for a 3D Topography app, that shows the (scaled) 3D topography for 10 miles around your location:

In[16]:= ListPlot3D[GeoElevationData[GeoDisk[Here, Quantity[10, "Miles"]]], MeshFunctions -> {#3 &}, Mesh -> 30, Background -> Black, Axes -> False, ViewPoint -> {2, 0, 3}]

 

Data Flows In

Since the watch communicates with the Wolfram Cloud, it can make use of all the real-time data that’s flowing into the Wolfram Knowledgebase. That data includes things like the current (x,y,z,t) position of the International Space Station:

In[17]:= entity:International Space Station (satellite) ["Position"]

Given the position, a little bit of Wolfram Language graphics programming gives us an ISS Locator app:

In[18]:= Module[{pos, line, rise}, {pos, line, rise} = SatelliteData[entity:International Space Station (satellite), {"Position", "SatelliteLocationLine", "RiseTime"}]; Style[Labeled[GeoGraphics[{{Pink, AbsoluteThickness[3], GeoPath @@ line}, {Red, PointSize[.04], Point[pos]}, {Opacity[.1], Black, GeoVisibleRegion[pos]}}, GeoGridLines -> Automatic, GeoCenter -> pos, GeoRange -> "World", GeoProjection -> "Orthographic", ImageSize -> {272, 340 - 38}], Style[TemplateApply["Next rise: ``", NumberForm[ UnitConvert[DateDifference[Now, rise], "Minutes"], 3]], White, 20]], Background -> Black]]

As another example of real-time data, here’s the code for an Apple Quanting app that does some quant-oriented computations on Apple stock:

In[19]:= Style[TradingChart[{"AAPL", DatePlus[-90]}, {"Volume", Style["MESASineWave", {RGBColor[1, 1, 1], RGBColor[0.46, 0.62, 0.8200000000000001]}], Style["BollingerBands", RGBColor[1, 1, 1]], Style["DoubleExponentialMovingAverage", RGBColor[1, 0.85, 0.21]]}, PerformanceGoal -> "Speed", Axes -> False, Frame -> False], Background -> Black]

And here’s the code for a Market Word Cloud app that shows a stock-symbols word cloud weighted by fractional price changes in the past day (Apple up, Google down today):

In[20]:= WordCloud[With[{c = FinancialData[#, "FractionalChange"]}, Abs[c] -> Style[#, ColorData[{"RedGreenSplit", 0.01 {-1, 1}}, c]]] & /@ {"AAPL", "XOM", "GOOG", "MSFT", "BRK-A", "WFC", "JNJ", "GE", "WMT", "JPM"}, Background -> Black]

Here’s the complete code for a geo-detecting Currency Converter app:

In[21]:= With[{home = $GeoLocationCountry["CurrencyUnit"]}, Style[QuantityForm[Grid[{#, "=", CurrencyConvert[#, home]} & /@ Cases[{Quantity[1, "USDollars"], Quantity[1, "Euros"], Quantity[1, "Yen"], Quantity[1, "BritishPounds"]}, Except[home]], Alignment -> Left], "Abbreviation"], White, Background -> Black, 30]]

It’s easy to make so many apps with the Wolfram Language. Here’s the core code for a Sunrise/Sunset app:

In[22]:= {Sunrise[], Sunset[]}

Setting up a convenient display for the watch takes a little more code:

In[23]:= With[{sunfmt = Style[DateString[#, {#2, " ", "Hour12Short", ":", "Minute", "AMPMLowerCase"}], 54] &, tfmt = Round[DateDifference[Now, #, {"Hour", "Minute"}], 5] &}, Rasterize@Style[Column[{sunfmt[Sunrise[], "rise"], tfmt[Sunrise[]], sunfmt[Sunset[], "set"], tfmt[Sunset[]]}, Alignment -> Right], FontSize -> 32, Background -> Black, White]]

The Wolfram Language includes real-time weather feeds:

In[24]:= AirTemperatureData[]

Which we can also display iconically:

In[25]:= IconData["AirTemperature", AirTemperatureData[]]

Here’s the data for the last week of air temperatures:

In[26]:= AirTemperatureData[Here, {Now - Quantity[1, "Weeks"], Now}]

And with a little code, we can format this to make a Temperature History app:

In[27]:= With[{temps = DeleteMissing[AirTemperatureData[Here, {Now - Quantity[1, "Weeks"], Now}]["Values"]]}, QuantityForm[Style[Column[{Grid[{{"Current", Last[temps]},{"High", Max[temps]}, {"Low", Min[temps]}}, Alignment -> {{Right, Left}}], ListLinePlot[temps, ImageSize -> 312, PlotStyle -> None, Filling -> Bottom, FillingStyle -> Automatic, ColorFunction -> Function[{x, y}, Blend[{RGBColor[0.45, 0.72, 0], RGBColor[1, 0.85, 0]}, y]], PlotTheme -> "NoAxes"]}, Alignment -> Right], Background -> Black, 24, White], "Abbreviation"]]

Sometimes the easiest way to get a result in the Wolfram Language is just to call Wolfram|Alpha. Here’s what Wolfram|Alpha shows on the web if you ask about the time to sunburn (it detects your current location):

Wolfram|Alpha recognizes your location, knows the current UV index there, and computes how long you could safely stay out in the sun depending on your skin type

Now here’s a real-time Sunburn Time app created by calling Wolfram|Alpha through the Wolfram Language (the different rows are for different skin tones):

In[28]:= times = Style[QuantityForm[#, {}], 24, White, FontFamily -> "Source Sans Pro"] & /@ Rest[WolframAlpha["sunburn time", {{"TypicalTimeToSunburn", 1}, "ComputableData"}][[All, 2]]]; In[29]:= Panel[Grid[Transpose[{{image:skintonesI, image:skintonesII, image:skintonesIII, image:skintonesIV, image:skintonesV, image:skintonesVI}, times}], Dividers -> {False, Center}, FrameStyle -> Gray, Spacings -> 5, Alignment -> {Center, Center}], Background -> Black]

 

Reports & Data Drops

The Wolfram Language has access not only to all its own curated data feeds, but also to private data feeds, especially ones in the Wolfram Data Drop.

As a personal analytics enthusiast, I maintain a databin in the Wolfram Data Drop that tells me my current backlog of unprocessed and unread email messages. I have a scheduled task that runs in the cloud and generates a report of my backlog history. And given this, it’s easy to have an SW Email Backlog app that imports this report on demand, and displays it on a watch:

Lighter orange is total number of messages; darker orange is unread messages...

And, yes, the recent increase in unprocessed and unread email messages is at least in part a consequence of work on this blog.

There are now lots of Wolfram Data Drop databins around, and of course you can make your own. And from any databin you can immediately make a watch app that shows a dashboard for it. Like here’s a Company Fridge app based on a little temperature sensor sitting in a break-room refrigerator at our company HQ (the cycling is from the compressor; the spike is from someone opening the fridge):

In[30]:= DateListPlot[Databin["4r4-gP4o", -300, "temp"], PlotStyle -> RGBColor[0, 0.501961, 1], Background -> Black, DateTicksFormat -> {"Hour12Short", "AMPMLowerCase"}, FrameStyle -> Directive[Black, FontColor -> White, 18], FrameLabel -> Automatic, TargetUnits -> Quantity[1, "DegreesFahrenheitDifference"], AspectRatio -> 1.11, ImageSize -> 312]["temp"]

Databins often get data from just a single source or single device. But one can also have a databin that gets data from an app running on lots of different devices.

As a simple example, let’s make an app that just shows where in the world that app is being accessed from. Here’s the complete code to the deploy such a “Data Droplets” app:

In[31]:= CloudDeploy[Delayed[With[{db = Databin[DatabinAdd["4rwD7T5G", 0], -20]["GeoLocations"]}, GeoGraphics[{Red, PointSize[.02], MapThread[{Opacity[#], Point[#2]} &, {Subdivide[0.15, 1, Length[db] - 1], db}]}, GeoRange -> All, GeoProjection -> "LambertAzimuthal", Background -> Black, PlotLabel -> Style["Recent Data Droplets", White, 24]]], "PNG"], "WatchApps/DataDroplets"]

The app does two things. First, whenever it’s run, it adds the geo location of the device that’s running it to a central databin in the Wolfram Data Drop. And second, it displays a world map that marks the last 20 places in the world where the app has been used:

Data Droplets app on the watch--just touch the screen...

 

Making Things Happen

A typical reason to run an app on the watch is to be able to see results right on your wrist. But another reason is to use the app to make things happen externally, say through APIs.

As one very simple example, here’s the complete code to deploy an app that mails the app’s owner a map of a 1-mile region around wherever they are when they access the app:

In[32]:= CloudDeploy[Delayed[SendMail[GeoGraphics[{Opacity[.4, Red], PointSize[.05], Point[Here]}, GeoRange -> Quantity[1, "Miles"]]]; Style["Sent!", 200], "PNG"], "WatchApps/MailMyLocation", IconRules -> image:maillocationicon]

Email sent by the MailMyLocation app--log where you've been, share your location, remember where you parked...

 

Apps to Generate Apps

So far, all the apps we’ve talked about are built from fixed pieces of Wolfram Language code that get deployed once to the Apple Watch. But the Wolfram Language is symbolic, so it’s easy for it to manipulate the code of an app, just like it manipulates any other data. And that means that it’s straightforward to use the Wolfram Language to build and deploy custom apps on the fly.

Here’s a simple example. Say we want to have an app on the watch that gives a countdown of days to one’s next birthday. It’d be very inconvenient to have to enter the date of one’s birthday directly on the watch. But instead we can have an app on the phone where one enters one’s birthday, and then this app can in real time build a custom watch app that gives the countdown for that specific birthday.

Here we enter a birthday in a standard Wolfram Language “smart field” that accepts any date format:

Run the generator app on your phone and enter your birthday...

And as soon as we touch Submit, this app runs Wolfram Language code in the Wolfram Cloud that generates a new custom app for whatever birthday we entered, then deploys that generated app so it shows up on our watch:

...And it deploys the generated app to the watch, ready to run

Here’s the complete code that’s needed to make the Birthday Countdown app-generating app.

In[33]:= CloudDeploy[FormFunction[{"Birthday" -> "Date"}, (CloudDeploy[Delayed[ExpressionCell[With[{count = Floor[UnitConvert[Mod[# - Today, ="1 yr"], "Day"]] &}, Style[Framed[Pane[QuantityForm[count[#Birthday], "Abbreviation"], {250, 250}, Alignment -> Center], RoundingRadius -> 50, FrameStyle -> Thick], 40, Hue[.52]]], Background -> Black], "PNG"], "WatchApps/BirthdayCountdown", IconRules -> image:cakeicon]; Style["BirthdayCountdown app generated & deployed", Larger, Background -> LightYellow]) &, "PNG"], "WatchApps/CountdownGenerator", IconRules -> image:cakeandgearicon]

And here is the result from the generated countdown app for my birthday:

As of this writing, there are 123 days until my next birthday. How many days until your own?

We can make all sorts of apps like this. Here’s an example where you fill out a list of any number of places, and create an app that displays an array of clocks for all those places:

Enter a list of cities on your phone, and get an array of clocks for them

You can also use app generation to put you into an app. Here’s the code to deploy a “You Clock” app-generating app that lets you take a picture of yourself with your phone, then creates an app that uses that picture as the hands of a clock:

In[34]:= CloudDeploy[FormFunction[{"image" -> "Image"}, (With[{hand = ImageRotate[ImagePad[ImageResize[#image, 100, Resampling -> "Gaussian"], {{0, 0}, {50, 0}}], -Pi/2]}, CloudDeploy[Delayed[ClockGauge[Now, PlotTheme -> "Minimal", GaugeMarkers -> {hand, hand, None}, Background -> Black, TicksStyle -> White, ImageSize -> 312], "PNG"], "WatchApps/YouClock", IconRules -> "YouClock"]]; Style["YouClock app deployed", 50]) &, "PNG"], "WatchApps/YouClockGenerator", IconRules -> "YCG"]

And here I am as the hands of a clock

And actually, you can easily go even more meta, and have apps that generate apps that generate apps: apps all the way down!

 

More Than I Expected

When I set out to use the Wolfram Language to make apps for the Apple Watch I wasn’t sure how it would go. Would the deployment pipeline to the watch work smoothly enough? Would there be compelling watch apps that are easy to build in the Wolfram Language?

I’m happy to say that everything has gone much better than I expected. The watch is very new, so there were a few initial deployment issues, which are rapidly getting worked out. But it became clear that there are lots and lots of good watch apps that can be made even with tiny amounts of Wolfram Language code (tweet-a-watch-app?). And to me it’s very impressive than in less than one full day’s work I was able to develop and deploy 25 complete apps.

Of course, what ultimately made this possible is the whole Wolfram Language technology stack that I’ve been building for nearly 30 years. But it’s very satisfying to see all the automation we’ve built work so nicely, and make it so easy to turn ideas into yet another new kind of thing: watch apps.

It’s always fun to program in the Wolfram Language, and it’s neat to see one’s code deployed on something like a watch. But what’s ultimately more important is that it’s going to be very useful to lots of people for lots of purposes. The code here is a good way to get started learning what to do. But there are many directions to go, and many important—or simply fun—apps to create. And the remarkable thing is that the Wolfram Language makes it so easy to create watch apps that they can become a routine part of everyday workflow: just another place where functionality can be deployed.


No comments:

Post a Comment

Related Posts Plugin for WordPress, Blogger...