Getting started with Vapor and push notifications
You will need Xcode 10+ and Vapor 3.0. Some understanding of Vapor will be helpful.
Introduction
In this tutorial, we’re going to look into how we setup Vapor to send push notifications using Pusher Beams. We will build a simple iOS application that registers an interest. Also, a Vapor server that handles a simple GET and POST request to send a push notification to our device.
Prerequisites
- A basic understanding of Vapor - please complete my “Getting started with Vapor” Part One and Part Two.
- Xcode 10+
- MacOS
- Vapor 3.0 - Install instructions here.
- An iOS device for testing notifications.
- An understanding of iOS development and Xcode environment.
- Cocoapods - Install instructions here.
- A REST client such as Postman and a basic understanding of how to use it.
Setting up our Vapor backend
Creating our Vapor project
From terminal and your working directory enter the following command to create your Vapor application.
$ vapor new PizzaPush
$ cd PizzaPush
Now we will build your application before opening it in Xcode. Remember your first build could take some time to complete.
$ vapor build
Now open your project in Xcode. Remember to open using Xcode you must run the following command in terminal:
$ vapor xcode -y
Also remember to change the run scheme to be the “run” scheme in case it is not already set to this.
Adding our dependency
We need to add the Pusher Beams package to our Swift Package Manager (SPM). Start by opening the Package.swift
file from the project navigator. This is our dependencies management file, a bit like our Podfile
or Cartfile
in iOS development.
Below the line that gives us our Vapor package dependency:
.package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"),
Add the following to add our Pusher Beams dependency:
.package(url: "git@github.com:pusher/push-notifications-server-swift.git", .branch("master")),
We need to tell SPM what dependencies each target should have we do this by editing targets section in the same file. Find the line:
.target(name: "App", dependencies: ["FluentSQLite", "Vapor"]),
Edit it so that it becomes:
.target(name: "App", dependencies: ["FluentSQLite", "Vapor", "PushNotifications"]),
We’ve added the name of our dependency to the array of dependencies for the app target. For more information on the dependency go here and for more information relating to SPM go here. Your Package.swift
file should now look something like this:
// Package.swift
// swift-tools-version:4.0
import PackageDescription
let package = Package(
name: "PizzaPush",
dependencies: [
// 💧 A server-side Swift web framework.
.package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"),
.package(url: "git@github.com:pusher/push-notifications-server-swift.git", .branch("master")),
// 🔵 Swift ORM (queries, models, relations, etc) built on SQLite 3.
.package(url: "https://github.com/vapor/fluent-sqlite.git", from: "3.0.0")
],
targets: [
.target(name: "App", dependencies: ["FluentSQLite", "Vapor", "PushNotifications"]),
.target(name: "Run", dependencies: ["App"]),
.testTarget(name: "AppTests", dependencies: ["App"])
]
)
Now we need to close Xcode and install the package and reopen it. It’s best practice in Vapor to create and install files outside of our Xcode project using command line tools and then reopen Xcode using the toolbox command. After closing Xcode run the following command in terminal in our working directory.
$ swift build
Reopen Xcode by running:
$ vapor xcode -y
Creating a Pusher Beams instance
Login or create an account to access your dashboard here. Create a new Pusher Beams instance using the dashboard.
Complete step one of the iOS setup guide, we will pick up the remainder later on in this tutorial. Press the X to exit the setup guide and you will be returned to your dashboard for that instance. Scroll to the bottom of this page and you will find your Pusher Beams instance ID and secret key, make note of these you will need them later.
Creating our routes
Open your routes.swift
file and replace the contents with the following:
//../Sources/App/routes.swift
import Vapor
// 1
import PushNotifications
/// Register your application's routes here.
public func routes(_ router: Router) throws {
// 2
router.get("push") { req -> String in
do {
try PushNotificationService.send(message: "Hello, new pizza offers are available!")
return "Push Success"
} catch {
return "Push Failed"
}
}
// 3
router.post(PizzaOffer.self, at: "push/offer/") { req, data -> String in
do {
try PushNotificationService.send(message: "Hello, \(data.pizzaName) has this offer: \(data.pizzaOffer)")
return "Push Success"
} catch {
return "Push Failed"
}
}
}
// 4
struct PizzaOffer: Content {
let pizzaName: String
let pizzaOffer: String
}
//5
class PushNotificationService {
// 6
class func send(message: String) throws {
let pushNotifications = PushNotifications(instanceId: "YOUR_INSTANCE_ID" , secretKey:"YOUR_SECRET_KEY")
let interests = ["pizza"]
let publishRequest = [
"apns": [
"aps": [
"alert": [
"title": "Pizza Offer",
"body": message
]
]
]
]
try pushNotifications.publish(interests, publishRequest, completion: { publishID in
print("Published \(publishID)")
})
}
}
1 - We need to import the new PushNotifications dependency we have just installed.
2 - Here we create a new GET route that is capable of sending a generic push notification using a wrapper we class we have created below.
3 - Here we create a new POST route that is capable of sending a more specific push notification based on the data that is passed in but still using the same wrapper class.
4 - This is the structure that our post request is expecting the data to arrive in.
5 - This is our wrapper class. Here we create an instance of the dependency we have installed using the Pusher Beams instances ID and secret key you created earlier. We only have one interest in our app (which is Pizza). We create a publish request using for APNs using the message that is passed in using the method call and we then publish this using our dependency.
Creating our iOS application
Now that we have created our routes, we need to have a user that has actually registered for notifications and signed up for the pizza interest so we can test out our implementation. We’re going to create a very basic app that doesn’t actually show anything to the user except for the notification on the lock screen.
Project setup
Create a new single view iOS application using Xcode and name it something like PizzaPush-iOS. Once the project is created we need to install the Beams SDK. Open the terminal and go to the working directory of the newly created project and run the following command.
$ pod init
Open the newly created Podfile
and add the following pod:
pod 'PushNotifications'
In the terminal run:
$ pod install
Make sure you close your Xcode project and reopen the newly created Xcode Workspace before continuing. Within your project capabilities make sure you have switched on the Push Notifications capability. Also turn on the Background Modes capability and tick the box for Remote Notifications.
Open your AppDelegate.swift
file and replace its contents with the following. Remembering to replace the instance ID with your own.
import UIKit
import PushNotifications
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let pushNotifications = PushNotifications.shared
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.pushNotifications.start(instanceId: "YOUR_INSTANCE_ID")
self.pushNotifications.registerForRemoteNotifications()
try? self.pushNotifications.subscribe(interest: "pizza")
return true
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
self.pushNotifications.registerDeviceToken(deviceToken)
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
self.pushNotifications.handleNotification(userInfo: userInfo)
}
}
You can now run the application on your iOS device and accept to receive notifications. The SDK will then manage registering our interest in pizza.
Test our integration
Now we can begin testing our integration. If you have closed your Vapor project reopen it in the usual way in Xcode and hit the run button. You should see a message in the console saying it has started and is listening on http://localhost:8080. To begin our first make sure the iPhone is on the lock screen and using your web browser visit: http://localhost:8080/push. You should receive a notification for Pizza Push with a title saying “Offer” and a generic message “Hello, new pizza offers are available!”
Our second test requires us to use Postman (or another REST client) to pass data through. Open postman or your equivalent REST client and set it up so that it looks like this.
If you press the send button you should receive a push notification on your iOS Device that now has the message “Hello, Margherita Pizza has this offer: 50% off”. You could change the name of the pizza or the offer to customize this offer making for more powerful push notifications.
Conclusion
We’ve learnt how to add a dependency the Beams server side SDK as a dependency to Vapor and setup some basic routes that can publish notifications to an iOS app that has registered for the interests. We’ve seen a couple of basic push notification examples that could be customized for different routes and receive different parameters to create different notifications for users.
Now that you have a basic understanding of the setup you should be able to go on and create your own routes that may handle more complex logic and display more complicated push notifications to users.
The source code for the Vapor project can be found here and for the iOS project here.
26 November 2018
by Christopher Batin