7.
Expanding the Application
Written by Scott Grosch
Now that you’ve got a database up and running, you need to tell your app how to connect to it. As you saw in the previous chapter, “Server Side Pushes,” Vapor will run a local server for you at http://192.168.1.1:8080 (change with your own IP address).
This is the URL that your app will need to talk to if you successfully registered for push notifications. Of course, remember to substitute your IP address in the URL.
Setting the team and bundle identifier
If you take a look at the starter project for this chapter, you’ll see that it’s a standard app that uses push notifications, utilizing the code that you used in previous chapters. Before you can use the project, however, you’ll have to configure the target so that the certificates are created for you. To do so:
- Select PushNotifications from the Project navigator (⌘ + 1).
- Select the PushNotifications target.
- Open the Signing & Capabilities tab.
- Choose your Team.
- Change the Bundle Identifier.
Follow steps 1–4 as shown below:
Remember, also, to turn on the Push Notifications capability as discussed in Chapter 4, “Xcode Project Setup.”
Updating the server
In your Xcode project, you can now modify AppDelegate.swift to contact the endpoint you just set up to register your device token. Return to your Xcode project. Modify application(_:didRegisterForRemoteNotifications WithDeviceToken:) method.
After you print the token, add a few lines to call your new web service:
// 1
let url = URL(string: "http://192.168.1.1:8080/api/token")!
// 2
var obj: [String: Any] = [
"token": token,
"debug": false
]
// 3
#if DEBUG
obj["debug"] = true
#endif
// 4
var request = URLRequest(url: url)
request.addValue("application/json",
forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = try! JSONSerialization
.data(withJSONObject: obj)
// 5
URLSession.shared.dataTask(with: request).resume()
Here’s what’s happening in the code above:
- Remember to update the IP address with the IP of your Mac that you discovered in Chapter 6, “Server Side Pushes.”
- The POST method that you configured to recieve your token expects a JSON object, and it will always need to know the token, so you start with that. Use the
Anytype as the value of the dictionary because you’ll pass along both strings and Booleans. - Set
debugto true if you’re running directly via Xcode. - Set up an HTTP POST request with the request body you built previously.
- Finally, send along the request.
Notice that you’re not checking the status of the request. If the registration with your website or database were to fail for any reason, you wouldn’t tell your end user this as there isn’t anything they can do about it anyway. Hopefully, the next time they run your app, you would’ve fixed the server-side issue and the registration will complete successfully.
If you try to run this now, you’ll get errors as Apple blocks the URL due to App Transport Security, or ATS. Since this is only a development example, you can disable that security measurment, but for a production app, you’d probably never want to do this:
-
Edit the Info.plist file.
-
Right-click in the empty whitespace and choose Add Row.
-
Set the key to be App Transport Security Settings.
-
Expand your newly created App Transport Security Settings using the left triangle.
-
Right-click on App Transport Security Settings and choose Add Row.
-
Set the key to be Allow Arbitrary Loads and the value to be YES.
Build and run your app. Put the app into the background by going to your home screen or locking your phone. At this point, if you run the sendPushes.php script that you created in Chapter 6, “Server Side Pushes,” you should get a push notification!
Note: For this to work, you need to make sure an instance of your Vapor server is running and configured to run on your IP address, as well as make sure your database is running. You also need to set up the sendPushes.php script to use your APNs token. This is all described in Chapter 6, “Server Side Pushes.”
Extending AppDelegate
You can probably already see how the push notification code is going to be almost exactly the same in every project you create. Do a little cleanup by moving this common code to an extension. Create a new file in your Xcode project called ApnsUploads.swift and then move your notification code over.
When you move this code, you’ll also add some additional lines at the end to display the body of the JSON request in a “pretty” format that’s easy for you to read. Your ApnsUploads.swift file should look like this:
import UIKit
import UserNotifications
extension AppDelegate {
func registerForPushNotifications(application: UIApplication) {
let center = UNUserNotificationCenter.current()
center.requestAuthorization(
options: [.badge, .sound, .alert]) { granted, _ in
guard granted else { return }
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
}
}
func sendPushNotificationDetails(to urlString: String,
using deviceToken: Data) {
guard let url = URL(string: urlString) else {
fatalError("Invalid URL string")
}
let token = deviceToken.reduce("") {
$0 + String(format: "%02x", $1)
}
var obj: [String: Any] = [
"token": token,
"debug": false
]
#if DEBUG
obj["debug"] = true
#endif
var request = URLRequest(url: url)
request.addValue("application/json",
forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = try! JSONSerialization.data(
withJSONObject: obj)
#if DEBUG
print("Device Token: \(token)")
let pretty = try! JSONSerialization.data(
withJSONObject: obj,
options: .prettyPrinted)
print(String(data: pretty, encoding: .utf8)!)
#endif
URLSession.shared.dataTask(with: request).resume()
}
}
As you add more items to the data that you send during registration, these “pretty” lines become a life saver when debugging. You might, for example, want to store the users’ language preferences or the version of the app they are running.
Now, you have a nice extension that you can simply copy into any app where you want to use push notifications. Once that’s done, your AppDelegate.swift’s extension becomes nice and clean, and should look like this:
extension AppDelegate: UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions:
[UIApplication.LaunchOptionsKey: Any]?)
-> Bool {
registerForPushNotifications(application: application)
return true
}
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
sendPushNotificationDetails(
to: "http://192.168.1.1:8080/api/token",
using: deviceToken
)
}
}
Key points
- Once you have your database established, you need to tell your app how to connect to it. Vapor will allow you to run a server written with Swift.
- Take the time to add some additional lines at the end of notification registration to display the body of the JSON request in a “pretty,” easy-to-read format, which can help in the future with debugging.
Where to go from here?
And there you have it! You’ve successfully built an API that saves device tokens and an app that consumes that API. This is the basic skeleton you will build upon when using push notifications. In Chapter 8, “Handling Common Scenarios,” you will start handling common push notification scenarios, such as displaying a notification while the app is in the foreground… so keep reading!