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

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

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

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

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

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

הטמעת העברת הודעות ב-Push ל-Chrome

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

רישום של קובץ שירות (service worker)

כדי להטמיע הודעות דחיפה לאינטרנט, צריך להשתמש ב-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 הוא משתנה גלובלי שמאפשר לעקוב אחרי ההרשמה הנוכחית של המשתמש להתרעות. נעשה בהם שימוש לאורך קטעי הקוד.

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

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

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

אחרי שה-service worker רשום, צריך להגדיר את המצב של ממשק המשתמש.

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

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

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

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

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

מאחר שרוב הבדיקות האלה גורמות להשבתת ממשק המשתמש שלנו, צריך להגדיר את המצב הראשוני כ'מושבת'. כך גם תוכלו למנוע בלבול במקרה של בעיה ב-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. בלי זה לא נוכל להציג התראה מקובץ השירות שלנו כשמתקבלת הודעת Push.
  • אנחנו בודקים מה הערך הנוכחי של Notification.permission כדי לוודא שהוא לא "denied". אם ההרשאה נדחתה, לא תוכלו להציג התראות עד שהמשתמש ישנה את ההרשאה באופן ידני בדפדפן.
  • כדי לבדוק אם יש תמיכה בהודעות דחיפה, בודקים אם המאפיין PushManager זמין באובייקט החלון.
  • לבסוף, השתמשנו ב-pushManager.getSubscription() כדי לבדוק אם כבר יש לנו מינוי. אם כן, אנחנו שולחים את פרטי המינוי לשרת שלנו כדי לוודא שיש לנו את המידע הנכון, ומגדירים את ממשק המשתמש כך שיציין אם התכונה 'הודעות דחיפה' כבר מופעלת או לא. בהמשך המאמר נראה אילו פרטים קיימים באובייקט המינוי.

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

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

יצירת פרויקט במסוף הפיתוח של Firebase

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

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

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

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

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

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

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

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

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

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

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

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

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

כדי לשלוח התראות, צריך להוסיף קובץ מניפסט עם השדה gcm_sender_id כדי שההרשמה לקבלת התראות תתבצע. הפרמטר הזה נדרש רק ב-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">

אם לא תוסיפו מניפסט אינטרנט עם הפרמטרים האלה, תופיע חריגה כשתנסו להירשם את המשתמש להודעות דחיפה, עם השגיאה "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';
        }
        });
    });
}

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

פונקציית Event Listener לדחיפה של 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.waitUntil() בדוגמה הזו. ה-method הזה מקבל promise ומאריך את משך החיים של בורר האירועים (או אפשר לחשוב עליו כגורם שמחזיק את ה-service worker בחיים), עד שה-promise מוגדר. במקרה הזה, ה-promise שמוענק ל-event.waitUntil הוא ה-Promise שהוחזר מ-showNotification().

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

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

שליחת הודעת Push

נרשמנו להתראות ודפדפן השירות שלנו מוכן להציג התראה, אז הגיע הזמן לשלוח הודעת Push דרך FCM.

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

כששולחים את המשתנה PushSubscription.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, בהתאם לשליחת הנתונים כ-JSON או כנתוני טופס.
  • מערך של registration_ids – אלה מזהי הרישום שצריך לחלץ מנקודות הקצה של המשתמשים.

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

מחליפים את הערכים &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.

אחד החסרונות של ההטמעה הנוכחית של Push API ב-Chrome הוא שאין אפשרות לשלוח נתונים עם הודעת דחיפה. לא, לא. הסיבה לכך היא שבהטמעה עתידית, נתוני עומס היישום יצטרכו להיות מוצפנים בשרת לפני שהם יישלחו לנקודת קצה לשליחת הודעות דחיפה. כך נקודת הקצה, לא משנה איזה ספק של הודעות דחיפה היא, לא תוכל לראות בקלות את תוכן הודעת הדחיפה. כך אפשר להגן גם מפני נקודות חולשה אחרות, כמו אימות לקוי של אישורי 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(), כלומר שמאזין האירועים לא ייצא עד שהקריאה האסינכרונית ל-fetch() תושלם וההתראה תוצג.

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

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

כשהמשתמש לוחץ על התראה, מתבצעת שליחה של אירוע notificationclick ב-service worker. בתוך הטיפול, תוכלו לבצע פעולה מתאימה, כמו להתמקד בכרטיסייה או לפתוח חלון עם כתובת 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.

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

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

כדי לבטל את המינוי של מכשיר של משתמש, צריך לבצע קריאה ל-method‏ 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 של FCM API, ומחפש תוצאות של error:NotRegistered ו-canonical_id, כפי שמוסבר במסמכי התיעוד של FCM.

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

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

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

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

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

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

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

אחת מהטיפים הכי טובים שאני יכול לתת למי שרק מתחיל להשתמש ב-service workers היא להשתמש בתיבה 'פתיחת חלון DevTools והשהיית ביצוע JavaScript בהפעלה של service worker לצורך ניפוי באגים'. תיבת הסימון הזו תוסיף נקודת עצירה בתחילת ה-service worker ותשהה את הביצוע. כך תוכלו להמשיך את הביצוע או לעבור על הסקריפט של ה-service worker ולבדוק אם נתקלת בבעיות.

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

אם נראה שיש בעיה בין FCM לבין אירוע ה-push של ה-service worker, אין הרבה מה שאפשר לעשות כדי לנפות באגים בבעיה כי אין דרך לדעת אם 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

בשלב הזה, ניפוי באגים של שירותי עובדים ב-Chrome ל-Android לא ברור. עוברים לכתובת chrome://inspect, מחפשים את המכשיר ומחפשים את הפריט ברשימה שנקרא 'Worker pid:….', שמכיל את כתובת ה-URL של ה-service worker.

צילום מסך שבו מוצג המיקום של שירותי ה-Worker בבדיקה של Chrome

חוויית משתמש של התראות בדחיפה

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

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

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

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

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

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

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

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

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

איך מטמיעים את פרוטוקול Web Push?

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

שאלות נפוצות

איפה נמצאים המפרטים?

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 יוכלו להשתמש ב-API של Firebase Cloud Messaging ‏ (FCM). המטרה היא להשתמש בפרוטוקול Web Push כשהסטנדרט יושלם ו-FCM תוכל לתמוך בו.

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

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

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

אם אתם לא צריכים שליחה ברקע, Web Sockets היא אפשרות מצוינת.

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

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

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

כדי להבטיח שהסקריפט של קובץ השירות (service worker) מגיע מהמקור המיועד ולא מתקפת אדם בתווך, צריך להשתמש במקורות מאובטחים. בשלב זה, המשמעות היא שימוש ב-HTTPS באתרים פעילים, אבל localhost יפעל במהלך הפיתוח.

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

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

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

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

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

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

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

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

האם לא צריך להשתמש ב-Permissions API?

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

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

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

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

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

כאן אפשר למצוא פרטים נוספים.

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

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

כאן אפשר למצוא פרטים נוספים.