שיטות מומלצות לניהול אסימוני רישום של FCM

אם אתם משתמשים בממשקי FCM API כדי ליצור בקשות לשליחה באופן פרוגרמטי, יכול להיות שבמהלך הזמן תגלו שאתם מבזבזים משאבים בשליחת הודעות למכשירים לא פעילים עם טוקנים לא עדכניים של רישום. המצב הזה יכול להשפיע על נתוני מסירת ההודעות שמדווחים במסוף Firebase או על נתונים שמיוצאים אל BigQuery, ויוצג כירידה דרמטית (אבל לא באמת תקפה) בשיעורי המסירה. במדריך הזה מוסבר על כמה אמצעים שאפשר לנקוט כדי להבטיח טירגוט יעיל של הודעות ודיווח תקף על מסירה.

טוקנים של הרשמה שפג תוקפם או שהם לא עדכניים

טוקנים לא עדכניים של רישום הם טוקנים שמשויכים למכשירים לא פעילים שלא התחברו אל FCM במשך יותר מחודש. ככל שעובר הזמן, הסיכוי שהמכשיר יתחבר שוב אל FCM הולך ופוחת. סביר להניח שהודעות שנשלחות באמצעות טוקנים לא פעילים כאלה, וגם הודעות שנשלחות לכל המשתמשים בנושאים מסוימים, לא יגיעו ליעד.

יש כמה סיבות לכך שטוקן הופך ללא פעיל. לדוגמה, יכול להיות שהמכשיר שאליו משויך האסימון אבד, נהרס או אוחסן ונשכח.

ב-Android, כשמגיעים ל-270 ימים של חוסר פעילות בטוקנים לא עדכניים, המערכת של FCM מחשיבה אותם כלא תקפים. אחרי שתוקף הטוקן פג, FCM מסמן אותו כלא תקין ודוחה שליחות אליו. עם זאת, FCM מנפיק טוקן חדש למופע האפליקציה במקרה הנדיר שהמכשיר מתחבר מחדש והאפליקציה נפתחת.

בפלטפורמות אחרות כמו iOS, ‏ FCM מסתמך על שירות הפוש הבסיסי (למשל, APNs), שאין לו תפוגה של טוקנים שמבוססת על חוסר פעילות של 270 ימים. לכן מומלץ לשמור על טוקנים עדכניים ולהסיר טוקנים לא פעילים של רישום.

שיטות מומלצות בסיסיות

יש כמה שיטות בסיסיות שמומלץ לפעול לפיהן בכל אפליקציה שמשתמשת בממשקי FCM API כדי ליצור בקשות שליחה באופן פרוגרמטי. השיטות המומלצות העיקריות הן:

  • מאחזרים את טוקני ההרשמה מ-FCM ומאחסנים אותם בשרת. תפקיד חשוב של השרת הוא לעקוב אחרי הטוקן של כל לקוח ולשמור רשימה מעודכנת של טוקנים פעילים. מומלץ מאוד להטמיע חותמת זמן של הטוקן בקוד ובשרתים, ולעדכן את חותמת הזמן הזו במרווחי זמן קבועים.
  • שמירה על טריות הטוקנים והסרת טוקנים לא עדכניים. בנוסף להסרת טוקנים ש-FCM כבר לא מחשיב כתקפים, כדאי לעקוב אחרי סימנים אחרים לכך שהטוקנים הפכו ללא עדכניים ולהסיר אותם באופן יזום. במדריך הזה מפורטות כמה אפשרויות להשגת המטרה הזו.

אחזור ואחסון של טוקנים של רישום

בהפעלה הראשונית של האפליקציה, FCM SDK יוצר אסימון רישום עבור מופע אפליקציית הלקוח. זהו האסימון שצריך לכלול בבקשות ששולחים מה-API לשליחה ממוקדת, או להוסיף למינויים לנושאים כדי לטרגט נושאים.

מומלץ מאוד שהאפליקציה תאחזר את האסימון הזה בהפעלה הראשונית ותשמור אותו בשרת האפליקציה לצד חותמת זמן. חותמת הזמן הזו צריכה להיות מוטמעת בקוד ובשרתים שלכם, כי היא לא מסופקת לכם על ידי ערכות ה-SDK של FCM.

בנוסף, חשוב לשמור את האסימון בשרת ולעדכן את חותמת הזמן בכל פעם שהוא משתנה, למשל במקרים הבאים:

  • האפליקציה משוחזרת במכשיר חדש
  • המשתמש מסיר את האפליקציה או מתקין אותה מחדש
  • המשתמש מנקה את נתוני האפליקציה
  • האפליקציה חוזרת להיות פעילה אחרי שפג התוקף של האסימון הקיים של FCM

דוגמה: אחסון של טוקנים וחותמות זמן ב-Cloud Firestore

לדוגמה, אפשר להשתמש ב-Cloud Firestore כדי לאחסן טוקנים באוסף שנקרא fcmTokens. כל מזהה מסמך באוסף תואם למזהה משתמש, והמסמך מאחסן את אסימון ההרשמה הנוכחי ואת חותמת הזמן של העדכון האחרון שלו. משתמשים בפונקציה set כמו בדוגמה הזו ב-Kotlin:

    /**
     * Persist token to third-party servers.
     *
     * Modify this method to associate the user's FCM registration token with any server-side account
     * maintained by your application.
     *
     * @param token The new token.
     */
    private fun sendTokenToServer(token: String?) {
        // If you're running your own server, call API to send token and today's date for the user

        // Example shown below with Firestore
        // Add token and timestamp to Firestore for this user
        val deviceToken = hashMapOf(
            "token" to token,
            "timestamp" to FieldValue.serverTimestamp(),
        )
        // Get user ID from Firebase Auth or your own server
        Firebase.firestore.collection("fcmTokens").document("myuserid")
            .set(deviceToken)
    }

בכל פעם שטוקן מאוחזר, הוא מאוחסן ב-Cloud Firestore על ידי קריאה ל-sendTokenToServer:

    /**
     * Called if the FCM registration token is updated. This may occur if the security of
     * the previous token had been compromised. Note that this is called when the
     * FCM registration token is initially generated so this is where you would retrieve the token.
     */
    override fun onNewToken(token: String) {
        Log.d(TAG, "Refreshed token: $token")

        // If you want to send messages to this application instance or
        // manage this apps subscriptions on the server side, send the
        // FCM registration token to your app server.
        sendTokenToServer(token)
    }
        var token = Firebase.messaging.token.await()

        // Check whether the retrieved token matches the one on your server for this user's device
        val preferences = this.getPreferences(Context.MODE_PRIVATE)
        val tokenStored = preferences.getString("deviceToken", "")
        lifecycleScope.launch {
            if (tokenStored == "" || tokenStored != token)
            {
                // If you have your own server, call API to send the above token and Date() for this user's device

                // Example shown below with Firestore
                // Add token and timestamp to Firestore for this user
                val deviceToken = hashMapOf(
                    "token" to token,
                    "timestamp" to FieldValue.serverTimestamp(),
                )

                // Get user ID from Firebase Auth or your own server
                Firebase.firestore.collection("fcmTokens").document("myuserid")
                    .set(deviceToken).await()
            }
        }

שמירה על טוקנים עדכניים והסרה של טוקנים לא עדכניים

לא תמיד קל לקבוע אם טוקן הוא חדש או לא פעיל. כדי לכסות את כל המקרים, כדאי להגדיר סף שמעליו הטוקנים ייחשבו לא פעילים. כברירת מחדל, FCM מחשיב טוקן כלא פעיל אם מופע האפליקציה שלו לא התחבר במשך חודש. סביר להניח שטוקן שגילו יותר מחודש שייך למכשיר לא פעיל, כי אחרת המכשיר הפעיל היה מרענן את הטוקן.

בהתאם לתרחיש השימוש, יכול להיות שחודש אחד יהיה קצר מדי או ארוך מדי, ולכן אתם צריכים לקבוע את הקריטריונים שמתאימים לכם.

זיהוי תגובות לא תקינות של טוקנים מהקצה העורפי FCM

חשוב לזהות תגובות של טוקנים לא תקינים מ-FCM ולהגיב על ידי מחיקה מהמערכת של כל טוקן רישום שידוע כלא תקין או שתוקפו פג. באמצעות HTTP v1 API, הודעות השגיאה האלה עשויות להצביע על כך שבקשת השליחה שלכם כוונה לטוקנים לא תקינים או לטוקנים שתוקפם פג:

  • UNREGISTERED (HTTP 404)
  • INVALID_ARGUMENT (HTTP 400)

אם אתם בטוחים שהמטען הייעודי של ההודעה תקין ואתם מקבלים אחת מהתגובות האלה עבור טוקן ממוקד, אפשר למחוק את הרשומה של הטוקן הזה, כי הוא לא יהיה תקף יותר. לדוגמה, כדי למחוק טוקנים לא תקינים מ-Cloud Firestore, אפשר לפרוס ולהריץ פונקציה כמו הבאה:

    // Registration token comes from the client FCM SDKs
    const registrationToken = 'YOUR_REGISTRATION_TOKEN';

    const message = {
    data: {
        // Information you want to send inside of notification
    },
    token: registrationToken
    };

    // Send message to device with provided registration token
    getMessaging().send(message)
    .then((response) => {
        // Response is a message ID string.
    })
    .catch((error) => {
        // Delete token for user if error code is UNREGISTERED or INVALID_ARGUMENT.
        if (error.errorCode == "messaging/registration-token-not-registered") {
            // If you're running your own server, call API to delete the
            token for the user

            // Example shown below with Firestore
            // Get user ID from Firebase Auth or your own server
            Firebase.firestore.collection("fcmTokens").document(user.uid).delete()
        }
    });

FCM מחזירה תגובה של טוקן לא תקין אם תוקף הטוקן של מכשיר Android פג אחרי 270 ימים של חוסר פעילות, או אם לקוח ביטל את הרישום באופן מפורש. אם אתם רוצים לעקוב בצורה מדויקת יותר אחרי טוקנים לא פעילים בהתאם להגדרות שלכם, אתם יכולים להסיר באופן יזום טוקנים לא פעילים של רישום.

עדכון אסימונים באופן קבוע

מומלץ לאחזר ולעדכן מעת לעת את כל אסימוני הרישום בשרת. כדי לעשות את זה, צריך:

  • מוסיפים לאפליקציית הלקוח לוגיקה של אפליקציה כדי לאחזר את הטוקן הנוכחי באמצעות קריאה מתאימה ל-API (למשל, token(completion): לפלטפורמות של Apple או getToken() ל-Android), ואז שולחים את הטוקן הנוכחי לשרת האפליקציה לאחסון (עם חותמת זמן). אפשר להגדיר משימה חודשית שתכסה את כל הלקוחות או הטוקנים.
  • מוסיפים לוגיקה של שרת כדי לעדכן את חותמת הזמן של האסימון במרווחי זמן קבועים, בלי קשר לשאלה אם האסימון השתנה או לא.

דוגמה ללוגיקה של Android לעדכון טוקנים באמצעות WorkManager מופיעה במאמר ניהול טוקנים של העברת הודעות בענן בבלוג של Firebase.

לא משנה באיזה תזמון תבחרו, חשוב לעדכן את הטוקנים באופן תקופתי. תדירות עדכון של פעם בחודש מאפשרת איזון טוב בין ההשפעה על הסוללה לבין זיהוי של טוקנים לא פעילים להרשמה. כשמרעננים את הטוקנים, מוודאים שכל מכשיר שהופך ללא פעיל ירענן את ההרשמה שלו כשהוא יהפוך שוב לפעיל. אין יתרון לרענון בתדירות גבוהה יותר מפעם בשבוע.

הסרת טוקנים לא עדכניים של רישום

לפני ששולחים הודעות למכשיר, צריך לוודא שחותמת הזמן של טוקן הרישום של המכשיר נמצאת בטווח של חלון התפוגה. לדוגמה, אפשר להטמיע את Cloud Functions for Firebase כדי להריץ בדיקה יומית ולוודא שחותמת הזמן נמצאת בתוך חלון זמן מוגדר של נתונים לא עדכניים, כמו const EXPIRATION_TIME = 1000 * 60 * 60 * 24 * 30;, ואז להסיר את הטוקנים של נתונים לא עדכניים:

exports.pruneTokens = functions.pubsub.schedule('every 24 hours').onRun(async (context) => {
  // Get all documents where the timestamp exceeds is not within the past month
  const staleTokensResult = await admin.firestore().collection('fcmTokens')
      .where("timestamp", "<", Date.now() - EXPIRATION_TIME)
      .get();
  // Delete devices with stale tokens
  staleTokensResult.forEach(function(doc) { doc.ref.delete(); });
});

ביטול ההרשמה של טוקנים לא פעילים לנושאים

אם אתם משתמשים בנושאים, יכול להיות שתרצו גם לבטל את הרישום של טוקנים לא פעילים מהנושאים שהם רשומים אליהם. התהליך כולל שני שלבים:

  1. האפליקציה צריכה להירשם מחדש לנושאים פעם בחודש ובכל פעם שטוקן ההרשמה משתנה. כך נוצר פתרון לתיקון עצמי, שבו המינויים מופיעים מחדש באופן אוטומטי כשהאפליקציה הופכת שוב לפעילה.
  2. אם מופעלת באפליקציה תקופה של חוסר פעילות למשך חודש (או תקופה אחרת שהגדרתם), צריך לבטל את ההרשמה של מופע האפליקציה לנושאים באמצעות Firebase Admin SDK כדי למחוק את המיפוי של הטוקן לנושא מהקצה העורפי של FCM.

היתרון בשני השלבים האלה הוא שההפצה תתבצע מהר יותר, כי יהיו פחות טוקנים לא עדכניים להפצה, והמופעים הלא עדכניים של האפליקציה יירשמו מחדש באופן אוטומטי ברגע שהם יהיו פעילים שוב.

מדידת הצלחת ההעברה

כדי לקבל תמונה מדויקת ככל האפשר של מסירת ההודעות, מומלץ לשלוח הודעות רק למופעים של אפליקציות שנמצאים בשימוש פעיל. זה חשוב במיוחד אם אתם שולחים הודעות באופן קבוע לנושאים עם מספר גדול של מנויים. אם חלק מהמנויים האלה לא פעילים, ההשפעה על נתוני המסירה יכולה להיות משמעותית לאורך זמן.

לפני שמטרגטים הודעות לטוקן, כדאי לשקול את הנקודות הבאות:

  • האם נתונים מ-Google Analytics, נתונים שנאספו ב-BigQuery או אותות מעקב אחרים מציינים שהאסימון פעיל?
  • האם ניסיונות המסירה הקודמים נכשלו באופן עקבי במשך תקופה מסוימת?
  • האם אסימון הרישום עודכן בשרתים שלך בחודש האחרון?
  • במכשירי Android, האם FCM Data API מדווח על אחוז גבוה של הודעות שלא נמסרו בגלל droppedDeviceInactive?

מידע נוסף על מסירה זמין במאמר הסבר על מסירת הודעות.