aboutsummaryrefslogtreecommitdiffhomepage
path: root/Example/Messaging/App/iOS/AppDelegate.swift
blob: 89a5120d27e7d447c8e4e6054031a85fc886e3ef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
 * Copyright 2017 Google
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import UIKit
import FirebaseCore
import FirebaseMessaging
import UserNotifications

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

  var window: UIWindow?

  static let isWithinUnitTest: Bool = {
    if let testClass = NSClassFromString("XCTestCase") {
      return true
    } else {
      return false
    }
  }()

  static var hasPresentedInvalidServiceInfoPlistAlert = false

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    guard !AppDelegate.isWithinUnitTest else {
      // During unit tests, we don't want to initialize Firebase, since by default we want to able
      // to run unit tests without requiring a non-dummy GoogleService-Info.plist file
      return true
    }

    guard SampleAppUtilities.appContainsRealServiceInfoPlist() else {
      // We can't run because the GoogleService-Info.plist file is likely the dummy file which needs
      // to be replaced with a real one, or somehow the file has been removed from the app bundle.
      // See: https://github.com/firebase/firebase-ios-sdk/
      // We'll present a friendly alert when the app becomes active.
      return true
    }

    FirebaseApp.configure()
    Messaging.messaging().delegate = self
    Messaging.messaging().shouldEstablishDirectChannel = true
    // Just for logging to the console when we establish/tear down our socket connection.
    listenForDirectChannelStateChanges();

    NotificationsController.configure()

    if #available(iOS 8.0, *) {
      // Always register for remote notifications. This will not show a prompt to the user, as by
      // default it will provision silent notifications. We can use UNUserNotificationCenter to
      // request authorization for user-facing notifications.
      application.registerForRemoteNotifications()
    } else {
      // iOS 7 didn't differentiate between user-facing and other notifications, so we should just
      // register for remote notifications
      NotificationsController.shared.registerForUserFacingNotificationsFor(application)
    }
    return true
  }

  func application(_ application: UIApplication,
                   didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    print("APNS Token: \(deviceToken.hexByteString)")
    NotificationCenter.default.post(name: APNSTokenReceivedNotification, object: nil)
    if #available(iOS 8.0, *) {
    } else {
      // On iOS 7, receiving a device token also means our user notifications were granted, so fire
      // the notification to update our user notifications UI
      NotificationCenter.default.post(name: UserNotificationsChangedNotification, object: nil)
    }
  }

  func application(_ application: UIApplication,
                   didRegister notificationSettings: UIUserNotificationSettings) {
    NotificationCenter.default.post(name: UserNotificationsChangedNotification, object: nil)
  }

  func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    print("application:didReceiveRemoteNotification:fetchCompletionHandler: called, with notification:")
    print("\(userInfo.jsonString ?? "{}")")
    completionHandler(.newData)
  }

  func applicationDidBecomeActive(_ application: UIApplication) {
    // If the app didn't start property due to an invalid GoogleService-Info.plist file, show an
    // alert to the developer.
    if !SampleAppUtilities.appContainsRealServiceInfoPlist() &&
       !AppDelegate.hasPresentedInvalidServiceInfoPlistAlert {
      if let vc = window?.rootViewController {
        SampleAppUtilities.presentAlertForInvalidServiceInfoPlistFrom(vc)
        AppDelegate.hasPresentedInvalidServiceInfoPlistAlert = true
      }
    }
  }
}

extension AppDelegate: MessagingDelegate {
  // FCM tokens are always provided here. It is called generally during app start, but may be called
  // more than once, if the token is invalidated or updated. This is the right spot to upload this
  // token to your application server, or to subscribe to any topics.
  func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
    if let token = Messaging.messaging().fcmToken {
      print("FCM Token: \(token)")
    } else {
      print("FCM Token: nil")
    }
  }

  // Direct channel data messages are delivered here, on iOS 10.0+.
  // The `shouldEstablishDirectChannel` property should be be set to |true| before data messages can
  // arrive.
  func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
    // Convert to pretty-print JSON
    guard let prettyPrinted = remoteMessage.appData.jsonString else {
      print("Received direct channel message, but could not parse as JSON: \(remoteMessage.appData)")
      return
    }
    print("Received direct channel message:\n\(prettyPrinted)")
  }
}

extension AppDelegate {
    func listenForDirectChannelStateChanges() {
        NotificationCenter.default.addObserver(self, selector: #selector(onMessagingDirectChannelStateChanged(_:)), name: .MessagingConnectionStateChanged, object: nil)
    }

    func onMessagingDirectChannelStateChanged(_ notification: Notification) {
        print("FCM Direct Channel Established: \(Messaging.messaging().isDirectChannelEstablished)")
    }
}

extension Dictionary {
  /// Utility method for printing Dictionaries as pretty-printed JSON.
  var jsonString: String? {
    if let jsonData = try? JSONSerialization.data(withJSONObject: self, options: [.prettyPrinted]),
       let jsonString = String(data: jsonData, encoding: .utf8) {
      return jsonString
    }
    return nil
  }
}