DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Cookies Revisited: A Networking Solution for Third-Party Cookies
  • Scaling in Practice: Caching and Rate-Limiting With Redis and Next.js
  • Efficiently Migrating From Jest to Vitest in a Next.js Project
  • Serverless Sign-In Solution Based on Next.js on CloudFare

Trending

  • AI Meets Vector Databases: Redefining Data Retrieval in the Age of Intelligence
  • Unlocking AI Coding Assistants Part 4: Generate Spring Boot Application
  • Docker Base Images Demystified: A Practical Guide
  • How Large Tech Companies Architect Resilient Systems for Millions of Users
  1. DZone
  2. Coding
  3. JavaScript
  4. Introducing Puppygram, Powered by Courier Inbox, Next.js, and Inngest

Introducing Puppygram, Powered by Courier Inbox, Next.js, and Inngest

Follow this step-by-step tutorial to build Instagram-style iOS notifications using the Courier iOS SDK, Courier Inbox, Next.js, and Inngest.

By 
Carter Rabasa user avatar
Carter Rabasa
·
Aug. 30, 23 · Tutorial
Likes (1)
Comment
Save
Tweet
Share
2.5K Views

Join the DZone community and get the full member experience.

Join For Free

An in-app notification center is an essential component of any modern app, but they're time-consuming and complicated to build from scratch. What if you want to launch something quickly and easily? Where do you start?  

I decided to use Courier Inbox to build something light years away from a product inbox. I thought long and hard about what to build and, perhaps sensing my creative block, my dog Otto came up and asked for his afternoon walk.

Otto photo

And just like that, Puppygram was born! Puppygram is an Instagram clone that is built for iOS and is powered by Courier Inbox, Next.js, and Inngest. In this blog post we’re going to cover:

  • Creating a fresh Next.js web application
  • Configuring Courier to send Inbox notifications
  • Fetching random pictures of cute dogs using the random.dog API
  • Using Inngest to send a new dog notification to our Inbox every minute
  • Creating the Puppygram iOS experience using the Courier iOS SDK

There are a few prerequisites for completing this tutorial:

  • Node.js
  • Xcode for Mac
  • Courier and Inngest accounts

When we're done, we'll have an app that looks like this:

Puppygram screenshot

You can find the full source code for the Puppygram Server and Puppygram iOS app on GitHub and a live demo of this app hosted on Vercel.

Creating a Next.js Web Application

In order to build a Next.js app, you’ll need to have Node.js installed. My preference these days is to use NVM (Node Version Manager) to install Node.js. It makes it easy to install multiple versions of Node.js and switch between them in your projects.

Once you’ve installed Node.js, open up a terminal and run the following command to install Next.js:

npx create-next-app@latest


You’ll be prompted to answer several questions, but it’s fine to stick to the defaults. Once this process is complete, a new directory will be created and loaded with all of the default files for this app.

Change into this new directory and create a .env.local file to store secrets for Courier and Inngest. We’ll populate this file while we’re building and testing on localhost, and you’ll just need to remember to copy these environment variables to whatever platform or infra you deploy your app to.

Get Courier API Credentials

Log in to your Courier account, click on the gear icon, and then API Keys. When you create a Courier account, two Workspaces are automatically created for you: one for testing and one for production. Each workspace has its own set of data and API keys.

For simplicity, we’re going to stick to the “production” workspace. Copy the “published” production API Key and paste into into .env.local using the following key:

COURIER_AUTH_TOKEN=pk_XXX


Configure Your Courier Inbox Provider

Click on “Channels” in the left nav. Channels represent the different mediums that a user can receive a notification on. Courier supports all the most popular channels, including SMS, email, mobile push, and others. For each channel, you’ll see a list of Providers. For instance, Courier supports multiple SMS providers, including Twilio, MessageBird, and Vonage just to name a few.

In our case, scroll down and select the “Courier Inbox” provider. When the page opens, scroll to the bottom and click “install”. You are now ready to send notifications to the “inbox” channel using the “Courier Inbox” provider.

Creating a Public Inbox

Courier Inbox is primarily designed to display notifications to individual users, but in our case, the Inbox and its content will be viewed by anyone using the app. Still, we need to create a “user” to send these notifications to, so click on “Users” in the left navigation and create a new user.

The only required field is user_id, so just go ahead and enter puppygram and click “Save”. In your .env.local, create the following variable:

NEXT_PUBLIC_COURIER_USER=puppygram


Get Inngest Credentials

Inngest is a workflow-as-code service that makes it easy to build reliable serverless workflows in your current codebase, without any new infrastructure. We’re going to use Inngest to create a cron job that sends puppy pics to our app once every minute.

Log in to your Inngest account and click on “Manage” in the top nav. Click on “Event Keys” in the subnav.

Event Keys are used to bundle similar events. This helps when navigating the Inngest dashboard, debugging, etc. The recommendation is that you create a new Event Key for every combination of environment and application. In our case, we’re just going to use the “Default ingest key”, but you can easily create new Event Keys to use. Copy the value for this key and paste it into your app’s .env.local file:

INNGEST_EVENT_KEY=xxx


Next, click on “Signing Key." The Signing Key is used by the Inngest SDK to securely communicate with your application. Copy the value for this key and paste it into your app’s .env.local file:

INNGEST_SIGNING_KEY=signkey-prod-yyy


Let’s Start Coding!

Okay, now that we have our services and configuration out of the way, let’s dive into the code. We’re going to start by building the server-side web application that is responsible for sending pictures of puppies to our iOS app. Our web application will be designed to:

  • Wake up once a minute
  • Select a random picture of a dog
  • Send an inbox notification with the URL of the picture of the random dog

Using Inngest To Schedule Jobs

With just a few lines of code, we are going to wire up our web application to Inngest so that the service can call into our application once a minute to trigger a new notification to our Courier Inbox. We’re going to breeze through their Quick start, which you can review in more detail later.

In the root of your project, run the following command to install the Inngest SDK:

npm install inngest


Create a new directory called inngest in your project root. Create a file called client.js in this new directory:

import { Inngest } from "inngest";

// Create a client to send and receive events
export const inngest = new Inngest({ name: "Puppygram Next.js" });


Now create a route handler to handle the /api/inngest route. Create a directory in app called api and a directory in api called inngest. Create a file called route.js:

import { serve } from "inngest/next";
import { inngest } from "../../../inngest/client";

export const { GET, POST, PUT } = serve(inngest, []);


Finally, let’s create an Inngest function that prints “Hello Puppygram” every minute. Edit route.js and paste this function just below the imports:

export const sendNotification = inngest.createFunction(
    { name: "Puppygram Send Notification" },
    { cron: "* * * * *" },
    async () => {       
      console.log(“Hello Puppygram!”)
    }
)


Now, update the serve call at the bottom of the file to include this new Inngest function that has been created:

export const { GET, POST, PUT } = serve(inngest, [
    sendNotification3])


In your terminal, go ahead and start your web application:

npm run dev


Open up another terminal and run:

npx inngest-cli@latest dev


This will execute a localhost version of the Inngest service. This will connect to your web application (running on port 3000) and begin executing your cron job. Go back to the terminal you launched your web app in, and every minute you should see this printout:

Hello Puppygram!


Getting Pictures of Random Dogs

Thanks to our friends at the Random Dog API, we have a service that we can use to get pictures of very cute, very random dogs. The API supports 3 different endpoints:

  • /woof - Return the ID of a random dog
  • /woof.json - Return a JSON payload of a random dog
  • /doggos - Return JSON array of all dog IDs

Since our service is going to wake up once a minute to send a notification with a picture of a random dog, we didn’t want to burden this free service with all those API calls. So instead we invoked the /doggos endpoint and copied that information into a file in our project.

Create a directory at the project root called data and a file in it called doggos.json. Paste the following into that file:

[
    "00186969-c51d-462b-948b-30a7e1735908.jpg",
    "00b417af-0b5f-42d7-9ad0-6aab6c3db491.jpg",
    "027eef85-ccc1-4a66-8967-5d74f34c8bb4.jpg",
    "02f1d7d0-9ff7-44af-8066-dd9247ebe74d.jpg",
    "03024628-188b-408e-a853-d97c9f04f903.jpg",
    "0356c15a-8874-4af3-a02a-ed0ae8d62b55.jpg"
]


Now, let’s update our sendNotification function in app/api/inngest/routes.js to pick a random image and print it out to the console. Add the following import to the top of the file:

import doggos from '../../../data/doggos.json' assert { type: 'json' }


Update the function to select a random image from the array and print it out:

export const sendNotification = inngest.createFunction(
    { name: "Puppygram Send Notification" },
    { cron: "* * * * *" },
    async () => {       
        // get random dog photo
        const randomIndex = Math.floor(Math.random() * doggos.length)
        const image = doggos[randomIndex]
        console.log(image)
    }
)


Reset your Next.js dev server, and you should see random image filenames being printed out once a minute. Now it’s time to send those to Courier Inbox!

Sending Random Dog Pics to Courier Inbox

The final part of the server-side component of Puppygram is the code to send the random dog pic to Courier Inbox. 

First, let’s add the Courier Node.js SDK to our project:

npm i @trycourier/courier


Next, import the module and initialize the API client at the top of route.js:

import { CourierClient } from '@trycourier/courier'

const courier = CourierClient()


Finally, in the body of our sendNotification function, make the API call to Courier:

export const sendNotification = inngest.createFunction(
    { name: "Puppygram Send Notification" },
    { cron: "* * * * *" },
    async () => {       
        // get random dog photo
        const randomIndex = Math.floor(Math.random() * doggos.length)
        const image = doggos[randomIndex]
        // send a notification with the URL to the random image
        await courier.send({
            message: {
                to: {
                   user_id: process.env.NEXT_PUBLIC_COURIER_USER
                },
               content: {
                    title: "",
                    body: "Meet my fluffy and playful partner in crime, always ready for some mischief! :heart_eyes: :paw_prints: #AdorableTroublemaker"
                },
                data: {
                    image_url: `https://random.dog/${image}`
                },
                routing: {
                   method: "single",
                    channels: [
                       "inbox"
                    ]
                }
            }
        })
    }
)


You’re done! Now, all you need to do is deploy this web app (there are many options for deploying a Next.js application) and deploy your serverless function to Inngest. Make sure your web application is fully deployed and live before you deploy the function to Inngest.

Now, let’s turn our attention to building the iOS app.

Creating an iOS App

In order to build an iOS app, you’ll need to install Xcode. I’ll wait…

< 2 hours later >

Hey, welcome back! Ok, now that you’ve downloaded several gigabytes of IDE, let's start building an iOS app!

Create a New App

  • Create a new Project, select "iOS" and "App," and click "Next".
  • Give it the name "Puppygram", make sure "SwiftUI" is selected, and click "Next".
  • Create a new folder for your project and then click "Create".

Install the Courier iOS SDK

Now that you've created your project, let's install the Courier iOS SDK.

Select the "Puppygram" project in the top left of your application nav and click "Package Dependencies" on the right. Click the "+" sign at the bottom and you'll be prompted to add a dependency. Paste this link in:

https://github.com/trycourier/courier-ios


Click "Add Package" to add this to your project. That's it! You now have the power of Courier at your fingertips.

Building the View

For the purpose of this blog post, we're going to build a single-screen app that has a list view that updates in real time to display the notifications we are sending the app.

In your project, open up the ContentView file. At the top of the file, import the Courier iOS SDK:

1import Courier_iOS


Underneath the ContentView declaration, create a messages instance variable to hold the Inbox messages that we receive from Courier:

1struct ContentView: View {2    @State private var messages: [InboxMessage] = []3    var body: some View {


Immediately following the .padding() call, add an onAppear event handler with the following code.

1.padding()2.onAppear{3  Task {4    try await5      // Sign-in to Courier6      Courier.shared.signIn(accessToken:"<JWT TOKEN>",clientKey:"<COURIER_CLIENT_KEY>",userId:"<NEXT_PUBLIC_COURIER_USER>")7      // Add an Inbox listener8      Courier.shared.addInboxListener(9        onInitialLoad: { },10        onError: { error in },11        onMessagesChanged: { messages, unreadMessageCount, totalMessageCount, canPaginate in12          // update the messages array when new messages come in13          self.messages = messages14        }15      )16  }17}


This code signs the user into Courier and wires up an event handler to update the messages instance variable when new messages come in. In order to get the Courier.shared.signIn call working properly, you need to replace those 3 values. Let's go one by one.

Authenticating the iOS SDK

Replace <NEXT_PUBLIC_COURIER_USER> with the to value you are using in your web app when you send a notification using Courier. In our case, the value is puppygram.

Replace<COURIER_CLIENT_KEY> with the value of the Courier Public Key for your Courier Inbox Provider. This key is completely safe to use on the client, in both web and mobile apps.

Finally, we need to set the accessToken property. In order to do this safely, it is recommended that developers generate JWTs for their users that specify scope and expiration information. For the purposes of this tutorial, we're going to use the Courier API to generate a JWT on the command line. Open up a terminal, and run the following command substituting your values for <NEXT_PUBLIC_COURIER_USER> and <COURIER_AUTH_TOKEN>:

1curl --request POST \2     --url https://api.courier.com/auth/issue-token \3     --header 'Accept: application/json' \4     --header 'Authorization: Bearer <COURIER_AUTH_TOKEN>' \5     --header 'Content-Type: application/json' \6     --data '7{8  "scope": "user_id:<NEXT_PUBLIC_COURIER_USER> read:messages",9  "expires_in": "2 days"10}11'


You'll get back some JSON that looks like this:

1{"token":"eyzzz"}


Copy the value of the token property and paste it into the authToken parameter for Courier.shared.signIn. You are totally authenticated!

Displaying the Messages

Now that we're authenticated, the last step for our app is to display the messages. Delete the code below:

1Image(systemName: "globe")2  .imageScale(.large)3  .foregroundColor(.accentColor)4Text("Hello, world!")


Replace it with this:

1List {2  ForEach(messages, id: \.self) { message in3    AsyncImage(url: URL(string: message.imageUrl)){ image in4      image.resizable()5    } placeholder: {6      ProgressView()7    }8    .frame(width: 300, height: 300)9    .clipShape(RoundedRectangle(cornerRadius: 25))              10    VStack {11      Text(message.subtitle ?? "")12    }13  }14}


This is the simplest way to define a List and the elements inside of it. For now, we're just going to just display the image (passed in via the custom data field of the REST API call) and the image description which we are passing in the body field of the REST API call.

Important Note: In the iOS SDK, the subtitle property of the message object maps to the value of body in the REST API call.

Now, if you try to build this it will fail. This is because we need to tell the iOS SDK that we're passing in a custom field in the REST API call. You can do this by extending the InboxMessage class. Just paste this code at the top of your file, just below the imports:

1extension InboxMessage {2    var imageUrl: String {3        get {4            return (data?["image_url"] as? String) ?? ""5        }6    }7}


If all goes well, you can press "play" and you should see something like this running in the simulator.

Wrapping Things Up

Okay, we covered a LOT of territory building an Instagram clone for cute dogs. We learned how to:

  • Create a fresh Next.js web application
  • Configure Courier to send Inbox (in-app) notifications
  • Fetch random pictures of cute dogs using the random.dog API
  • Use Inngest to send a new dog notification to our Inbox every minute
  • Create a fresh iOS app
  • Use the Courier iOS SDK to custom render notifications

With a little UI polish, you can render the data and end up with an app that looks and feels like this:

Whether you're building Kittygram, Bunnygram, or a modern application inbox, Courier Inbox, provides a flexible set of APIs and UI components to help you build exactly what you need. And don't forget, while we focused on iOS, Inbox UI components are available for both web and Android applications too.

Enjoy!

IOS SDK Next.js Web application

Published at DZone with permission of Carter Rabasa. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Cookies Revisited: A Networking Solution for Third-Party Cookies
  • Scaling in Practice: Caching and Rate-Limiting With Redis and Next.js
  • Efficiently Migrating From Jest to Vitest in a Next.js Project
  • Serverless Sign-In Solution Based on Next.js on CloudFare

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!