Web Push Bildirimleri ve Firebase (FCM) Entegrasyonu

İbrahim ÖZGÖN
Arabam Labs
Published in
7 min readOct 9, 2017

Nedir — Ne için kullanılır?

Bir web uygulamasının asenkron olarak kullanıcı ile iletişime geçmesidir. Bir uygulama sunucusundan iletilen bildirim mesajı web uygulamasına ulaşır ve kullanıcıya gösterilir. Kullanıcının sitenizde-uygulamanızda olması gerekmez. Bildirim, tarayıcı açık olduğu sürece kullanıcıya iletilecektir. Eğer bildirim gönderilen kullanıcı tarayıcısı açık değil ise, bildirim servisi gönderdiğimiz bildirimi tutarak, kullanıcı aktif olduğunda gösterecektir.

Bu iş için kullanılan hazır kütüphane seçenekleri -uygulamalar nelerdir?

İnternette yaptığınız ilk aramada alttaki 3. parti seçeneklerden bir kaçını mutlaka bulursunuz:

Biz bu geliştirmemizde Google’ın Firebase Cloud Messaging ürününü kullanmayı tercih ettik.

Maliyeti

Bu ürünü seçerken ki ilk dikkat ettiğimiz yer tabii ki maliyeti oldu :) Firebase ile bildirim gönderimi tamamen ücretsiz.

Kısıtlamalar

Entegrasyona başlamadan, uygulamamızın sadece kullanıcı özel alanları(panelim, sipariş, ilan verme) güvenli bağlantı(https) ile çalışıyordu. Push api dökümanında söylendiği gibi bildirim göndermek istiyorsak tüm uygulamamızı güvenli bağlantıdan çalışır hale getirmemiz gerekiyor.

Eksikler

Fcm in yönetim paneli, gönderimler hakkında ne yazık ki hiçbir bilgi sunmuyor.

Hangi tarayıcılar destekliyor?

Biz bu yazıyı yazarken globalde %73.13 lük bir kullanım desteği sunuluyor. Yine Microsoft Edge tarayıcısının konu üzerinde çalıştığını, Notification desteği MS Edge ve Safari tarayıcılarına da yakın gelecekte eklenecek, hatta Twitter kulislerinde Safari tarafında aktif olarak geliştirildiğini duyduk.

Firebase hesap açma

Konu hakkında bahsettikten sonra hesap açma işlemlerine başlayabiliriz.

Firebase yönetim paneline google hesabı ile giriş yapılıyor. Giriş yaptıktan sonra basitçe yeni uygulama oluştur seçeneğini ve yeni bir proje oluştur seçeneğini seçiyoruz. Önümüze 3 uygulama seçeneği geliyor.

Biz web uygulamasını seçiyoruz. Web uygulaması bize direkt olarak entegrasyon javascript dosyasını veriyor.

Web Sunucusu Entegrasyonu

Web Sunucusu entegrasyonunu projeyi birlikte yaptığımız değerli arkadaşım Emre Çamaşuvi(Github Twitter) anlatıyor olacak.

Öncelikle uygulama yaptığınız sitenin localhost veya https’li bir sayfa olması gerekmekte. Örneğin bildirim pencerelerindeki resimlerde https’li resim linki vermezseniz; bildirimlerinizde resimler görünmez.

Bir de dip not; gizli sekmede de bildirimleri service worker etkileşimlerini göremezsiniz.

Firebase konsolda projenizi açtıktan sonra firebase.js dosyasını sayfaya çağırmak gerekiyor. Biz ilgili .Js dosyasını sayfa yüklendikten sonra çağırıyoruz. Script’in onload eventinde de ilgili configurasyon bilgilerimizle, instance’ımızı yaratıyoruz.

window.addEventListener("load", function(){
loadScriptsWithCb("//www.gstatic.com/firebasejs/3.6.1/firebase.js", function(){
// Initialize Firebase
var firebaseConfig = {
apiKey: "xxxxx",
authDomain: "xxx.firebaseapp.com",
databaseURL: "https://xxx.firebaseio.com",
storageBucket: "xxx.appspot.com",
messagingSenderId: "49952xxx300"
};
if (!firebase.apps.length) {
self.firebaseConstructor = firebase.initializeApp(firebaseConfig);
self.messaging = firebaseConstructor.messaging();
}
});
});

Sıra geldi bildirim iznine. Siteniz UX Fail’a örnek gösterilsin istemezseniz bildirim iznini uygun şekilde almanız şart. Önce kullanıcıya ne için bildirim göndermek istediğinizi tasarımınıza uygun şekilde göstermeli ve sadece kullanıcı kabul ederse; tarayıcının bildirim iznini istemelisiniz. Kullanıcı bir kere “engelle” derse (ki günümüzdeki o kadar istismar edildi ki bu bildirimler; yeni reklam körlüğü bildirimleri engellemek oldu) bunu tersine çevirmeniz çok çok zor.

Kullanıcı gösterim izni istemezse bir süre bu “hayır” seçimini cookie’de tutabilir, ardından tekrar bildirim iznini kullanıcıya doğru yaklaşımla sorabilirsiniz.

Modern tarayıcıların bildirim desteği epeydir var. Siz yine de “progressive enhancement” yaklaşımından hareketle yeni tarayıcılar için önce bildirim tanımlı mı kontrol etmeli, tanımlıysa ve izin sonucu belirsiz ise (ki 3 seçenekten default olanı budur) kullanıcınızı tarayıcısından da izin vermesi için ikna etme aşamasına geçebilirsiniz.

if (!!(<any>this.$window).Notification && (<any>this.$window).Notification.permission === "default")  {
// devam
}

messaging namespace, firebase instance’da tetiklendiği için, messaging.requestPermission() ile tarayıcının native bildirim isteğini tetikleyebiliriz. Firebase kullanmasaydık bu isteği native olarak da Notification.requestPermission() ile tetikleyebilirdik. Dönen sonuç Promise olacağı için .then(callback) ile bir sonraki adıma geçebiliriz.

window.messaging.requestPermission().then(function () {
console.log('Notification izni temin edildi.');
});

Dönen success callback içinde olmak güzel. Bu aşamada öncelikle kullanıcıya “artık bildirim gönderebildiğimizi” ifade eden pazarlama popuplarını cookie, localStorage vs içine yazarak kaldırmak gerekiyor; kaleyi fethettik; gönülleri de fethetmeli.

Sıradaki hamle backend’in firebase servisini kullanması için gereken Token’ı kaydetmek. Bunun için önce firebase messaging’den kullanıcı (izin verdiği tarayıcı) için Token almalıyız.

window).messaging.getToken().then(function (token: any) {
sendTokenToServer(token);
});

ServiceWorkerlara bulaşmadan önce çokca methini duymuştum, kabaca belirtmek gerekirse service workerlar assetleri cacheleme; işbu assetleri örneğin offline durumda bile net varmış gibi sunabilme ve tarayıcı açık/kapalıyken arkaplanda sistemi yormadan çalışıp notification/payloadData göndermemize yarıyor.

Bildirimleri service worker tarafında sunmamız için firebase özelinde sitenizin /root pathinde firebase-messaging-sw.js isimli bir dosya oluşturmanız gerekiyor. Bu firebase instance’ımızın arayacağı kaynak dosyası. Firebase instance oluşturulurken, işbu dosya ismi firebase.js içinden register edildi. Firebase kullanmazsanız, register işlerini sizin halletmeniz gerekiyor.

ServiceWorker scope’u ilk başta garip gelebilir. tarayıcının window’u gibi, serviceWorker’ın “self” diye native bir üst katman objesi var. eventListenerlarımızı self’e ekleyeceğiz. ImportScripts ile firebase’in ilgili js dosyalarını çekip, tekrar instance yaratmamız gerekmekte.

Oluşturduğumuz messaging instance’ın setBackgroundMessageHandler metoduyla token’ı kaydettiğimiz user’a backend’den payload gönderebiliriz. Bizim kullandığımız kodu aşağıda görebilirsiniz. callback içindeki self.registration bizim izin objemiz, showNotification metodu ise zurnanın zırt dediği yer.

messaging.setBackgroundMessageHandler(function(payload) {
let obj = payload.data;
const title = obj.title;
let options = {
body: obj.body,
icon: obj.icon,
timestamp: +new Date,
badge: obj.badge,
data: {
payloadr: {
memberNotificationId: obj.memberNotificationId, click_action: obj.click_action
}
},
path: obj.click_action
}
if (!!obj.imageUrl) {
options["image"] = obj.imageUrl;
}
if (!!obj.type) {
options["tag"] = obj.type;
}
return self.registration.showNotification(title, options)
});

Bu işlem bildirimi gösterecektir; bildirime tıklandığında payload içindeki Url’i açması için ise notificationclick metoduna callback function eklememiz gerekiyor. Biz tıklanan bildirimi, tarayıcıda açmanın yanı sıra fetch Api ile okundu bildirimini endpoint’e token ile vuruyor ve işbu bildirimi “okundu” olarak işaretletiyoruz.

self.addEventListener('notificationclick', function(event) {
let obj = event.notification;
let payloaderData = obj.data.payloadr;
var checkAndMarkAsRead = function(){
if (!payloaderData.memberNotificationId) {
return;
}
var url = new URL(this.origin + "/markasreadUrl"),
params = {Id: payloaderData.memberNotificationId};
messaging.getToken().then(function(token){
params.token = token;
Object.keys(params).forEach(key => url.searchParams.append(key, params[key]))
fetch(url).then(function(response) {
if (!response.ok) return new Error(response);
console.log("ok", response);
}).catch(function(err) {
console.log(err);
});
});
};

event.notification.close();

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){
checkAndMarkAsRead();
return clients.openWindow(payloaderData.click_action);
}
}));
});

Sayfanız arkaplandaysa veya tarayıcınız kapalıyken çağırdığımız metotlar bu şekildeyken, ilgili site tarayıcıda açıkken de Js controller tarafında yine messaging instance üzerinde onMessage metodu ile bildirimleri sayfanız üzerinde de gösterebilirsiniz.

window.messaging.onMessage(function (payload) {
let currentUrl = window.location.href;
let payloaderData = payload.data;
let checkAndMarkAsRead = function(){
if (!payloaderData.memberNotificationId) {
return;
}
let markReadObj = {
Id: payloaderData.memberNotificationId
}
self.httpHelperService.httpPost("/markasreadUrl", markReadObj, (res) => {
console.log("marked As Read");
window.open(payloaderData.click_action, '_blank');
}, (err)=>{
self.DialogService.warning({
error: err.Message
});
}, { isNotLoading: true });
};

self.DialogService.successWithCallback({
message: payloaderData.body,
path: payloaderData.click_action,
title: payloaderData.title,
confirm: "Hemen Görüntüle"
}, checkAndMarkAsRead);
});

Uygulama Sunucusu Entegrasyonu

Github adresinde tüm entegrasyon projesini bulabilirsiniz.

Fcm sunucusu ile iletişim kurmak için basit bir http apimiz var. Kısaca gönderilen nesne serialize edilerek bir url e post isteği yapıyor.

Buraya yolladığımız nesne ise şu şekilde:

public class FcmWebPushNotification : IFcmPushNotificationBase { [JsonProperty(“data”)] 
public IPushNotificationMessageBase Message { get; set; } [JsonProperty(“to”)]
public string Token { get; set; }
}

Görüldüğü üzere token kime gönderileceğini ifade ediyor. Mesajda gösterilecek değerleri ise Message içinde gönderiyoruz.

public class FcmWebNotificationMessage : IPushNotificationMessageBase { 
[JsonProperty(“type”)]
public string Type { get; set; }
[JsonProperty(“imageUrl”)]
public string ImageUrl { get; set; }
[JsonProperty(“click_action”)]
public string ClickAction { get; set; }
[JsonProperty(“title”)]
public string Title { get; set; }
[JsonProperty(“body”)]
public string Body { get; set; }
[JsonProperty(“icon”)]
public string Icon { get; set; }
[JsonProperty(“badge”)]
public string Badge { get; set; }
}

Gönderim işlemini ise Program.cs içinde bulabilirsiniz. Yeni bir api oluşturup nesnemizi gönderiyoruz ve bomm.

var api = new FcmApi();var pushNotification = new FcmWebPushNotification
{
Message = new FcmWebNotificationMessage
{
Type = NotificationType.Type1.ToString(),
Icon = "https://foto.arabam.com/assets/dist/img/notifications/192x192-arabam-ikon.png",
Badge = "https://foto.arabam.com/assets/dist/img/notifications/a-monochrome.png",
ImageUrl = "https://arbimg55.mncdn.com/ilanfotograflari/2017/10/03/7666880/6c98e628-10ce-4482-8f1c-e0e3491d6696_image_for_silan_7666880_1920x1080.jpg",
Title = "Yeni İlan Var!",
Body = "Aramanız için yeni ilan girildi. Hemen inceleyin, hayalinizdeki arabaya kavuşun!",
ClickAction ="https://www.arabam.com"
},
Token = userToken
};
var result = api.Push(authToken, pushNotification);
Masaüstü
Mobil Web

Notification Nasıl Gözükecek?

--

--