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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

כדי שמינוי 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 של האתרים.

כדי להירשם, צריך להפעיל את ה-method Subscription() באובייקט PushManager, שאליו אפשר לגשת דרך ServiceWorkerRegistration.

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

אם השגיאה promise שהוחזרה על ידי השיטה 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';
        }
        });
    });
}

בשלב הזה אפליקציית האינטרנט שלך מוכנה לקבלת הודעת Push, אבל לא יקרה כלום עד שנוסיף קובץ Push event שלנו לקובץ ה-Service Worker.

האזנה לאירועי דחיפה ב-Service Worker

כשמתקבלת הודעת 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
    })
    );
});

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

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

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

שליחת הודעת דחיפה

נרשמת לקבלת הודעות דחיפה, וה-Service Worker מוכן להציג התראה, כך שהגיע הזמן לשלוח אותו דרך 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. בדפדפן רגיל אפשר פשוט לקבל נקודת קצה (endpoint) ולהפעיל אותה בדרך רגילה, והיא תפעל ללא קשר לכתובת ה-URL.

כלומר, תצטרכו לבדוק בשרת אם נקודת הקצה מיועדת ל-FCM, ואם כן, לחלץ את ערך הרישום_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 הם:

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

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

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

דוגמה מלאה יותר לאירוע 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('/');
        }
    })
    );
});

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

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

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

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

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

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

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

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

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

אחד מהטיפים הכי טובים שאפשר לתת לכל מי שלא משתמש ב-service worker הוא להשתמש בתיבת הסימון "Open DevTools window והשהיית הפעלת JavaScript בהפעלה של קובץ השירות לצורך ניפוי באגים". תיבת הסימון הזו תוסיף נקודת עצירה בתחילת ה-Service Worker והשהיית הביצוע, כך תוכלו להמשיך בסקריפט של Service Worker או לבצע צעד עליו ולבדוק אם נתקלתם בבעיות.

צילום מסך שמראה איפה תיבת הסימון של ביצוע ההשהיה נמצאת ב-serviceworker-internal.

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

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

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

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

היתרון בתקן 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 Protocol עם Chrome ו-FCM.

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

איך להטמיע את Web Push Protocol

דפדפן 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 להעברת הודעות (FCM). המטרה היא להשתמש בפרוטוקול Web Push Protocol כשתקן מוגדר באופן סופי ו-FCM יכול לתמוך בו.

למה לא להשתמש ב-Web Sockets או באירועים שנשלחו על ידי שרת (EventSource)?

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

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

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

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

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

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

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

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

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

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

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

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

מהן המגבלות של העברת הודעות Push ב-Chrome?

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

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

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

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

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

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

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

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

פרטים נוספים זמינים כאן.

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

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

פרטים נוספים זמינים כאן.