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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

הקוד הבא מבצע רישום של המשתמש לצורך העברת הודעות בדחיפה:

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';
        }
        });
    });
}

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

נרשמנו להתראות ודף ה-Service Worker שלנו מוכן להציג התראה, אז הגיע הזמן לשלוח הודעת 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 – אלה מזהי הרישום שצריך לחלץ מנקודות הקצה של המשתמשים.

מומלץ לעיין במסמכים כדי ללמוד איך לשלוח הודעות Push מהשרת שלכם, אבל תוכלו להשתמש ב-cURL כדי לבדוק את ה-Service Worker כדי לשלוח הודעות 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.

החיסרון בהטמעה הנוכחית של 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. בתוך ה-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.

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

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

כדי לבטל את המינוי של מכשיר של משתמש, צריך לבצע קריאה ל-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 works. התצוגה הזו לא מספקת הרבה מידע, מלבד שירותי ה-Service Workers שפועלים כרגע.
  2. עוברים לכתובת chrome://serviceworker-internals, ומשם אפשר לראות את המצב של שירותי ה-Worker ולראות שגיאות, אם יש כאלה. הדף הזה הוא זמני עד שכלי הפיתוח יכלול קבוצת תכונות דומה.

אחת מהטיפים הכי טובים שאני יכול לתת למי שרק מתחיל לעבוד עם שירותי עבודה היא להשתמש בתיבה 'פתיחת חלון DevTools והשהיית ביצוע JavaScript בזמן ההפעלה של שירות העבודה לצורך ניפוי באגים'. סימון התיבה הזו יוסיף נקודת עצירה בתחילת ה-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.

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

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

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

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

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

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

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

Web Push Protocol הוא תקן חדש שספקי דחיפה יכולים להטמיע, וכך הם לא צריכים לחשוש מי ספק ה-Push, כך שלא תצטרכו להירשם למפתחות 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 Protocol

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

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

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

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

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

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

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

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

קובצי Service Worker זקוקים למקורות מאובטחים כדי לוודא שהסקריפט של 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', בהודעה החדשה.

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