Home iOS & Swift Books Push Notifications by Tutorials

7
Expanding the Application Written by Scott Grosch

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

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:

  1. Select PushNotifications from the Project navigator ( + 1).
  2. Select the PushNotifications target.
  3. Open the Signing & Capabilities tab.
  4. Choose your Team.
  5. Change the Bundle Identifier.

Follow steps 1–4 as shown below:

Updating the server

Token details

You’ll want to be able to pass appropriate details to your web service which describes the user’s push notification registration.

import Foundation

struct TokenDetails {
  let token: String
  var debug = false
}
private let encoder = JSONEncoder()
extension TokenDetails: Encodable {
  private enum CodingKeys: CodingKey {
    case token, debug
  }
}
func encoded() -> Data {
  return try! encoder.encode(self)
}
extension TokenDetails: CustomStringConvertible {
  var description: String {
    return String(data: encoded(), encoding: .utf8) ?? "Invalid token"
  }
}
init(token: Data) {
  self.token = token.reduce("") { $0 + String(format: "%02x", $1) }
  self.encoder.outputFormatting = .prettyPrinted
}

Sending the tokens

Now that you have a way to generate the details which will be sent, update the application(_:didRegisterForRemoteNotificationsWithDeviceToken:) method in AppDelegate.swift.

guard let url = URL(string: "http://192.168.1.1:8080/api/token") else {
  fatalError("Invalid URL string")
}
// 2
var details = TokenDetails(token: deviceToken)

// 3
#if DEBUG
details.debug.toggle()
print(details)
#endif
var request = URLRequest(url: url)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = details.encoded()

URLSession.shared.dataTask(with: request).resume()

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 Swift file in your Xcode project called ApnsUploads.swift and then move your notification code over.

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")
    }

    var details = TokenDetails(token: deviceToken)

    #if DEBUG
    details.debug.toggle()
    print(details)
    #endif

    var request = URLRequest(url: url)
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.httpMethod = "POST"
    request.httpBody = details.encoded()

    URLSession.shared.dataTask(with: request).resume()
  }
}
import SwiftUI

class AppDelegate: NSObject, 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",
      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!

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.

Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:

© 2021 Razeware LLC

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.