quickstart-ios
quickstart-ios copied to clipboard
How can I retrieve the APNs before the FCM token in Firebase in Xcode specifically?
Before encountering the error, I was using Xcode which had Firebase installed. At around 12pm today, I began encountering an error that looked like this
APNS device token not set before retrieving FCM Token for Sender ID 'XXXXXXXXX'.Be sure to re-retrieve the FCM token once the APNS device token is set.
Prior to 12pm, I did not encounter the error at all
After tinkering with it for 9 hours, I tried moving methods around, looked up many websites to find the exact error, debugging, and even went to YouTube of all places, but can't figure out where the error is coming from.
If it helps, I am currently using Xcode 16 with Firebase version 10.27.
Here's the code for anyone who thinks they can find the answer
This is in my AppDelegate from constant debugging
For extra context:
I have the app running on my iPhone 15 Pro Max and was running well before the error I have Background Modes enabled (Background fetch, processing, remote notifications) In my Firebase Console, I have the APN key in my Cloud Messaging section I have added the app to my Firebase server I have the Google Info.plist in my code I have the app registred for App Attest (AppCheck) and DeviceCheck
`import UIKit import UserNotifications import Firebase import FirebaseMessaging import TabularData import FirebaseInAppMessaging
class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate, MessagingDelegate {
var window: UIWindow?
// Configure Firebase when the app launches
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
print("1st launch")
// Set up push notification permissions
registerForPushNotifications(application)
// Set the messaging delegate
print("2nd launch")
Messaging.messaging().delegate = self
return true
}
// Register for push notifications
func registerForPushNotifications(_ application: UIApplication) {
print("3rd launch")
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(options: authOptions) { granted, error in
print("Permission granted: \(granted)")
}
application.registerForRemoteNotifications()
}
// Called when APNs has assigned a device token to the app
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
// Forward the APNs token to Firebase
Messaging.messaging().apnsToken = deviceToken
print("APNs Token registered: \(deviceToken)")
}
// Handle any errors when trying to register for remote notifications
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Failed to register for remote notifications: \(error)")
}
// Called when Firebase messaging gets a new FCM token
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
guard let fcmToken = fcmToken else { return }
print("Firebase FCM token: \(fcmToken)")
// Optionally, send this token to your server
// sendFCMTokenToServer(fcmToken)
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
handleDailyReportTrigger()
completionHandler([.banner, .sound, .badge])
}
func scheduleDailyReport() {
print("Scheduling the daily report in scheduleDailyReport...")
// Schedule the generation of the report
var dateComponents = DateComponents()
dateComponents.hour = 16 // Adjust to your preferred time
dateComponents.minute = 10
print("At the UserNotificationCenter")
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
let request = UNNotificationRequest(identifier: "DailyReport", content: UNMutableNotificationContent(), trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("Error scheduling daily report: \(error.localizedDescription)")
} else {
print("Daily report scheduled successfully.")
}
}
// Automatically generate and display the report when the scheduled time is reached
generateDailyReport()
}
private func generateDailyReport() {
let startOfDay = Calendar.current.startOfDay(for: Date())
let endOfDay = Calendar.current.date(byAdding: .day, value: 1, to: startOfDay)!
let historyRef = Database.database().reference().child("history")
let query = historyRef.queryOrdered(byChild: "timestamp")
.queryStarting(atValue: startOfDay.timeIntervalSince1970)
.queryEnding(atValue: endOfDay.timeIntervalSince1970)
query.observeSingleEvent(of: .value) { snapshot in
var services: [[String: Any]] = []
for child in snapshot.children {
if let childSnapshot = child as? DataSnapshot, let serviceData = childSnapshot.value as? [String: Any] {
services.append(serviceData)
}
}
// Only proceed with CSV if we have data
if !services.isEmpty {
let csvURL = self.createCSVReport(from: services)
self.displayCSVFile(at: csvURL)
// Optionally delete the history
historyRef.removeValue { error, _ in
if let error = error {
print("Error deleting history: \(error.localizedDescription)")
} else {
print("History deleted successfully")
}
}
} else {
print("No services found for the day.")
}
} withCancel: { error in
print("Error fetching history data: \(error.localizedDescription)")
}
}
private func createCSVReport(from services: [[String: Any]]) -> URL {
var dataFrame = DataFrame()
// Create columns
let customerNames = Column(name: "Customer Name", contents: services.map { $0["customerName"] as? String ?? "N/A" })
let addresses = Column(name: "Address", contents: services.map { $0["address"] as? String ?? "N/A" })
let phoneNumbers = Column(name: "Phone Number", contents: services.map { $0["phoneNumber"] as? String ?? "N/A" })
let driverNames = Column(name: "Driver Name", contents: services.map { $0["driverName"] as? String ?? "N/A" })
let statuses = Column(name: "Status", contents: services.map { $0["status"] as? String ?? "N/A" })
let timestamps = Column(name: "Timestamp", contents: services.map { service in
let timestamp = service["timestamp"] as? TimeInterval ?? 0
let date = Date(timeIntervalSince1970: timestamp)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
return dateFormatter.string(from: date)
})
// Add columns to the DataFrame
dataFrame.append(column: customerNames)
dataFrame.append(column: addresses)
dataFrame.append(column: phoneNumbers)
dataFrame.append(column: driverNames)
dataFrame.append(column: statuses)
dataFrame.append(column: timestamps)
// Export DataFrame to CSV format
let csvData = try! dataFrame.csvRepresentation()
// Save to directory
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = documentsDirectory.appendingPathComponent("Service_Report_\(Date().toFormattedString()).csv")
try! csvData.write(to: fileURL)
print("CSV file created at: \(fileURL.path)")
return fileURL
}
private func displayCSVFile(at url: URL) {
let activityViewController = UIActivityViewController(activityItems: [url], applicationActivities: nil)
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
windowScene.windows.first?.rootViewController?.present(activityViewController, animated: true, completion: nil)
}
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
print("UserInfo: \(userInfo)")
handleDailyReportTrigger()
completionHandler()
}
private func handleDailyReportTrigger() {
let viewModel = AdminDashboardViewModel() // Access your view model here
viewModel.generateDailyReport()
}
}
@MainActor class NotificationManager: ObservableObject{ @Published private(set) var hasPermission = false static let shared = NotificationManager()
init() {
Task{
await getAuthStatus()
}
}
func request() async{
do {
try await UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound])
await getAuthStatus()
} catch{
print(error)
}
}
func getAuthStatus() async {
let status = await UNUserNotificationCenter.current().notificationSettings()
switch status.authorizationStatus {
case .authorized, .ephemeral, .provisional:
hasPermission = true
default:
hasPermission = false
}
}
func postNewPickupRequestNotification() {
NotificationCenter.default.post(name: .newPickupRequest, object: nil)
}
@MainActor
func observeNewPickupRequestNotification(observer: Any, selector: Selector) {
NotificationCenter.default.addObserver(observer, selector: selector, name: .newPickupRequest, object: nil)
}
}
extension Notification.Name { static let newPickupRequest = Notification.Name("newPickupRequest") }
@MainActor class NotificationHandler: NSObject, ObservableObject { @Published var newRequestAlert = false
override init() {
super.init()
Task { @MainActor in
NotificationManager.shared.observeNewPickupRequestNotification(observer: self, selector: #selector(handleNewPickupRequest))
}
}
@objc private func handleNewPickupRequest() {
newRequestAlert = true
scheduleLocalNotification(title: "New Pickup Request", body: "A new pickup request has been added.", timeInterval: 1)
print("handleNewPickupRequest is here!")
// Dismiss alert after some time (e.g., 3 seconds)
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.newRequestAlert = false
}
}
private func scheduleLocalNotification(title: String, body: String, timeInterval: TimeInterval) {
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = .default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeInterval, repeats: false)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request)
}
}
class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
print(userInfo)
completionHandler()
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.banner, .sound, .badge])
}
} `