התראות באינטרנט הפתוח

מאט גאונט

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

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

החל מגרסה 42 של Chrome, למפתחים יש גישה ל-Push API ול-Notification API.

Push API ב-Chrome מסתמך על כמה טכנולוגיות שונות, כולל מניפסטים של אפליקציות אינטרנט ו-Service Workers. בפוסט הזה נבחן כל אחת מהטכנולוגיות האלה, אבל רק את המינימום הדרוש כדי להתחיל לפרסם הודעות בדחיפה. בקישורים שלמעלה תוכלו לקרוא הסבר על חלק מהתכונות האחרות של מניפסטים והיכולות של קובצי שירות (service worker) ללא חיבור לאינטרנט.

נראה גם מה יתווסף ל-API בגרסאות עתידיות של Chrome, ולבסוף נעסוק בשאלות נפוצות.

יישום העברת הודעות בדחיפה עבור Chrome

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

רישום Service Worker

תלוי אם קובץ שירות (service worker) יטמיע הודעות דחיפה לאינטרנט. הסיבה לכך היא שכאשר מתקבלת הודעת Push, הדפדפן יכול להפעיל קובץ שירות (service worker) שפועל ברקע בלי שדף נפתח, ואז לשלוח אירוע כדי שתוכלו להחליט איך לטפל בהודעה הזו.

לפניכם דוגמה לאופן שבו נרשמים קובץ שירות (service worker) באפליקציית האינטרנט. כשהרישום מסתיים בהצלחה, אנחנו קוראים ל-initialiseState(), ונסביר אותו בקרוב.

var isPushEnabled = false;

…

window.addEventListener('load', function() {
    var pushButton = document.querySelector('.js-push-button');
    pushButton.addEventListener('click', function() {
    if (isPushEnabled) {
        unsubscribe();
    } else {
        subscribe();
    }
    });

    // Check that service workers are supported, if so, progressively
    // enhance and add push messaging support, otherwise continue without it.
    if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/service-worker.js')
    .then(initialiseState);
    } else {
    console.warn('Service workers aren\'t supported in this browser.');
    }
});

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

לאחר מכן אנחנו בודקים אם יש תמיכה ב-Service Workers לפני שרושמים את קובץ service-worker.js שיש בו את הלוגיקה לטיפול בהודעות Push. כאן אנחנו פשוט אומרים לדפדפן שקובץ ה-JavaScript הזה הוא קובץ השירות (service worker) של האתר שלנו.

הגדרת המצב הראשוני

דוגמה לחוויית משתמש מופעלת ומושבתת לשליחת הודעות בדחיפה ב-Chrome.

אחרי שה-Service Worker נרשם, אנחנו צריכים להגדיר את המצב של ממשק המשתמש שלנו.

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

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

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

תרשים שמדגיש את השיקולים השונים ואת מצב הדחיפה ב-Chrome

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

מכיוון שרוב הבדיקות האלה גורמות להשבתה של ממשק המשתמש, צריך להגדיר את המצב הראשוני למושבת. כך תוכלו גם למנוע בלבול במקרה של בעיה ב-JavaScript של הדף, כמו לא ניתן להוריד את קובץ ה-JS או שהמשתמש השבית את JavaScript.

<button class="js-push-button" disabled>
    Enable Push Messages
</button>

במצב הראשוני הזה, אנחנו יכולים לבצע את הבדיקות שתוארו למעלה בשיטה initialiseState(), כלומר אחרי שה-Service Worker נרשם.

// Once the service worker is registered set the initial state
function initialiseState() {
    // Are Notifications supported in the service worker?
    if (!('showNotification' in ServiceWorkerRegistration.prototype)) {
    console.warn('Notifications aren\'t supported.');
    return;
    }

    // Check the current Notification permission.
    // If its denied, it's a permanent block until the
    // user changes the permission
    if (Notification.permission === 'denied') {
    console.warn('The user has blocked notifications.');
    return;
    }

    // Check if push messaging is supported
    if (!('PushManager' in window)) {
    console.warn('Push messaging isn\'t supported.');
    return;
    }

    // We need the service worker registration to check for a subscription
    navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
    // Do we already have a push message subscription?
    serviceWorkerRegistration.pushManager.getSubscription()
        .then(function(subscription) {
        // Enable any UI which subscribes / unsubscribes from
        // push messages.
        var pushButton = document.querySelector('.js-push-button');
        pushButton.disabled = false;

        if (!subscription) {
            // We aren't subscribed to push, so set UI
            // to allow the user to enable push
            return;
        }

        // Keep your server in sync with the latest subscriptionId
        sendSubscriptionToServer(subscription);

        // Set your UI to show they have subscribed for
        // push messages
        pushButton.textContent = 'Disable Push Messages';
        isPushEnabled = true;
        })
        .catch(function(err) {
        console.warn('Error during getSubscription()', err);
        });
    });
}

סקירה קצרה של שלבים אלה:

  • אנחנו מוודאים ש-showNotification זמין באב-הטיפוס של ServiceWorkerRegistration. בלי זה לא נוכל להציג התראה מ-Service Worker כשמתקבלות הודעת דחיפה.
  • אנחנו בודקים מהו הערך הנוכחי של Notification.permission כדי לוודא שהוא לא "denied". אם ההרשאה תידחה, לא תוכלו להציג התראות עד שהמשתמש ישנה את ההרשאה בדפדפן באופן ידני.
  • כדי לבדוק אם יש תמיכה בהעברת הודעות בדחיפה, אנחנו בודקים שהפונקציה PushManager זמינה באובייקט החלון.
  • לבסוף, השתמשנו ב-pushManager.getSubscription() כדי לבדוק אם כבר יש לנו מינוי. במקרה כזה, אנחנו שולחים את פרטי המינוי לשרת שלנו כדי לוודא שיש לנו את המידע הנכון, ומגדירים את ממשק המשתמש כך שיציין אם הודעות Push כבר מופעלות או לא. בהמשך המאמר נבדוק אילו פרטים קיימים באובייקט המינוי.

אנחנו מחכים עד שהבעיה navigator.serviceWorker.ready תטופל כדי לבדוק אם יש מינוי ולהפעיל את לחצן הדחיפה, כי אפשר להירשם לקבלת הודעות שנשלחות מהאפליקציה רק אחרי שה-Service Worker פעיל.

בשלב הבא צריך לטפל בבקשות שהמשתמש רוצה להפעיל בדחיפה, אבל לפני שנוכל לעשות זאת, עלינו להגדיר פרויקט ב-Google Developer Console ולהוסיף כמה פרמטרים למניפסט שלנו כדי להשתמש ב-Firebase Cloud Messaging (FCM), שנקרא בעבר Google Cloud Messaging (GCM).

יצירת פרויקט ב-Firebase Developer Console

ב-Chrome נעשה שימוש ב-FCM כדי לטפל בשליחה ובשליחה של הודעות Push. עם זאת, כדי להשתמש ב-FCM API, צריך להגדיר פרויקט ב-Firebase Developer Console.

השלבים הבאים ספציפיים לדפדפן Chrome, Opera ל-Android ודפדפן Samsung שבהם הם משתמשים ב-FCM. בהמשך המאמר נדון באופן שבו זה יעבוד בדפדפנים אחרים.

יצירת פרויקט חדש למפתחים ב-Firebase

בתור התחלה, צריך ליצור פרויקט חדש בכתובת https://console.firebase.google.com/ בלחיצה על 'Create New Project' (יצירת פרויקט חדש).

צילום מסך חדש של פרויקט Firebase

מוסיפים שם לפרויקט, יוצרים אותו ותועברו ללוח הבקרה של הפרויקט:

דף הבית של פרויקט Firebase

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

תפריט הגדרות הפרויקט ב-Firebase

בדף ההגדרות, לוחצים על הכרטיסייה 'העברת הודעות בענן'.

תפריט העברת הודעות בענן בפרויקט Firebase

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

הוספת מניפסט של אפליקציית אינטרנט

בדחיפה, אנחנו צריכים להוסיף קובץ מניפסט עם שדה gcm_sender_id, כדי שהמינוי ל-Push יצליח. הפרמטר הזה נדרש רק בדפדפנים Chrome, Opera ל-Android ו-Samsung כדי שהם יוכלו להשתמש ב-FCM או ב-GCM.

הדפדפנים האלה משתמשים בפרמטר gcm_sender_id כשהוא רושם מכשיר של משתמש באמצעות FCM. המשמעות היא ש-FCM יכול לזהות את המכשיר של המשתמש ולוודא שמזהה השולח תואם למפתח ה-API המתאים, ושהמשתמש אישר לשרת שלכם לשלוח לו הודעות בדחיפה.

לפניכם קובץ מניפסט פשוט במיוחד:

{
    "name": "Push Demo",
    "short_name": "Push Demo",
    "icons": [{
        "src": "images/icon-192x192.png",
        "sizes": "192x192",
        "type": "image/png"
        }],
    "start_url": "/index.html?homescreen=1",
    "display": "standalone",
    "gcm_sender_id": "<Your Sender ID Here>"
}

עליך להגדיר את הערך gcm_sender_id למזהה השולח מהפרויקט ב-Firebase.

אחרי ששומרים את קובץ המניפסט בפרויקט (manifest.json הוא שם טוב), מפנים אליו מה-HTML באמצעות התג הבא שמוצג בראש הדף.

<link rel="manifest" href="/manifest.json">

אם לא תוסיפו מניפסט אינטרנט עם הפרמטרים האלה, תקבלו חריגה כשתנסו לרשום את המשתמש לשליחת הודעות ב-Drive עם השגיאה "Registration failed - no sender id provided" או "Registration failed - permission denied".

הרשמה להודעות Push

הגדרת מניפסט ועכשיו אפשר לחזור ל-JavaScript של האתרים שלך.

כדי להירשם, צריך להפעיל את השיטה subscribe() באובייקט PushManager, שאליו נכנסים דרך ServiceWorkerRegistration.

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

אם ה-promise שהוחזר על ידי השיטה subscribe() נפתר, תקבלו אובייקט PushSubscription שיכיל נקודת קצה (endpoint).

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

הקוד הבא מנוי את המשתמש להודעות בדחיפה:

function subscribe() {
    // Disable the button so it can't be changed while
    // we process the permission request
    var pushButton = document.querySelector('.js-push-button');
    pushButton.disabled = true;

    navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
    serviceWorkerRegistration.pushManager.subscribe()
        .then(function(subscription) {
        // The subscription was successful
        isPushEnabled = true;
        pushButton.textContent = 'Disable Push Messages';
        pushButton.disabled = false;

        // TODO: Send the subscription.endpoint to your server
        // and save it to send a push message at a later date
        return sendSubscriptionToServer(subscription);
        })
        .catch(function(e) {
        if (Notification.permission === 'denied') {
            // The user denied the notification permission which
            // means we failed to subscribe and the user will need
            // to manually change the notification permission to
            // subscribe to push messages
            console.warn('Permission for Notifications was denied');
            pushButton.disabled = true;
        } else {
            // A problem occurred with the subscription; common reasons
            // include network errors, and lacking gcm_sender_id and/or
            // gcm_user_visible_only in the manifest.
            console.error('Unable to subscribe to push.', e);
            pushButton.disabled = false;
            pushButton.textContent = 'Enable Push Messages';
        }
        });
    });
}

בשלב הזה, אפליקציית האינטרנט שלכם מוכנה לקבל הודעות Push, אבל לא יקרה כלום עד שנוסיף רכיב Push event listener לקובץ של קובץ השירות (service worker).

מעבד אירוע דחיפה של Service Worker

כשמתקבלת הודעת Push (בקטע הבא נסביר איך לשלוח בפועל הודעות Push), אירוע Push יישלח ב-Service Worker, ובשלב זה תצטרכו להציג התראה.

self.addEventListener('push', function(event) {
    console.log('Received a push message', event);

    var title = 'Yay a message.';
    var body = 'We have received a push message.';
    var icon = '/images/icon-192x192.png';
    var tag = 'simple-push-demo-notification-tag';

    event.waitUntil(
    self.registration.showNotification(title, {
        body: body,
        icon: icon,
        tag: tag
    })
    );
});

הקוד הזה רושם event listener מסוג Push ומציג התראה עם כותרת מוגדרת מראש, טקסט גוף ההודעה, סמל ותג התראה. הנה כמה דברים שחשוב להדגיש בדוגמה הזו – method event.waitUntil(). השיטה הזו לוקחת הבטחה ומאריכה את משך החיים של הגורם המטפל באירועים (או שאפשר לחשוב עליה כשימור של ה-Service Worker) עד שההבטחה מוסדרת. במקרה הזה, ההבטחה שהועברה ל-event.waitUntil היא ההבטחה שהוחזרה מ-showNotification().

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

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

שליחת הודעה

נרשמו לקבלת הודעות דחיפה ו-Service Worker מוכן להציג התראה, אז זה הזמן לשלוח הודעת דחיפה באמצעות FCM.

הדבר רלוונטי רק לדפדפנים שמשתמשים ב-FCM.

כששולחים את המשתנה PushSubscription.endpoint לשרת, נקודת הקצה (endpoint) של FCM היא מיוחדת. היא כוללת פרמטר בסוף כתובת ה-URL שהוא registration_id.

דוגמה לנקודת סיום:

https://fcm.googleapis.com/fcm/send/APA91bHPffi8zclbIBDcToXN_LEpT6iA87pgR-J-MuuVVycM0SmptG-rXdCPKTM5pvKiHk2Ts-ukL1KV8exGOnurOAKdbvH9jcvg8h2gSi-zZJyToiiydjAJW6Fa9mE3_7vsNIgzF28KGspVmLUpMgYLBd1rxaVh-L4NDzD7HyTkhFOfwWiyVdKh__rEt15W9n2o6cZ8nxrP

כתובת ה-URL של FCM היא:

https://fcm.googleapis.com/fcm/send

הערך של registration_id יהיה:

APA91bHPffi8zclbIBDcToXN_LEpT6iA87pgR-J-MuuVVycM0SmptG-rXdCPKTM5pvKiHk2Ts-ukL1KV8exGOnurOAKdbvH9jcvg8h2gSi-zZJyToiiydjAJW6Fa9mE3_7vsNIgzF28KGspVmLUpMgYLBd1rxaVh-L4NDzD7HyTkhFOfwWiyVdKh__rEt15W9n2o6cZ8nxrP

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

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

if endpoint.startswith('https://fcm.googleapis.com/fcm/send'):
    endpointParts = endpoint.split('/')
    registrationId = endpointParts[len(endpointParts) - 1]

    endpoint = 'https://fcm.googleapis.com/fcm/send'

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

ההיבטים העיקריים שיש לזכור כשמתקשרים ל-FCM הם:

  • כשקוראים ל-API, חובה להגדיר כותרת Authorization עם הערך key=&lt;YOUR_API_KEY&gt; כאשר &lt;YOUR_API_KEY&gt; הוא מפתח ה-API מפרויקט Firebase.
    • FCM משתמש במפתח ה-API כדי לאתר את מזהה השולח המתאים, לוודא שהמשתמש נתן הרשאה לפרויקט שלכם ולוודא שכתובת ה-IP של השרת נוספה לרשימת ההיתרים של אותו פרויקט.
  • כותרת Content-Type מתאימה של application/json או application/x-www-form-urlencoded;charset=UTF-8, בהתאם לסוג הנתונים ששולחים.
  • מערך של registration_ids – אלה מזהי הרישום שצריך לחלץ מנקודות הקצה מהמשתמשים.

מומלץ לעיין במסמכים שבהם מוסבר איך לשלוח הודעות Push מהשרת שלכם, אבל כדי לבדוק בקלות את קובץ השירות, תוכלו להשתמש ב-cURL כדי לשלוח הודעות Push לדפדפן.

מחליפים את &lt;YOUR_API_KEY&gt; ו-&lt;YOUR_REGISTRATION_ID&gt; בפקודת cURL הזו בפקודה משלכם, ומריצים אותה מטרמינל.

אתה אמור לראות הודעה מרשימה:

    curl --header "Authorization: key=<YOUR_API_KEY>" --header
    "Content-Type: application/json" https://fcm.googleapis.com/fcm/send -d
    "{\"registration_ids\":[\"<YOUR_REGISTRATION_ID>\"]}"
דוגמה להודעת דחיפה מ-Chrome ל-Android.

כשמפתחים את הלוגיקה של הקצה העורפי, חשוב לזכור שהכותרת והפורמט של גוף ה-POST הם ספציפיים לנקודת הקצה של FCM. לכן צריך לזהות מתי נקודת הקצה מיועדת ל-FCM, ולהוסיף באופן מותנה את הכותרת ועיצוב גוף ה-POST. בדפדפנים אחרים (ובתקווה שב-Chrome בעתיד) תצטרכו להטמיע את Web Push Protocol.

החיסרון של ההטמעה הנוכחית של Push API ב-Chrome הוא שאי אפשר לשלוח נתונים בדחיפה. לא, כלום. הסיבה לכך היא שבהטמעה עתידית, נתוני המטען הייעודי (payload) צריכים להיות מוצפנים בשרת לפני שהם נשלחים לנקודת קצה (endpoint) של העברת הודעות בדחיפה. כך, נקודת הקצה, לא משנה מה ספק הדחיפה, לא תוכל לצפות בקלות בתוכן של הודעת ה-Push. ההגנה הזו גם מגינה מפני נקודות חולשה אחרות, כמו אימות לא מוצלח של אישורי HTTPS והתקפות אדם בתווך בין השרת לספק ה-Push. עם זאת, ההצפנה עדיין לא נתמכת, ולכן בינתיים תצטרכו לבצע אחזור כדי לקבל את המידע הנחוץ לאכלוס ההתראה.

דוגמה לאירוע דחיפה מלא יותר

ההודעה שראינו עד כה היא די בסיסית, ומבחינת הדגימות, היא לא מכסה תרחיש לדוגמה בעולם האמיתי.

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

בקוד הבא אנחנו מאחזרים נתונים מסוימים מ-API, ממירים את התגובה לאובייקט ומשתמשים בה כדי לאכלס את ההתראה.

self.addEventListener('push', function(event) {
    // Since there is no payload data with the first version
    // of push messages, we'll grab some data from
    // an API and use it to populate a notification
    event.waitUntil(
    fetch(SOME_API_ENDPOINT).then(function(response) {
        if (response.status !== 200) {
        // Either show a message to the user explaining the error
        // or enter a generic message and handle the
        // onnotificationclick event to direct the user to a web page
        console.log('Looks like there was a problem. Status Code: ' + response.status);
        throw new Error();
        }

        // Examine the text in the response
        return response.json().then(function(data) {
        if (data.error || !data.notification) {
            console.error('The API returned an error.', data.error);
            throw new Error();
        }

        var title = data.notification.title;
        var message = data.notification.message;
        var icon = data.notification.icon;
        var notificationTag = data.notification.tag;

        return self.registration.showNotification(title, {
            body: message,
            icon: icon,
            tag: notificationTag
        });
        });
    }).catch(function(err) {
        console.error('Unable to retrieve data', err);

        var title = 'An error occurred';
        var message = 'We were unable to get the information for this push message';
        var icon = URL_TO_DEFAULT_ICON;
        var notificationTag = 'notification-error';
        return self.registration.showNotification(title, {
            body: message,
            icon: icon,
            tag: notificationTag
        });
    })
    );
});

כדאי להדגיש שוב ש-event.waitUntil() מקבל הבטחה שמוחזרת מההבטחה שמוחזרת על ידי showNotification(). כלומר, ה- listener שלנו לאירוע לא ייצא עד שהשיחה האסינכרונית ב-fetch() תסתיים וההתראה תוצג.

במקרה כזה, תוצג לך התראה גם במקרה של שגיאה. הסיבה לכך היא שאם לא נציג את ההתראה, Chrome יציג התראה כללית משלו.

פתיחת כתובת URL כשמשתמש לוחץ על התראה

כשהמשתמש לוחץ על התראה, אירוע notificationclick נשלח ב-Service Worker. בתוך ה-handler שלכם תוכלו לבצע את הפעולות המתאימות, כמו התמקדות בכרטיסייה או פתיחת חלון עם כתובת URL ספציפית:

self.addEventListener('notificationclick', function(event) {
    console.log('On notification click: ', event.notification.tag);
    // Android doesn't close the notification when you click on it
    // See: http://crbug.com/463146
    event.notification.close();

    // This looks to see if the current is already open and
    // focuses if it is
    event.waitUntil(
    clients.matchAll({
        type: "window"
    })
    .then(function(clientList) {
        for (var i = 0; i < clientList.length; i++) {
        var client = clientList[i];
        if (client.url == '/' && 'focus' in client)
            return client.focus();
        }
        if (clients.openWindow) {
        return clients.openWindow('/');
        }
    })
    );
});

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

יש פוסט שמוקדש לכמה מהדברים שאפשר לעשות כאן עם Notification API.

ביטול רישום למכשיר של משתמש

יש לכם מכשיר רשום של משתמש והוא מקבל הודעות דחיפה, אבל איך אפשר לבטל את הרישום שלו?

הפעולות העיקריות שצריך לבצע כדי לבטל הרשמה של מכשיר של משתמש הן להפעיל את השיטה unsubscribe() באובייקט PushSubscription ולהסיר את נקודת הקצה מהשרתים (כדי שלא יישלחו הודעות דחיפה שלא יתקבלו). הקוד שבהמשך עושה בדיוק את זה:

function unsubscribe() {
    var pushButton = document.querySelector('.js-push-button');
    pushButton.disabled = true;

    navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
    // To unsubscribe from push messaging, you need get the
    // subscription object, which you can call unsubscribe() on.
    serviceWorkerRegistration.pushManager.getSubscription().then(
        function(pushSubscription) {
        // Check we have a subscription to unsubscribe
        if (!pushSubscription) {
            // No subscription object, so set the state
            // to allow the user to subscribe to push
            isPushEnabled = false;
            pushButton.disabled = false;
            pushButton.textContent = 'Enable Push Messages';
            return;
        }

        var subscriptionId = pushSubscription.subscriptionId;
        // TODO: Make a request to your server to remove
        // the subscriptionId from your data store so you
        // don't attempt to send them push messages anymore

        // We have a subscription, so call unsubscribe on it
        pushSubscription.unsubscribe().then(function(successful) {
            pushButton.disabled = false;
            pushButton.textContent = 'Enable Push Messages';
            isPushEnabled = false;
        }).catch(function(e) {
            // We failed to unsubscribe, this can lead to
            // an unusual state, so may be best to remove
            // the users data from your data store and
            // inform the user that you have done so

            console.log('Unsubscription error: ', e);
            pushButton.disabled = false;
            pushButton.textContent = 'Enable Push Messages';
        });
        }).catch(function(e) {
        console.error('Error thrown while unsubscribing from push messaging.', e);
        });
    });
}

שמירה על ההרשמה עדכנית

ייתכן שהמינויים לא יהיו מסונכרנים בין FCM לשרת שלך. ודאו שהשרת מנתח את גוף התגובה של ה-POST של שליחת POST של ה-FCM API, ומחפשים תוצאות של error:NotRegistered ושל canonical_id, כמו שמוסבר במסמכי התיעוד של FCM.

יכול להיות גם שמינויים לא יהיו מסונכרנים בין Service Worker לבין השרת. לדוגמה, לאחר הרשמה או ביטול הרשמה בהצלחה, חיבור רעוע לרשת עשוי למנוע מכם לעדכן את השרת, או שמשתמש עשוי לבטל את ההרשאה לשליחת התראות, פעולה שמובילה לביטול אוטומטי. אפשר לטפל במקרים כאלה על ידי בדיקת התוצאה של serviceWorkerRegistration.pushManager.getSubscription() מדי פעם (למשל בטעינת דף) וסנכרון שלה עם השרת. ייתכן שתרצו להירשם מחדש באופן אוטומטי גם אם כבר אין לכם מינוי ו-Notification.permission == 'granted'.

בsendSubscriptionToServer(), תצטרכו לשקול את אופן הטיפול בבקשות רשת שנכשלו כשמעדכנים את endpoint. אחד מהפתרונות הוא לעקוב אחרי המצב של endpoint בקובץ cookie כדי לקבוע אם יש צורך בפרטים העדכניים ביותר של השרת.

כל השלבים שלמעלה יובילו להטמעה מלאה של הודעות Push באינטרנט ב-Chrome 46. עדיין יש תכונות ספציפיות שיקלו עליכם (כמו API סטנדרטי להפעלת הודעות Push), אבל הגרסה הזו מאפשרת לכם להתחיל כבר היום ליצור העברת הודעות בדחיפה לאפליקציות האינטרנט שלכם.

איך לנפות באגים באפליקציית האינטרנט

בזמן הטמעת הודעות Push, הבאגים יפעלו באחד משני מקומות: בדף או בקובץ השירות (service worker).

אפשר לנפות באגים בדף באמצעות DevTools. כדי לנפות באגים ב-Service Worker יש שתי אפשרויות:

  1. עוברים אל chrome://inspect > Service work. התצוגה הזו לא מספקת מידע רב מלבד ה-Service Workers שפועלים כרגע.
  2. נכנסים לכתובת chrome://serviceworker-internals ומכאן תוכלו לראות את המצב של Service Workers ולראות שגיאות, אם יש כאלה. הדף הזה זמני עד שתוצג בכלי הפיתוח קבוצת תכונות דומה.

אחד מהטיפים הכי טובים שאפשר לתת לכל מי שרק חדש ב-Service Worker הוא להשתמש בתיבת הסימון 'Open DevTools window and paused JavaScript running on service worker הפעלה לצורך ניפוי באגים'. תיבת הסימון הזו תוסיף נקודת עצירה (breakpoint) בתחילת קובץ השירות (service worker) והאפשרות להשהות את הביצוע. כך תוכלו להמשיך את הסקריפט של Service Worker או לעבור אותו שלב כדי לראות אם תיתקלו בבעיות.

צילום מסך שבו מופיעה תיבת הסימון של ביצוע ההשהיה ב-serviceworker-internals.

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

{"multicast_id":1234567890,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1234567890"}]}

לתשומת ליבך, התגובה "success": 1. אם במקום זאת מופיעה הודעת שגיאה, זה סימן לכך שמשהו לא תקין במזהה הרישום של FCM והודעת הדחיפה לא נשלחת ל-Chrome.

ניפוי באגים ב-Service Workers ב-Chrome ל-Android

כרגע ניפוי הבאגים ב-Service Workers ב-Chrome ל-Android אינו מובן מאליו. צריך להיכנס אל chrome://inspect, לאתר את המכשיר ולחפש פריט ברשימה בשם "Worker pid:...." שכולל את כתובת ה-URL של קובץ השירות (service worker).

צילום מסך שמראה איפה נמצאים קובצי שירות (service worker) בבדיקה של Chrome

חוויית המשתמש בהתראות

צוות Chrome הכין מסמך של שיטות מומלצות לחוויית המשתמש של התראות Push, ומסמך שמסקר חלק ממקרי הקצה של פעולות עם התראות.

העתיד של העברת הודעות בדחיפה ב-Chrome ובאינטרנט הפתוח

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

פרוטוקול Web Push ונקודות קצה (Endpoint)

היתרון הגדול של תקן Push API הוא האפשרות לקחת את נקודת הקצה, להעביר אותה לשרת ולשלוח הודעות Push באמצעות ההטמעה של Web Push Protocol.

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

Chrome היה הדפדפן הראשון שהטמיע את Push API, ו-FCM לא תומך ב-Web Push Protocol, ולכן Chrome דורש את gcm_sender_id וצריך להשתמש ב-API המנוחה ל-FCM.

היעד הסופי של Chrome הוא לעבור לשימוש בפרוטוקול Web Push עם Chrome ו-FCM.

עד אז, צריך לזהות את נקודת הקצה 'https://fcm.googleapis.com/fcm/send' ולטפל בה בנפרד מנקודות קצה אחרות, כלומר לפרמט את נתוני המטען הייעודי (payload) בדרך ספציפית ולהוסיף את מפתח ההרשאה.

איך ליישם את Web Push Protocol?

Firefox Nightly עובד כרגע על Push, וסביר להניח שהוא יהיה הדפדפן הראשון שיטמיע את Web Push Protocol.

שאלות נפוצות

איפה המפרט?

https://slightlyoff.github.io/ServiceWorker/spec/service_worker/ https://w3c.github.io/push-api/ https://notifications.spec.whatwg.org/

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

אין כרגע פתרון לבעיה הזו, אבל אפשר לעקוב אחר ההתקדמות ב-Chromium.

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

למה צריך לציין gcm_sender_id?

הפעולה הזו נדרשת כדי ש-Chrome, Opera ל-Android ודפדפן Samsung יוכלו להשתמש ב-Firebase Cloud Messaging Messaging API (FCM). המטרה היא להשתמש ב-Web Push Protocol לאחר השלמת התקן ו-FCM יוכל לתמוך בו.

למה לא להשתמש ב-Web Sockets או ב-Server-Sent Events (EventSource)?

היתרון בשימוש בהודעות דחיפה הוא שגם אם הדף שלך סגור, Service Worker יתעורר והוא יוכל להציג הודעה. החיבור של Web Sockets ו-EventSource נסגר כאשר הדף או הדפדפן נסגר.

מה לעשות אם אין לי צורך במסירה של אירועים ברקע?

אם אתם לא זקוקים להעברה ברקע, כדאי להשתמש ב-Web Sockets.

מתי אפשר להשתמש בדחיפה בלי להציג התראות (למשל, דחיפה שקטה ברקע)?

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

למה נדרש HTTPS לשם כך? איך אפשר לעקוף את הבעיה הזו במהלך הפיתוח?

קובצי שירות (service worker) דורשים מקורות מאובטחים כדי לוודא שהסקריפט של Service Worker מגיע מהמקור המיועד ולא מתקפה מסוג man-in-the-middle. נכון לעכשיו, פירוש הדבר הוא שהשימוש ב-HTTPS באתרים פעילים, אבל Localhost יעבוד במהלך הפיתוח.

איך נראית תמיכה בדפדפן?

Chrome תומך בגרסה היציבה שלו, ו-Mozilla מאמצים עבודה בדחיפה ב-Firefox Nightly. תוכלו לקרוא מידע נוסף על הבאג הטמעה של Push API, וכאן אפשר לעקוב אחרי ההטמעה של ההתראות שלו.

האם אפשר להסיר התראה אחרי פרק זמן מסוים?

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

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

מהן המגבלות של העברת הודעות בדחיפה ב-Chrome?

יש כמה מגבלות שמתוארות בפוסט הזה:

  • השימוש של Chrome ב-CCM כשירות דחיפה יוצר מספר דרישות קנייניות. אנחנו משתפים פעולה כדי לבדוק אם אפשר לפתור חלק מהבעיות האלה בעתיד.
  • עליך להציג התראה כאשר אתה מקבל הודעת דחיפה.
  • ב-Chrome במחשב יש אזהרה שלפיה אם Chrome לא פועל, לא יתקבלו הודעות בדחיפה. שונה מ-ChromeOS ומ-Android, שם יתקבלו תמיד הודעות בדחיפה.

לא כדאי שנשתמש ב-Permissions API?

ה-Permission API מוטמע ב-Chrome, אבל הוא לא בהכרח יהיה זמין בכל הדפדפנים. מידע נוסף זמין כאן.

למה Chrome לא פותח את הכרטיסייה הקודמת כשאני לוחץ על התראה?

הבעיה הזו משפיעה רק על דפים שלא נשלטים כרגע על ידי קובץ שירות (service worker). אפשר לעיין כאן במידע נוסף.

מה קורה אם ההתראה לא מעודכנת עד למועד שבו המכשיר של המשתמש קיבל את הדחיפה?

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

לפרטים נוספים

מה קורה אם שולחים 10 הודעות דחיפה אבל רוצה שהמכשיר יקבל רק הודעה אחת?

ב-FCM יש פרמטר 'כווץ', שאפשר להשתמש בו כדי להורות ל-FCM להחליף בהודעה החדשה כל הודעה בהמתנה שמכילה את אותו 'כווץ_key'.

לפרטים נוספים