September 13, 2023

How to Use NFC and Spin to Create an App

Matt Butcher Matt Butcher

webassembly javascript typescript nfc app

How to Use NFC and Spin to Create an App

My dog Tiberius gets fed three times a day. At least he should get fed three times a day. The whole family shares the responsibility of filling his dish with kibble. But we don’t always coordinate well. In the hustle and bustle of daily life, sometimes Tiberius gets fed twice. Or sometimes he gets a little mischievous and begs for food each time a family member comes home. He once managed to get fed five times in one day using those sad puppy eyes to greet each of us at the door.

We recently landed on a fun hack project to solve this problem. It involves an NFC tag, a small Spin app, and an iPhone shortcuts. Now the family can track our dog feeding by tapping a phone on the dog food bin. Here’s an article about how to use NFC and Spin to create an app.

The Dog Is Happier With a NFC, Spin, and an iPhone Hack

You can see the main app page on the Spin Up Hub or go straight to GitHub to play around with it.

There are three parts to this solution:

  1. Near Field Communication (NFC) - We put an NFC sticker on the dog food bin.
  2. Spin - I wrote a small Spin app with a few REST endpoints and a very basic webpage.
  3. iPhone Shortcuts - We each have an iPhone Shortcuts (Shortcuts is an app that ships on every iPhone) that, when triggered by our NFC sticker, runs actions in our Shortcuts workflow.

When it’s all put together, the process looks like this:

  • When I feed the dog, I tap my phone on the food bin. My phone’s NFC reader triggers the Apple Shortcuts.
  • The Shortcuts contacts my Spin app to see how many times the dog has been fed.
  • If I should refrain from feeding the dog, it displays an alert message on the phone.
  • If I should feed the dog, it will log my feeding of the dog and then present a webpage to tell us all how many times the dog has been fed today.

It’s a nice way to keep track of the dog’s feeding without being an inconvenience. After all, we’re each toting around a phone all day every day.

Here’s a more detailed view of how this app works.

1. NFC Tags: Cheap and Easy

NFC is a cool technology that allows a device (such as a phone or tablet) to respond to the presence of an NFC tag; when the tag is very close to the device. NFC is used for everything from bus passes to Nintendo amiibo.

In our case, what we care about is that an NFC tag has a unique ID that the iPhone can store, and then later react to.

NFC tags are cheap and easy to get. I bought a pack of 50 sticker-backed tags on Amazon for something like $15. There are all kinds of options, though, and most of them are inexpensive.

One quick caveat that I learned quickly: NFC tags do not work so well on metal surfaces. While the dog’s food bin is plastic, I was originally trying to set up the tag on a metal table … and it did not work.

2. A Spin App

The goal was to utilize the tag to trigger a phone action, subsequently updating a synchronized feeding database accessible to all family members. It’s easy to write a Spin app to do the database part, and also to build some fancier features:

  • Writing a REST and Web interface is super easy.
  • Fermyon Cloud’s Key/Value storage was perfect for the data.
  • Deploying to Fermyon Cloud made it easy to share a URL with the whole family.

The code is all here, and I won’t walk through it. But I will talk through the high-level architecture and show off a few snippets.

The app needed three things:

  1. An HTTP GET endpoint that revealed the number of times the dog had already been fed.
  2. An HTTP POST endpoint to increment that number; when I feed the dog.
  3. A website to display the dog’s current eating status.

Originally I had envisioned also tracking the number of dog treats we gave the dog, and you can see that code stubbed in as well. But so far we haven’t needed that feature. The dog is still winning on that front.

I wrote the app in TypeScript because that is the easiest language for me. The total code size is only 87 lines. At the core of the entire app is one function called feedDogN, which feeds the dog zero or more times, and then returns the dog’s eating status.

const feedDogN = async function (numTimes: number): Promise<any> {
  let today = todaysDate()
  let store = Kv.openDefault()
  try {
    var record = store.getJson(today)
    // Short circuit if the dog isn't being fed right now.
    if (numTimes > 0) {
      console.log("Updating record by " + numTimes)
      record.fed += numTimes
      store.setJson(today, record)
    }
    return record
  } catch (e: any) {
    console.log("caught error: " + e)
    console.log("creating new record for " + today)
    let newRecord = {
      date: today,
      fed: numTimes,
      treats: 0,
    }
    store.setJson(today, newRecord)
    return newRecord
  }
}

Here’s how the function works:

  • Every day gets a new record, where the key is the date and the value is an object.
  • If numTimes is greater than 0, then the record is updated to reflect a new feeding.
  • The feeding record for the day is returned whether or not the dog is fed.

There’s a little work to also lazily instantiate the record if no record exists for the day.

The format of the record is straightforward, and we’ll use it from the iPhone in the next step.

{
  date: "TODAY'S DATE",
  fed: 0,    // Number of times the dog is fed
  treats: 0 // Unused. For when I implement treat tracking
}

That’s it.

I used the built-in Spin router to build out a simple routing table that looks like this:

router
  .get("/v1/dog", isDogFed)       // Show the record
  .post("/v1/dog/feed", feedDog)  // Update by 1
  .get("/", mainPage)             // Show the web page
  .all('*', () => { return { status: 404, body: "Not Found" } })

Since the web page also uses some assets like images, I also installed the Spin fileserver and the Spin redirect module. So I have three components declared in my spin.toml:

[[component]]
id = "feed-the-dog"
source = "target/feed-the-dog.wasm"
exclude_files = ["**/node_modules"]
key_value_stores = ["default"]
[component.trigger]
route = "/..."
[component.build]
command = "npm run build"

[[component]]
source = { url = "<https://github.com/fermyon/spin-fileserver/releases/download/v0.0.2/spin_static_fs.wasm>", digest = "sha256:65456bf4e84cf81b62075e761b2b0afaffaef2d0aeda521b245150f76b96421b" }
id = "files"
files = [{ source = "assets", destination = "/" }]
[component.trigger]
route = "/static/..."

[[component]]
source = { url = "<https://github.com/fermyon/spin-redirect/releases/download/v0.0.1/redirect.wasm>", digest = "sha256:d57c3d91e9b62a6b628516c6d11daf6681e1ca2355251a3672074cddefd7f391" }
id = "redirect-favicon"
environment = { DESTINATION = "/static/favicon.ico" }
[component.trigger]
route = "/favicon.ico"
executor = { type = "wagi" }

The redirect module is there to handle browser requests for /favicon.ico, which displays the favicon image.

With those high-level points, you can dive into the entire code here and see how it works.

Once I had the code written, I used the spin deploy command to deploy the app to Fermyon Cloud, which gave me back a URL of the form feed-the-dog-XXXXXX.fermyon.app (with XXXXX replacing some random letters or digits). That’s all I need to build the iPhone Shortcuts.

3. The iPhone Shortcuts

Apple iPhone ships with an app called Shortcuts. Shortcuts is essentially a NoCode environment for building and triggering actions in a workflow. Over time I’ve created some little Shortcuts to do things like tell me when my next Outlook scheduled meeting is, play jazz music on my office speaker, and show me where on a map the closest coffee shop is.

For tracking the dog’s eating, I wanted to build one that interacted with my Spin app and with the NFC sticker. This was done by combining a Shortcuts shortcut with a Shortcuts automation.

The main workflow is built as a shortcut (not an automation) so that it is easy to share with the rest of my family. Here’s the sequence I built:

dog food video

Here’s the logic behind the steps in the Shortcuts workflow:

  • The first action sets the URL to the app’s v1/dog endpoint.
  • The second fetches the data from the URL, and holds it in the phone’s memory.
  • The third action gets the value for fed i.e. {"fed": 2}.
  • Then the numerical value is extracted out of a parsed version of the JSON.
  • If numbers is greater than or equal to 3 (the dog’s max meals) then the app displays th alert; ❗❗Don’t feed the dog 🐶.
  • Otherwise, the URL is set to the app’s feed (/v1/dog/feed) endpoint, which will increase the count of fed.
  • Lastly, the Shortcuts workflow uses Open (a Safari Open URLs action) to display the result via the app’s web page https://feed-the-dog-XXXXXXX.fermyon.app/ (in the phone’s browser).

Once that was all done and saved, I had a shortcut named “Feed the Dog”. The next step was to use the same Shortcuts app to add automation.

Linking the NFC tag to a Shortcut

In Shortcuts parlance, automation ties an event to a workflow. Above we defined the workflow as a shortcut. So now we just need to tie the action of tapping a specific NFC tag to the “Feed the Dog” shortcut defined above.

Here’s what I did:

  • In the Shortcut app, I clicked the Automations icon, then clicked +, and then Create Personal Automation.
  • Scrolling down through the list of Automation triggers, I located NFC and chose that.
  • It prompts to Scan the NFC, at which point I just held the phone over the NFC tag until it scanned the tag. I named the newly scanned tag “Dog Food”.
  • Then, when it prompted me to add an action, I chose Run Shortcut and then selected the Feed the Dog shortcut I created above.

Dog food automation

Sharing with others is a little inconvenient. Apple makes it simple to choose a Shortcut and use the sharing button to text or email it to others. But every person who uses the Automation will need to walk through the “Linking the NFC tag to a Shortcut” process.

Testing it all out, when I tap the phone on the dog food bin, this is what I see:

Dogfood Safari

Conclusion

The problem was simple: make sure the dog eats the right amount of food. With an NFC tag, a few dozen lines of TypeScript, and the iPhone Shortcuts app, I built a little app that lets me tap my phone on the dog food bin to register the dog as being fed.

Our developer documentation has more information about writing Spin applications, deploying to Fermyon Cloud, and using your very own custom domain to share your app with the world.

If you want to fast-track an application like this you might like to kick things off with the Dog Feeding Tracker with NFC and iPhone template in our Spin Up Hub.

Have any questions? Reach out to us on Discord.

Also, check out the “Where to find us next” slider at the bottom of the Fermyon Developer Home page. Come and visit us live at an event, say hello, and pick up some Fermyon swag.


🔥 Recommended Posts


Quickstart Your Serveless Apps with Spin

Get Started