基于Firebase平台开发(九) —— 使用Firebase Cloud Messaging进行Push Notification的发送和接收(二)

版本记录

版本号 时间
V1.0 2021.04.10 星期六

前言

Firebase是一家实时后端数据库创业公司,它能帮助开发者很快的写出Web端和移动端的应用。自2014年10月Google收购Firebase以来,用户可以在更方便地使用Firebase的同时,结合Google的云服务。Firebase能让你的App从零到一。也就是说它可以帮助手机以及网页应用的开发者轻松构建App。通过Firebase背后负载的框架就可以简单地开发一个App,无需服务器以及基础设施。接下来几篇我们就一起看一下基于Firebase平台的开发。感兴趣的看下面几篇文章。
1. 基于Firebase平台开发(一) —— 基于ML Kit的iOS图片中文字的识别(一)
2. 基于Firebase平台开发(二) —— 基于ML Kit的iOS图片中文字的识别(二)
3. 基于Firebase平台开发(三) —— Firebase基本使用简介(一)
4. 基于Firebase平台开发(四) —— Firebase基本使用简介(二)
5. 基于Firebase平台开发(五) —— Firebase基本使用简介(三)
6. 基于Firebase平台开发(六) —— 基于Firebase Analytics的App使用率的跟踪(一)
7. 基于Firebase平台开发(七) —— iOS的A/B Test(一)
8. 基于Firebase平台开发(八) —— 使用Firebase Cloud Messaging进行Push Notification的发送和接收(一)

源码

1. Swift

首先看下工程组织结构

下面就是源码了

1. AppMain.swift
import SwiftUI

@main
struct AppMain: App {
  @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

  var body: some Scene {
    WindowGroup {
      AppTabView()
        .accentColor(Color("rw-green"))
    }
  }
}
2. AppDelegate.swift
import UIKit
import Firebase
import FirebaseMessaging
import FirebaseAnalytics

class AppDelegate: NSObject, UIApplicationDelegate {
  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
    FirebaseApp.configure()
    FirebaseConfiguration.shared.setLoggerLevel(.min)

    UNUserNotificationCenter.current().delegate = self
    let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
    UNUserNotificationCenter.current().requestAuthorization(
      options: authOptions) { _, _ in }
    application.registerForRemoteNotifications()

    Messaging.messaging().delegate = self

    return true
  }
}

extension AppDelegate: UNUserNotificationCenterDelegate {
  func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    willPresent notification: UNNotification,
    withCompletionHandler completionHandler:
    @escaping (UNNotificationPresentationOptions) -> Void
  ) {
    process(notification)
    completionHandler([[.banner, .sound]])
  }

  func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    didReceive response: UNNotificationResponse,
    withCompletionHandler completionHandler: @escaping () -> Void
  ) {
    process(response.notification)
    completionHandler()
  }

  func application(
    _ application: UIApplication,
    didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
  ) {
    Messaging.messaging().apnsToken = deviceToken
  }

  private func process(_ notification: UNNotification) {
    let userInfo = notification.request.content.userInfo
    UIApplication.shared.applicationIconBadgeNumber = 0
    if let newsTitle = userInfo["newsTitle"] as? String,
      let newsBody = userInfo["newsBody"] as? String {
      let newsItem = NewsItem(title: newsTitle, body: newsBody, date: Date())
      NewsModel.shared.add([newsItem])
      Analytics.logEvent("NEWS_ITEM_PROCESSED", parameters: nil)
    }
    Messaging.messaging().appDidReceiveMessage(userInfo)
    Analytics.logEvent("NOTIFICATION_PROCESSED", parameters: nil)
  }
}

extension AppDelegate: MessagingDelegate {
  func messaging(
    _ messaging: Messaging,
    didReceiveRegistrationToken fcmToken: String?
  ) {
    let tokenDict = ["token": fcmToken ?? ""]
    NotificationCenter.default.post(
      name: Notification.Name("FCMToken"),
      object: nil,
      userInfo: tokenDict)
  }
}
3. NewsModel.swift
import Foundation

class NewsModel: ObservableObject {
  @Published private(set) var newsItems: [NewsItem] = []

  func add(_ items: [NewsItem]) {
    var tempItems = newsItems
    tempItems.append(contentsOf: items)
    newsItems = tempItems.sorted {
      $0.date > $1.date
    }
  }
}

extension NewsModel {
  static let shared = mockModel()
  private static func mockModel() -> NewsModel {
    let newsModel = NewsModel()
    let decoder = JSONDecoder()
    decoder.dateDecodingStrategy = .iso8601
    guard
      let url = Bundle.main.url(forResource: "MockNewsItems", withExtension: "json"),
      let data = try? Data(contentsOf: url),
      let newsItems = try? decoder.decode([NewsItem].self, from: data)
    else {
      return newsModel
    }
    newsModel.add(newsItems)
    return newsModel
  }
}
4. NewsItem.swift
import Foundation

struct NewsItem: Identifiable, Codable {
  var id = UUID()
  let title: String
  let body: String
  let date: Date

  private enum CodingKeys: String, CodingKey {
    case title
    case body
    case date
  }
}
5. TopicsModel.swift
import SwiftUI
import FirebaseMessaging

class TopicsModel: ObservableObject {
  static let familyKey = "family"
  static let petsKey = "pets"
  @AppStorage(TopicsModel.familyKey) var family = false {
    didSet {
      updateSubscription(for: TopicsModel.familyKey, subscribed: family)
    }
  }
  @AppStorage(TopicsModel.petsKey) var pets = false {
    didSet {
      updateSubscription(for: TopicsModel.petsKey, subscribed: pets)
    }
  }

  private func updateSubscription(for topic: String, subscribed: Bool) {
    if subscribed {
      subscribe(to: topic)
    } else {
      unsubscribe(from: topic)
    }
  }

  private func subscribe(to topic: String) {
    Messaging.messaging().subscribe(toTopic: topic)
  }

  private func unsubscribe(from topic: String) {
    Messaging.messaging().unsubscribe(fromTopic: topic)
  }
}
6. AppTabView.swift
import SwiftUI

struct AppTabView: View {
  var body: some View {
    TabView {
      NavigationView {
        NewsListView(newsModel: NewsModel.shared)
      }
      .tabItem {
        Image(systemName: "newspaper")
        Text("News")
      }
      NavigationView {
        TopicsView()
      }
      .tabItem {
        Image(systemName: "rectangle.3.offgrid.bubble.left")
        Text("Topics")
      }
    }
  }
}

struct AppTabView_Previews: PreviewProvider {
  static var previews: some View {
    AppTabView()
  }
}
7. NewsListView.swift
import SwiftUI

struct NewsListView: View {
  @ObservedObject var newsModel: NewsModel

  var body: some View {
    List(newsModel.newsItems) { newsItem in
      NewsItemView(newsItem: newsItem)
    }
    .listStyle(InsetGroupedListStyle())
    .navigationTitle("Good News")
  }
}

struct NewsListView_Previews: PreviewProvider {
  static var previews: some View {
    NavigationView {
      NewsListView(newsModel: NewsModel.shared)
    }
  }
}
8. NewsItemView.swift
import SwiftUI

struct NewsItemView: View {
  let newsItem: NewsItem
  var body: some View {
    VStack(alignment: .leading) {
      Text(newsItem.title)
        .font(.title2)
        .bold()
      Text(newsItem.body)
        .font(.body)
      Text(newsItem.date, formatter: dateFormatter)
        .font(.caption2)
        .italic()
    }
  }

  private var dateFormatter: DateFormatter {
    let formatter = DateFormatter()
    formatter.dateStyle = .short
    formatter.timeStyle = .short
    return formatter
  }
}

struct NewsItemRowView_Previews: PreviewProvider {
  static var previews: some View {
    NewsItemView(newsItem: NewsItem(title: "News Title", body: "News body, with more information", date: Date()))
  }
}
9. TopicsView.swift
import SwiftUI

struct TopicsView: View {
  @ObservedObject var topicsModel = TopicsModel()

  var body: some View {
    Form {
      Section(header: Text("Select the topics you would like to receive notifications for")) {
        Toggle("Family", isOn: $topicsModel.family)
        Toggle("Pets", isOn: $topicsModel.pets)
      }
    }
    .navigationTitle("Topics")
  }
}

struct SubscriptionsView_Previews: PreviewProvider {
  static var previews: some View {
    NavigationView {
      TopicsView()
    }
  }
}
10. NotificationService.swift
import UserNotifications
import FirebaseMessaging

class NotificationService: UNNotificationServiceExtension {
  var contentHandler: ((UNNotificationContent) -> Void)?
  var bestAttemptContent: UNMutableNotificationContent?

  override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
    self.contentHandler = contentHandler
    bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContent
    guard let bestAttemptContent = bestAttemptContent else { return }
    FIRMessagingExtensionHelper().populateNotificationContent(
      bestAttemptContent,
      withContentHandler: contentHandler)
  }

  override func serviceExtensionTimeWillExpire() {
    // Called just before the extension will be terminated by the system.
    // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
    if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
      contentHandler(bestAttemptContent)
    }
  }
}

后记

本篇主要讲述了使用Firebase Cloud Messaging进行Push Notification的发送和接收,感兴趣的给个赞或者关注~~~

你可能感兴趣的:(基于Firebase平台开发(九) —— 使用Firebase Cloud Messaging进行Push Notification的发送和接收(二))