Yazının tamamını okuyamıyorsanız tıklayınız.

Photo by Mark König on Unsplash

Photo by Mark König on Unsplash

Hiç, bir bildirimi anında görmek için telefonunuza yöneldiğiniz oldu mu?

Daha doğrusu, kaç defa bir bildirim sesi duyup da telefonunuza bakmadan durabildiniz ki?

Mobil uygulamalara push notification (bundan sonra anlık bildirim ya da bildirim olarak anılacaktır) eklemek, kullanıcı deneyimini geliştiren güçlü bir özelliktir, bilirsiniz. Bu yazıda, Vue.js ve Quasar kullanarak anlık bildirim entegrasyonunu gerçekleştirmek için izlenmesi gereken adımları paylaşıyorum.

Vue.js ve Quasar kullanarak bildirim alma ve gönderme konusuna giriş yaptığım önceki yazımda yaşadığım hisleri çok net ve güzel yazmıştım. O yazıyı okuyup, alkışlarınızı gönderip dönünüz lütfen.😊 Cihaz kimlik bilgisini başarıyla aldıktan sonra sıra asıl mobil bildirim entegrasyonuna gelmişti, işimiz çok zordu ve her zamanki gibi acildi. Haydi başlayalım.

Mobil bildirim entegrasyonu aşağıdaki beş adımdan oluşur:

  • Quasar ve Capacitor ile projeyi hazırlama

  • Firebase projesi oluşturma ve yapılandırma

  • Firebase Token(kimlik anahtarı) işlemleri

  • Bildirim özelliklerini yapılandırma

  • Sunucu tarafından (Backend) bildirim gönderme işlemi

1) Quasar ve Capacitor ile Projeyi Hazırlama

1.1) Projeye Capacitor ekle

Projeye Capacitor desteğini eklemek için şu komutu çalıştırın:

csharp
quasar mode add capacitor

1.2) Projeye Capacitor Device API Kütüphanesini Ekleme

Cihaz bilgilerine (bildirim için gerekli olan benzersiz kimlikler gibi) erişebilmek için Capacitor’ün sunduğu Device API modülünü projeye dahil etmeniz gerekir. Bunun için terminalde şu komutu çalıştırın:

bash
npm install @capacitor/device

1.3) Platform Yapılandırmaları

Capacitor aracılığıyla uygulamanızın Android ve iOS cihazlarda sorunsuz çalışabilmesi için projenizi aşağıdaki adımlarla yapılandırın:

  • Android Platformu Ekleme: Aşağıdaki komutu çalıştırarak Android desteğini projenize dahil edin. Bu işlem, src-capacitor/android dizininde gerekli yapılandırma dosyalarını otomatik olarak oluşturacaktır:

bash
quasar build -m capacitor -T android
  • iOS Platformu Ekleme: Eğer uygulamanıza iOS desteği de kazandırmak istiyorsanız aşağıdaki komutu kullanın. Bu komutla birlikte iOS platformuna özel yapılandırmalar oluşturulacaktır. İşlem tamamlandıktan sonra Xcode üzerinden cihaz bağlantı süreçlerini yönetebilirsiniz:

r
quasar build -m capacitor -T ios

1.4) Senkronizasyon İşlemini Gerçekleştirme

Yaptığınız yapılandırma değişikliklerinin ve eklediğiniz kütüphanelerin platform dosyalarına (Android ve iOS dizinlerine) işlenmesi için senkronizasyon komutunu çalıştırmanız gerekir.

bash
npx cap sync

1.5) Bağımlılıkların ve Senkronizasyonun Manuel Kontrolü

Bazen yüklediğiniz paketlerin platform dizinine tam olarak yansıması için terminal üzerinden src-capacitor/android dizinine giderek ilgili komutları manuel olarak tekrarlamanız gerekebilir. Bunun için şu adımları izleyin:

  • İlgili dizine geçiş yapın:

bash
cd src-capacitor/Android
  • Kütüphaneyi bu dizin içinde tekrar yükleyin:

bash
npm install @capacitor/device
  • Dizini senkronize edin:

bash
npx cap sync

Bu işlemlerin ardından proje kök dizinine geri dönerek Android platformunu tamamen senkronize ettiğinizden emin olun:

bash
npx cap sync android

2) Firebase Projesi Oluşturma ve Yapılandırma

Firebase, anlık bildirim (push notification) özelliği için oldukça güçlü bir altyapı sunar. Bu rehberde, cihazlara bildirim göndermek amacıyla Firebase Cloud Messaging (FCM) servisini kullanacağız. FCM, her bir mobil cihazla kesintisiz iletişim kurmamızı sağlayan bir köprü görevi görür.

Şimdi adım adım Firebase üzerinde yeni bir proje oluşturup gerekli yapılandırmaları yapalım.

Not: Firebase çok köklü bir geçmişe sahip olmasa da arayüzünde sık sık değişiklikler yapabiliyor; bu yüzden ekran görüntüsü ekleyerek rehberin güncelliğini riske atmayacağım. Arayüz burada anlattığımdan farklıysa da siz kurcalayıp bulursunuz artık! :)

2.1) Firebase Console’da Yeni Proje Oluşturma

  • Firebase Console sayfasına gidin ve oturum açın.

  • “Add Project” (Proje Ekle) seçeneğine tıklayın ve projenize bir isim verin.

  • Gerekli izinleri ve proje bilgilerini onaylayarak ilerleyin ve projenizi oluşturun.

2.2) FCM’yi Etkinleştirme

Firebase projesinde anlık bildirim özelliğini kullanabilmek için FCM’yi aktif hâle getirmeliyiz.

  • Project Settings (Proje Ayarları) kısmına gidin ve Cloud Messaging sekmesini bulun.

  • Burada projenin özelliklerini ve izinlerini kontrol edin. FCM genellikle varsayılan olarak aktif gelir, ancak aktif değilse etkinleştirin.

2.3) iOS ve Android Uygulaması Ekleme

Firebase üzerinden bildirim gönderebilmek için hem iOS hem de Android platformlarına özel yapılandırmalar yapmamız gerekiyor.

  • Android Uygulaması Ekleme: “Android” simgesine tıklayın ve paketinize uygun Uygulama Kimliği (Android Package Name) bilgisini girin. Bu bilgi, projenizin android/app/build.gradle dosyasında bulunur.

  • Gerekli yapılandırmaları tamamlayın ve oluşturulan google-services.json dosyasını projenize indirin.

  • google-services.json dosyasını src-capacitor/android/app klasörüne yerleştirin.

  • İndirdiğiniz google-services.json dosyasını src-capacitor/android/app klasörüne yerleştirin.

  • iOS Uygulaması Ekleme: “iOS” simgesine tıklayın ve gerekli bilgileri doldurun. Firebase tarafından oluşturulan GoogleService-Info.plist dosyasını indirip iOS projenizin ilgili klasörüne yerleştirin.

2.4) Firebase SDK ve Anahtarları

Bildirimlerin başarılı bir şekilde gönderilmesi için Firebase projesindeki API ve yapılandırma bilgilerini projemize eklememiz gerekiyor.

  • API Anahtarlarını Alma: Firebase Console’da Project Settings > General sekmesine gidin. Sayfanın alt kısmında yer alan API anahtarlarını ve yapılandırma bilgilerini not alın.

  • SDK Yapılandırması: Gerekli anahtarları Firebase SDK kurulumuna ve quasar.config.js dosyanıza şu formatta ekleyin:

cpp
const firebaseConfig = {
  apiKey: "FIREBASE_API_KEY",
  authDomain: "YOUR_APP.firebaseapp.com",
  projectId: "YOUR_PROJECT_ID",
  storageBucket: "YOUR_APP.appspot.com",
  messagingSenderId: "YOUR_SENDER_ID",
  appId: "YOUR_APP_ID"
};

Bu aşamada Firebase projeniz, hem Android hem de iOS cihazlar için bildirim alabilecek şekilde yapılandırılmış oldu.

3) Firebase Token (Kimlik Anahtarı) İşlemleri

Kaynak: Nursel Teşekkürler emeğine sağlık.

Kaynak: Nursel Teşekkürler emeğine sağlık.

FCM token (bundan sonra kimlik anahtarıolarak anılacaktır), cihazların Firebase Cloud Messaging sunucusuyla kimlik doğrulaması yapmasına olanak tanır. Her cihaz, uygulamanın bildirim alabilmesi için kendine has (yegâne) bir kimlik anahtarı alır. Kullanıcıya ve cihaza özel bildirimler gönderebilmek için bu anahtarın sunucu tarafına iletilmesi gerekir.

Bu kimlik anahtarı şu durumlarda değişebilir:

  • Uygulama cihaza ilk kez kurulduğunda veya kaldırılıp tekrar kurulduğunda,

  • Kimlik anahtarının geçerlilik süresi dolduğunda,

  • Kullanıcı, cihaz ayarlarından uygulama verilerini temizlediğinde,

  • Cihazdaki Google hesabı kaldırıldığında veya yeni bir hesap eklendiğinde,

  • Firebase, güvenlik güncellemeleri veya yapılandırma değişiklikleri nedeniyle yenileme istediğinde,

  • Sunucu tarafındaki manuel müdahalelerle anahtar geçersiz kılındığında.

Bu koşullarda uygulamanız onTokenRefresh veya benzeri bir işleyici (handler) ile FCM’den yeni bir anahtar almalı ve bunu sunucuda güncellemelidir.

Bu bölümde FCM için gerekli kütüphaneleri kullanacak, cihazımızı Firebase’e kaydedecek (register), kimlik anahtarımızı alacak ve bu anahtarın nasıl kullanıldığını öğreneceğiz. İşe kütüphaneleri kurarak başlayalım. Aşağıda projenizdeki package.json dosyasında bulunması gereken bağımlılıkları paylaşıyorum. Gerekli Capacitor ve Firebase kütüphanelerini npm install komutu ile terminalden kurabilirsiniz:

perl
"dependencies": {
    "@capacitor-community/fcm": "^6.0.0",
    "@capacitor/android": "^6.0.0",
    "@capacitor/app": "^6.0.1",
    "@capacitor/core": "^6.0.0",
    "@capacitor/device": "^6.0.0",
    "@capacitor/ios": "^6.0.0",
    "@capacitor/keyboard": "^6.0.0",
    "@capacitor/push-notifications": "^6.0.0",
    "firebase": "^10.8.0",
    "@quasar/extras": "^1.0.0",
    "axios": "^1.6.2",
    .
    .
    .
  },

3.1) Bildirim Gönderebilmek İçin Gereken Cihaz Bilgilerini Sunucu Tarafına İletmek

Öncelikle, ilk yazımda anlattığım gibi cihaz kimlik bilgisine ihtiyacınız olacak. Bunu ve diğer cihaz bilgilerini aşağıdaki kod örneğiyle elde edebilirsiniz.

Buradaki import yöntemine dikkat ettiniz mi? Normalde import kısmı dosyanın en başında olur; ancak bu kodlar sadece mobil cihazlarda (native) çalışacağı için, uygulama web tarafında hata vermesin diye import işlemi bir platform kontrolü altında dinamik olarak yapılıyor:

javascript
import { Platform } from 'quasar'
.
.
.
if (Platform.is.nativeMobileWrapper) {
  const { Device } = await import("@capacitor/device");
  const { FCM } = await import("@capacitor-community/fcm");
  const deviceId = await Device.getId()
  const deviceInfo = await Device.getInfo()
  const sdkVersion = deviceInfo.osVersion // (android) 31 veya (iOS) 17.2.1
  const deviceType = deviceInfo.platform // android veya iOS

  // Bu bilgiler sunucu tarafına iletilir.

3.2) Firebase Kimlik Anahtarı Alabilmek İçin Yazılması Gereken Kodlar

Aslında işimizin en önemli kısmı bu başlık altında işlenecek. Farklı dosyalara eklenecek kritik kodlar, işin çok büyük bir kısmını sırtlayacak.

  • quasar.config yapılandırması:

boot satırına aşağıdaki dosya ismini eklememiz gerekiyor:

vbnet
boot: ["PushNotificationListeners.js",...],
  • src/boot dosyası oluşturma:

Yukarıda belirttiğimiz PushNotificationListeners.js isimli dosyayı src/boot klasörüne ekleyeceğiz. Anlık bildirim gelmeden önceki hazırlık ve geldikten sonraki süreç tamamen bu sınıf üzerinden yönetilecek:

javascript
import { boot } from "quasar/wrappers";
import { LocalStorage, Platform } from "quasar";
import { FCM } from "@capacitor-community/fcm";
import { PushNotifications } from "@capacitor/push-notifications";
import { Device } from "@capacitor/device";

export default boot(async ({ app, router }) => {

  const handleNotificationAction = async (notificationData) => {
    const data = notificationData.data;

    if (data && data.id) {
      await router.isReady();
      router.push({name: "ArticleDetail", query: { id: data.id }});
    }
  };

  const addListeners = async () => {
    try {
      await PushNotifications.addListener("registration", (token) => {
        console.info("Registration token: ", token.value);
      });

      await PushNotifications.addListener("registrationError", (err) => {
        console.error("Registration error: ", err.error);
      });

      await PushNotifications.addListener("pushNotificationReceived",
        (notification) => {
          if (process.env.NODE_ENV === "development") {
            console.log("Push notification received: ",
               JSON.stringify(notification, null, 2));
          }
        }
      );

      await PushNotifications.addListener("pushNotificationActionPerformed",
        (notification) => {
          handleNotificationAction(notification.notification);
        }
      );
    } catch (error) {
      console.error("Error adding listeners: ", error);
    }
  };

  const checkNotificationPermissions = async () => {
    try {
      let permStatus = await PushNotifications.checkPermissions();
      if (permStatus.receive === "prompt") {
        permStatus = await PushNotifications.requestPermissions();
      }
      return permStatus.receive === "granted";
    } catch (error) {
      console.error("Error checking permissions: ", error);
      return false;
    }
  };

  const registerNotifications = async () => {
    const hasPermission = await checkNotificationPermissions();
    if (!hasPermission) throw new Error("User denied permissions!");

    try {
      await PushNotifications.register();
    } catch (error) {
      console.error("Error registering notifications: ", error);
    }
  };

  const fcmRegister = async () => {
    try {
      const response = await FCM.getToken();
      LocalStorage.setItem("FIREBASE_TOKEN", response.token);
    } catch (error) {
      console.error("Error getting FCM token: ", error);
    }
  };

  if (Platform.is.nativeMobileWrapper) {
    try {
      await addListeners();
      await registerNotifications();
      await fcmRegister();
    } catch (error) {
      console.error("Error in notification setup: ", error);
    }
  }
});

Bu kod bloğu; Quasar uygulamamızın başlatılması sırasında gerekli izinleri alır, bildirim dinleyicilerini ekler ve cihazın FCM kimlik anahtarını alarak sunucu tarafına gönderebilir duruma getirir.

Adım adım inceleyelim. Lütfen bu açıklama kısmını atlamayınız; çünkü yazdığınız kodun ne anlama geldiğini bilmeden “kopyala-yapıştır” yaparsanız ileride anlamak için çok daha fazla vakit kaybedebilirsiniz. Ayrıca iş görüşmesinde, “Bildirim entegrasyonu yaptım,” dediğinizde detay sorarlar (biz de sorarız!), apışıp kalmayın. 😂

3.2.1) Modüllerin ve Bağımlılıkların İçe Aktarılması

Kodumuzun en başında, projemizin ihtiyaç duyduğu araçları sisteme dahil ediyoruz:

javascript
import { boot } from "quasar/wrappers";
import { LocalStorage, Platform } from "quasar";
import { FCM } from "@capacitor-community/fcm";
import { PushNotifications } from "@capacitor/push-notifications";
import { Device } from "@capacitor/device";
  • boot: Quasar uygulamalarında boot dosyaları; uygulama başlatıldığında bir defa yüklenen dosyalardır, bilirsiniz. Bu fonksiyon ile projeye çeşitli başlangıç ayarları eklenebilir.

  • LocalStorage: Tarayıcıda kalıcı veri depolamak için kullanılır. Burada Firebase kimlik anahtarını saklamak için kullandık.

  • Platform: Quasar’ın modülü, cihazın türünü (örneğin, mobil veya masaüstü) belirlemeye yardımcı olur.

  • FCM (Firebase Cloud Messaging): @capacitor-community/fcm modülünden gelen bu kütüphane, anlık bildirim işlevselliğini sağlamak için kullanılan ana yapıdır.

  • PushNotifications: @capacitor/push-notifications modülü, anlık bildirim işlemlerini (izinler, kayıt vb.) yönetmek için kullanılır.

  • Device: @capacitor/device modülü, cihaz bilgilerini almak için kullanılır.

3.2.2) Ana Boot Fonksiyonu:

dart
export default boot(async ({ app, router }) => { ... });
  • Amaç: Quasar uygulaması başlatıldığında otomatik olarak çalışır.

  • · Nesneler (app ve router): Bu nesneler, uygulamanın Vue örneği (instance) ve yönlendirici (router) fonksiyonlarına doğrudan erişim sağlar. Bildirim geldiğinde kullanıcıyı ilgili sayfaya yönlendirebilmek adına bu erişim bizim için hayati önem taşır.

3.2.3) Bildirim Üzerinden Yönlendirme Fonksiyonu:

kotlin
const handleNotificationAction = async (notificationData) => {
  const data = notificationData.data;
  if (data && data.id) {
    await router.isReady();
    router.push({ name: "ArticleDetail", query: { id: data.id } });
  }
};
  • Amaç: Kullanıcı bildirime tıkladığında uygulamayı manuel olarak açmak yerine doğrudan belirli bir sayfaya yönlendirmektir.

data.id: Bildirimin içindeki veri, belirli bir içerik (örneğin, makale detayı) için bir kimlik içerir. Eğer bu kimlik varsa uygulama ArticleDetail adlı sayfaya yönlendirilir ve id sorgu parametresi olarak atanır.

3.2.4) Bildirim Olay Dinleyicileri Ekleme:

javascript
const addListeners = async () => { ... };
  • Amaç: Bildirimlerin çeşitli olaylarını dinlemek ve bunları yönetmektir.

  • registration: Uygulama başarılı bir şekilde kayıt olduğunda bir kimlik anahtarı (token) alır ve bu anahtarı konsolda görüntüler.

  • registrationError: Kayıt sırasında herhangi bir teknik hata oluşursa bu hata ayrıntılarıyla birlikte konsolda gösterilir.

  • pushNotificationReceived: Cihaza bir bildirim ulaştığı anda tetiklenir ve gelen bildirimin içeriğini (başlık, mesaj vb.) konsola yazar.

  • pushNotificationActionPerformed: Kullanıcı bildirime dokunarak etkileşime geçtiğinde tetiklenir, handleNotificationAction fonksiyonunu çağırarak bildirimde yer alan veriye göre gerekli işlemi yapar.

3.2.5) Bildirim İzinlerini Kontrol Etme:

javascript
const checkNotificationPermissions = async () => { ... };
  • Amaç: Uygulamanın anlık bildirim gösterebilmesi için gerekli izinleri kontrol eder.

  • checkPermissions(): Mevcut bildirim izin durumunu kontrol eder. Eğer daha önce izin verilmemişse kullanıcıya bir onay penceresi (prompt) çıkararak izin ister.

  • Sonuç: Eğer kullanıcı bildirimlere izin verirse true, reddederse false değeri döndürür. Bu değer, bir sonraki aşamada kayıt işlemlerinin başlatılıp başlatılmayacağını belirler.

3.2.6) Bildirim Kayıt İşlemi:

javascript
const registerNotifications = async () => { ... };
  • Amaç: Uygulamanın anlık bildirim alabilmesi için gerekli sistem izinlerini sağladıktan sonra, cihazı bildirim sistemine kaydetmektir.

  • checkNotificationPermissions fonksiyonunu çağırır, eğer kullanıcı izin vermemişse bir hata fırlatır. İzin verilmişse PushNotifications.register() komutu ile uygulamayı bildirim sistemine dahil eder.

3.2.7) FCM Kimlik Anahtarı Alma İşlemi:

javascript
const fcmRegister = async () => { ... };
  • Amaç: FCM kimlik anahtarını (token) almak ve bu anahtarı LocalStorage üzerinde saklamaktır.

  • FCM.getToken() kullanılarak cihazın benzersiz kimlik anahtarı alınır. Anahtar başarıyla alındığında LocalStorage içerisinde “FIREBASE_TOKEN” anahtarı altında kaydedilir.

  • Bu kimlik anahtarı, daha sonra sunucuya (backend) gönderilerek kullanıcıya özel bildirimler iletmek için kullanılır.

3.2.8) Platform Kontrolü ve İşlevlerin Çağrılması:

javascript
if (Platform.is.nativeMobileWrapper) {
  try {
    await addListeners();
    await registerNotifications();
    await fcmRegister();
  } catch (error) {
    console.error("Error in notification setup: ", error);
  }
}
  • Amaç: Platform.is.nativeMobileWrapper ile yazdığımız bildirim kodunun yalnızca mobil cihazlarda çalışmasını garanti altına almaktır. Eğer mobilde çalışıyorsa addListeners, registerNotifications ve fcmRegister fonksiyonları çağrılır.

  • Hata Yönetimi: Tüm süreç bir try-catch bloğu içine alınmıştır. Kurulum aşamasında herhangi bir aksilik yaşanırsa uygulama çökmez, bunun yerine hata mesajı konsola yazdırılır.

N eden capacitor/push-notifications paketinden doğrudan kimlik anahtarı alamıyoruz? Bunu aylar önce denemiştim, kodu bu kütüphaneyi kullanarak yazdığımda kimlik anahtarı almayı bir türlü başaramamıştım. Hatırladığım kadarıyla “FCM is not implemented on web.” gibi bir hata almıştım. FCM, notification kodunu tarayıcı için web tabanlı yazmaya çalıştığımı zannediyordu. Bu yüzden capacitor-community/fcm kütüphanesi üzerinden kimlik anahtarı alma kodunu örneklendireceğim. Yazması sadece bir paragraf oldu ama bu kütüphaneyi bulma sürecimizden bir korku romanı çıkardı.

3.3) Belirli Bildirim Konularına Abone Olma ve Abonelikten Çıkma

Firebase Cloud Messaging (FCM), kullanıcılara belirli konulara abone olma ve bu konulardan abonelikten çıkma seçeneği sunarak bildirim deneyimlerini özelleştirmelerine olanak tanır. Bu özellik, bildirimlerin yalnızca ilgili kullanıcı gruplarına gönderilmesini sağlar ve kullanıcıların gereksiz bildirimlerle rahatsız edilmelerinin önüne geçer. Böylelikle kullanıcılar, ilgi alanlarına veya ihtiyaçlarına göre tercihlerini yönetebilirler.

3.3.1) Kullanım Senaryoları

  • Hedef kitleye göre bildirim gönderme: Kullanıcıları belirli gruplara ayırarak her gruba özel bildirimler gönderebilirsiniz. Örneğin, updates konusuna abone olan kullanıcılara yalnızca uygulama güncellemeleri hakkında, special_offers konusuna abone olanlara ise yalnızca kampanya ve indirim bildirimleri gönderilebilir.

  • Kullanıcı tercihlerine göre bildirim yönetimi: Yukarıda söylediğim gibi uygulamanın ayarlar ekranında, kullanıcılar ilgilendikleri bildirim türlerine göre abone olabilir veya abonelikten çıkabilirler. Bu, kullanıcıya kontrol hissi verir ve kişiselleştirilmiş bir deneyim yaşatır.

  • Özel etkinlik ve kampanyalar için: Yalnızca belirli bir dönem (örneğin: summer_sale) için geçici bir konu oluşturulabilir. Böylece kullanıcılar yalnızca o döneme özel bildirimlere odaklanabilirler.

Abonelik İşlemleri için Örnek Kod:

Aşağıdaki kod, FCM kullanarak kullanıcıları belirli konulara (topics) abone yapmayı ve abonelikten çıkarmayı gösterir:

javascript
import { FCM } from "@capacitor-community/fcm";

// Kullanıcıyı belirli bir konuya abone yapma
const subscribeToTopic = async (topic) => {
  try {
    await FCM.subscribeTo({ topic });
    console.log(`Successfully subscribed to topic: ${topic}`);
  } catch (error) {
    console.error(`Error subscribing to topic ${topic}:`, error);
  }
};

// Kullanıcıyı belirli bir konudan abonelikten çıkarma
const unsubscribeFromTopic = async (topic) => {
  try {
    await FCM.unsubscribeFrom({ topic });
    console.log(`Successfully unsubscribed from topic: ${topic}`);
    // Başarıyla abonelikten çıkıldığında kullanıcıya bildirim gösterilebilir
  } catch (error) {
    console.error(`Error unsubscribing from topic ${topic}:`, error);
  }
};

// Örnek kullanım
await subscribeToTopic("updates"); // "updates" adlı konuya abone olur
await unsubscribeFromTopic("updates"); // "updates" adlı konudan abonelikten çıkar
  • subscribeToTopic: Kullanıcıyı belirtilen konu (topic) için abone yapar. Bu konuya gönderilen bildirimler, konuya abone olan tüm cihazlara iletilir. Başarılı bir abonelik sonrası kullanıcıya sürece dair bir geri bildirim verebilirsiniz.

  • unsubscribeFromTopic: Kullanıcıyı belirtilen konu (topic) için abonelikten çıkarır. Kullanıcı, bu işlemi gerçekleştirdikten sonra artık o konudaki bildirimleri almaz. Bu işlem de başarılı olduğunda kullanıcıya abonelikten çıkıldığına dair bir geri bildirim göstermek kullanışlı olur.

3.3.2) Kullanıcı Arayüzünde Abonelik Yönetimi

Bir ayarlar ekranında, kullanıcıya abone olabileceği bildirim türlerini gösteren bir liste veya seçenek sunabilirsiniz.

  • Checkbox veya toggle kullanımı: Her bildirim türünü temsil eden bir checkbox veya toggle butonu ekleyebilirsiniz. Kullanıcı bir toggle butonunu açtığında subscribeToTopic fonksiyonu, kapattığında ise unsubscribeFromTopic fonksiyonu çağrılır.

xml
<template>
  <q-list bordered padding>
    <q-item v-for="topic in topics" :key="topic.name" clickable>
      <q-item-section>
        <q-checkbox
          v-model="topic.subscribed"
          @update:model-value="onToggleTopic(topic)"
          :label="topic.label"
        />
      </q-item-section>
    </q-item>
  </q-list>
</template>

<script>
import { ref } from "vue";
import { subscribeToTopic, unsubscribeFromTopic } from "./path/to/fcmUtils";

export default {
  setup() {
    // Bildirim konuları tanımlanır
    const topics = ref([
      { name: "updates", label: "Güncellemeler Bildirimi", subscribed: false },
      { name: "special_offers", label: "Özel Teklifler Bildirimi", subscribed: false },
    ]);

    // Konu aboneliğini yönetme işlevi
    const onToggleTopic = async (topic) => {
      try {
        if (topic.subscribed) {
          await subscribeToTopic(topic.name);
          console.log(`${topic.label} konusuna abone olundu.`);
        } else {
          await unsubscribeFromTopic(topic.name);
          console.log(`${topic.label} konusundan abonelik kaldırıldı.`);
        }
      } catch (error) {
        console.error(`Error toggling subscription for ${topic.name}:`, error);
      }
    };

    return { topics, onToggleTopic };
  },
};
</script>
  • topics dizisi: Bildirim konularını; bu konuların name, label ve subscribed durumlarını içeren bir dizi olarak tanımladık. Bu sayede, yeni bir bildirim türü eklemek için yalnızca topics dizisine bir nesne eklemek yeterlidir.

  • v-for ve q-checkbox: v-for döngüsüyle her bildirim konusu için bir q-checkbox bileşeni oluşturuluyor. Bu checkbox bileşeni v-model ile subscribed durumuna bağlanmıştır, bu da kullanıcının işaretleme durumunu otomatik olarak günceller.

  • onToggleTopic fonksiyonu: Checkbox işaretlendiğinde veya kaldırıldığında bu fonksiyon çalışır. Eğer subscribed değeri true ise subscribeToTopic fonksiyonu çağrılarak abone olunur, değilse unsubscribeFromTopic ile abonelik kaldırılır.

Bu yapı, Quasar’ın q-checkbox ve q-list bileşenlerini kullanarak daha dinamik, düzenli ve genişletilebilir bir çözüm sunar. Tercihe göre bu bildirim abonelik verileri sunucu (backend) tarafında da tutulabilir.

3.3.3) Bildirim Kanallarını Yönetirken Dikkat Edilmesi Gerekenler

  • Geri bildirim: Kullanıcı abone olduğunda veya abonelikten çıktığında ekranda kısa bir geri bildirim (örneğin: “Güncellemeler kanalına abone olundu.”) göstermek, kullanıcı deneyimini geliştirir.

  • İzin ve onay: Kullanıcının ilgisini çekmeyen bildirimlerden rahatsız olmaması için kullanıcıdan onay alınarak bildirim gönderilmesi, uygulamanın daha kullanıcı dostu olmasını sağlar.

  • Abonelik durumu saklama: Kullanıcının abone olduğu konuları LocalStorage veya veritabanında saklayarak uygulama tekrar açıldığında bu bilgilerin güncel kalmasını sağlayabilirsiniz.

javascript
// Abonelik durumu saklanır:
LocalStorage.setItem("isSubscribedToUpdates", true);
// Abonelik durumu kontrol edilir:
const isSubscribed = LocalStorage.getItem("isSubscribedToUpdates");

Bu bölümle birlikte, Firebase Cloud Messaging kullanarak kullanıcılarınızın ilgi alanlarına uygun bildirim türlerine abone olmalarını veya bu aboneliklerden çıkmalarını sağladık. Bu yaklaşım, kullanıcıların yalnızca ilgilerini çeken bildirimleri almasına yardımcı olur, gereksiz bildirimleri azaltır ve uygulamanızın kullanıcı deneyimini iyileştirir.

Photo by Brian J. Tromp on Unsplash

Photo by Brian J. Tromp on Unsplash

4) Bildirim Özelliklerini Yapılandırma

Bu bölümde, kullanıcı izinlerinin ötesinde bildirimlerin etkin bir şekilde yönetilmesini sağlayacak diğer ayarlara ve yapılandırmalara odaklanacağız. Uygulamanın bildirimleri doğru bir şekilde işlemesi ve kullanıcıya etkili bir deneyim sunması için yapılacaklar:

4.1) Bildirim Kategorileri ve Öncelik Ayarları

  • Bildirim türleri: Bildirimleri kategorize etmek, kullanıcı deneyimini kişiselleştirir. Örneğin, “güncellemeler”, “mesajlar” veya “hatırlatmalar” gibi farklı bildirim türleri için ayrı kategoriler oluşturulabilir. Bu, bildirimlerin ayırt edici bir şekilde işlenmesine olanak tanır.

  • Öncelik seviyeleri: Android ve iOS platformlarında bildirimlerin öncelik seviyelerini belirleyebiliriz. Örneğin, önemli bildirimlere yüksek öncelik verilebilirken daha az önemli bildirimler düşük öncelikte tutulabilir.

4.2) Sessiz Bildirimler (Silent Notifications)

  • Sessiz bildirimler, kullanıcıyı rahatsız etmeden arka planda veri güncellemeleri için kullanılır. Örneğin, yeni mesaj geldiğinde kullanıcıya sesli veya görsel bir bildirim göndermeden sadece veri güncellemesi yapmak için bu tür tercih edilebilir.

  • Sessiz bildirimler için, sunucu tarafından gönderilen bildirime “content-available”: 1 gibi bir parametre eklenir.

4.3) Bildirim Sesleri ve Özel Ringtonelar

  • Kullanıcının ilgisini çekecek şekilde farklı bildirim sesleri veya özel zil sesleri (ringtones) ayarlayabilirsiniz. Örneğin, mesajlar için standart bir ses belirlerken acil durumlar için farklı bir ses eklemek etkili olabilir.

  • Android ve iOS üzerinde özel ses dosyaları belirlenerek uygulama dizinine yerleştirilebilir.

4.4) Bildirim Görselleri ve Simgeler

  • Bildirimlerde küçük simgeler veya büyük görseller kullanarak mesajı daha dikkat çekici hâle getirmek mümkündür.

  • Büyük resimler: Örneğin, ürün güncellemelerini veya haber içeriklerini büyük görsellerle göstermek için Android’de bigPicture ve iOS’ta attachments gibi özellikler kullanılabilir.

  • Küçük simgeler: Bildirimlerde küçük simge kullanımı, özellikle Android’de gereklidir. Quasar’da uygulama simgelerini veya özel simgeleri res dizininde uygun boyutlarda ayarlayabilirsiniz.

4.5) Bildirimlerin Zamanlaması ve Geciktirilmesi

  • Bazı bildirimlerin belirli bir zamana göre veya bir etkinlik sonrasında gösterilmesi gerekebilir. Örneğin, kullanıcı uygulamayı uzun süre açmamışsa belirli bir gün sonra hatırlatma gönderilmesi için zamanlanmış bildirimler kullanılabilir.

  • Bildirim zamanlama, özellikle hatırlatma veya etkinlik tabanlı süreçlerde oldukça yararlıdır. Bu özellik, sunucu veya Firebase üzerinde programlanarak belirli bir süre sonra tetiklenir.

4.6) Bildirim Gösterim Sıklığı Yönetimi

  • Kullanıcının aşırı bildirim almasını engellemek için belirli bir sınırlama getirilebilir. Örneğin, bir kullanıcının aynı türde bildirimleri art arda almasını engellemek için sunucu tarafında bir sınırlama yapılabilir veya uygulama içinde zaman damgası tutulabilir.

  • Sıklık Sınırlaması: Günlük veya saatlik bir limit koyarak bildirimlerin sıklığını kontrol etmek, kullanıcı deneyimini iyileştirir.

4.7) Çoklu Dil Desteği (Localization)

  • Bildirimlerin, uygulamanın dil ayarına göre farklı dillerde gösterilmesini sağlamak için çoklu dil desteği ekleyebilirsiniz. Firebase veya sunucu üzerinden gönderilen bildirimlerde “locale” bilgisiyle dil ayarlaması yapılabilir.

  • Bu yapı, özellikle uluslararası kullanıcılara hitap eden uygulamalarda kullanıcı dostu bir deneyim sağlar.

4.8) Kapatılabilir ve Tekrar Eden Bildirimler

  • Kapatılabilir bildirimler: Bazı bildirimler, belirli bir süre sonra otomatik olarak kapatılacak şekilde ayarlanabilir. Örneğin, indirme işlemi tamamlandığında gösterilen bilgilendirme gibi.

  • Tekrar eden bildirimler: Hatırlatma veya etkinlik bildirimlerinde belirli aralıklarla tekrar eden bildirimler planlanabilir. Bu yapılandırma, özellikle günlük veya haftalık hatırlatma gerektiren uygulamalarda kullanışlıdır.

Önemli not: Bu (zamanlanmış/tekrar eden) bildirimler genellikle Firebase üzerinden yönetilmez. Android ya da iOS tarafında yerel (native) olarak geliştirilir, gösterilir ve yönetilir.

4.9) Uygulama İçi Bildirim İşleme (In-App Notification Handling)

  • Uygulama açıkken gelen bildirimleri yönetmek için uygulama içi bildirim işleme ekleyebilirsiniz. Örneğin, kullanıcı uygulama içinde aktifken gelen bildirimler, bildirim paneline gönderilmek yerine uygulama içinde gösterilebilir.

  • Uygulama içi bildirimler, kullanıcının dikkatini doğrudan uygulama üzerinden çekmek için idealdir ve özellikle sohbet veya sosyal medya uygulamalarında yaygın olarak kullanılır.

Bu yapılandırmalarla, bildirimlerin daha kullanıcı dostu bir şekilde yönetilmesini ve özelleştirilmesini sağlayabiliriz. Bu bölümdeki adımlar, kullanıcı deneyimini artırarak uygulamanızın bildirim özelliklerini daha verimli hâle getirir. Üstteki maddeleri kapsayan Quasar ve Vue 3 kullanılarak yazılmış bir örnek kod parçası paylaşayım:

javascript
const scheduleLocalNotification = async () => {
    const locale = LocalStorage.getItem("user_locale") || "en"; // Dil desteği için
    await PushNotifications.schedule({
      notifications: [
        {
          title: locale === "en" ? "Reminder" : "Hatırlatma",
          body: locale === "en" ? "Check the new updates!" : "Yeni güncellemeleri kontrol edin!",
          id: 1,
          schedule: { at: new Date(new Date().getTime() + 10000) }, // 10 saniye sonra
          sound: "default",
          attachments: [{ id: "bigImage", url: "https://example.com/image.jpg" }], // Büyük görsel
          actionTypeId: "OPEN_APP",
          extra: { screen: "Home" },
        },
      ],
    });
  };

Geliştirme sırasında notification’ın bir türlü gitmediği anları, ekranda boş bir pencere görmeyi, Firebase’in test panelindeki o kütüklüğü veya sürekli aynı notification’a maruz kalmayı eminim siz de deneyimlemişsinizdir; nitekim bu rehberi okuyorsanız bunları yaşamanız kaçınılmazdır. Bildirimin gelmesini sabırsızlıkla beklerken bir yandan kodları gözden geçirmek, nerede hata yaptığınızı bulmaya çalışmak, hatta bildirim daha düşmeden bug yakalayıp çözmek ve bildirimin gelmeyeceğini anlamak…

İşte o anlar, insanın sabrını gerçekten zorlayan anlar. Sabredeceksiniz, bu uzuuun yazıyı bitirene kadar okuyacaksınız ve başarı sizin olacak. Bir yandan doğru çözümü bulmanın verdiği heyecanı yaşarken öte yandan bildirimlerin tıkır tıkır çalıştığını görmek müthiş bir tatmin duygusu veriyor. İşte bu yazıda, o sinir bozucu anları minimuma indirip süreci daha keyifli hâle getirecek adımları sizinle paylaştım. Sadece Quasar ve Vue.js ile anlık bildirim geliştirmesini merak eden okurlarım 5 numaralı başlığı atlayabilir ve sonuç kısmına geçebilir.

5) Sunucu Tarafından Bildirim Gönderme İşlemi

Sunucu üzerinden bildirim gönderme konusunda çok fazla kaynak olduğu için bu başlığı çok uzun tutmayacağım. Ancak geliştirme sürecinin her aşamasında şahsen bulunduğum ve kodlama yaptığım için, bu adımı da rehberde kısaca işleyeceğim.

Eh, takım lideriyim artık! Tüm geliştirmelere hem bizzat “maydonoz olmak” ve ekibime temiz kod yazdırmak hem de yazımında bulunmasam bile yukarıdan şöööyle bir bakıp code review yapmak, çok severek yaptığım iş tanımımın bir parçası.

Sunucu tarafından bildirim gönderebilmek için Firebase Admin SDK’sını kullanarak FCM’ye bağlanmamız gerekir. Bu SDK, sunucu ortamında Firebase ile güvenli bir şekilde iletişim kurmamızı sağlar. Bu örnekte, Node.js ortamında bildirim gönderme işlemi yapacağız. Gerekli adımlar:

5.1) Firebase Admin SDK Kurulumu

Firebase Admin SDK’yı yüklemek için aşağıdaki komutu kullanın:

javascript
npm install firebase-admin

5.2) Firebase Hizmet Hesabı (Service Account) Ayarları

Firebase Console üzerinden bir hizmet hesabı JSON dosyası oluşturun. Bu dosya, sunucunun Firebase ile kimlik doğrulaması yapabilmesini sağlar. Dosyayı projede güvenli bir konuma kaydedin.

5.3) Firebase Admin SDK’yı Yapılandırma

Firebase hizmet hesabı dosyasını kullanarak Firebase Admin SDK’yı başlatın:

php
const admin = require("firebase-admin");
// Hizmet hesabı JSON dosyasını yükleyin
const serviceAccount = require("./path/to/serviceAccountKey.json");
// Firebase Admin SDK'yı başlatın
admin.initializeApp({
    credential: admin.credential.cert(serviceAccount)
});

5.4) Bildirim Gönderme İşlemi

Firebase Admin SDK aracılığıyla belirli bir kullanıcıya veya belirli bir konuya bildirim gönderebilirsiniz.

5.4.1) Tek Bir Cihaza Bildirim Gönderme

Tek bir cihaza bildirim göndermek için cihazın FCM kimlik anahtarına (token) ihtiyacınız vardır. Örneğin, bir kullanıcının registrationToken adlı FCM kimlik anahtarı varsa aşağıdaki kodla gönderim yapabilirsiniz:

javascript
const sendNotificationToDevice =
  async (registrationToken, title, body) => {
    const message = {
      notification: {
        title: title,
        body: body,
      },
      token: registrationToken,
    };
javascript
try {
    const response = await admin.messaging().send(message);
    console.log("Successfully sent message:", response);
  } catch (error) {
    console.error("Error sending message:", error);
  }
};
// Örnek kullanım
sendNotificationToDevice(
  "cihaz_fcm_tokeni",
  "Yeni Güncelleme!",
  "Yeni özellikler eklendi. Şimdi kontrol edin!");

5.4.2) Belirli Bir Konuya (Topic) Abone Olan Kullanıcılara Bildirim Gönderme

Belirli bir konuya abone olan tüm kullanıcılara toplu bildirim göndermek için topic özelliğini kullanabilirsiniz. Bu yapı, ilgili konuyu takibe alan tüm kullanıcıları hedef alır.

javascript
const sendNotificationToTopic = async (topic, title, body) => {
  const message = {
    notification: {
      title: title,
      body: body,
    },
    topic: topic,
  };
javascript
try {
    const response = await admin.messaging().send(message);
    console.log(`Successfully sent message to topic ${topic}:`, response);
  } catch (error) {
    console.error(`Error sending message to topic ${topic}:`, error);
  }
};
// Örnek kullanım
sendNotificationToTopic(
  "updates",
  "Yeni Güncelleme!", "Tüm yenilikler burada, şimdi göz atın!");

5.4.3) Bildirime Veri Eklemek

Bildirimlere ekstra veri ekleyerek uygulama içinde daha kapsamlı işlemler yapılmasını sağlayabilirsiniz. Örneğin, belirli bir makale kimliği (id) ile gönderim yaparak kullanıcıyı doğrudan ilgili içeriğe yönlendirebilirsiniz.

kotlin
const sendDataNotification = async (registrationToken, title, body, data) => {
  const message = {
    notification: {
      title: title,
      body: body,
    },
    data: data,
    token: registrationToken,
  };
javascript
try {
    const response = await admin.messaging().send(message);
    console.log("Successfully sent data message:", response);
  } catch (error) {
    console.error("Error sending data message:", error);
  }
};
// Örnek kullanım
sendDataNotification(
  "cihaz_fcm_tokeni",
  "Yeni Makale Yayında!",
  "Bu haftaki özel yazıyı kaçırmayın.",
  { id: "12345" }
);

Kod Açıklamaları

  1. sendNotificationToDevice: Bu fonksiyon, belirli bir cihaza registrationToken kullanarak bildirim gönderir.

  2. sendNotificationToTopic: Bu fonksiyon, belirli bir topic değerine abone olan tüm cihazlara bildirim gönderir. Bu yöntem, toplu gönderimler için idealdir.

  3. sendDataNotification: Bu fonksiyon, bildirimle birlikte ekstra veri (data) gönderir. Bu veri, uygulamada bildirim tıklanma işlemine göre ek işlevler (yönlendirme vb.) yapılmasını sağlar.

Kullanım Önerileri ve En İyi Uygulamalar

  • Bildirim sıklığını kontrol edin: Kullanıcılara çok sık bildirim göndermemeye özen gösterin. Gereksiz bildirimler kullanıcıların ilgisini azaltabilir.

  • Konu bazlı abonelikler: Kullanıcıların yalnızca ilgilendikleri konulara abone olmalarını sağlayarak daha hedefli bildirimler gönderebilirsiniz.

  • Veri güvenliği: Bildirimlere hassas veriler eklemekten kaçının, bildirim ile gönderilen tüm veriler cihaz tarafından okunabilir.

  • Başarılı gönderim logları: Bildirimlerin başarıyla gönderilip gönderilmediğini izlemek için log tutun. Bu, bildirimlerin etkinliğini analiz etmenize yardımcı olur.

Şahsen çok fazla bildirim gönderen bir uygulama gözüme çarptığı zaman, gelen iletiye basılı tutarak uygulamayı sessize alıyor ve bir daha hiçbir uyarısını ekranımda görmüyorum. Bu bölümle birlikte, uygulamanın sunucu tarafında kullanıcıya veya konuya özel bildirim gönderme süreçlerini detaylandırmış olduk.

Sonuç

Bu kılavuzda, Vue.js ve Quasar kullanarak bir mobil uygulamaya Firebase Cloud Messaging (FCM) ile bildirim entegrasyonu yapmanın adımlarını inceledik. İlk olarak Quasar ve Capacitor yapılandırmasını tamamladık, ardından Firebase projesini oluşturduk ve FCM kimlik anahtarı işlemlerini gerçekleştirdik. Kullanıcıların ilgi alanlarına göre iletiler alabilmeleri için konulara abone olma ve abonelikten çıkma işlemlerini nasıl yönetebileceğimizi gösterdik. Son olarak, sunucu tarafında belirli bir kullanıcıya veya konuya yönelik bildirim gönderme işlemlerini ele aldık.

Bu adımları tamamlayarak uygulamanızda kullanıcıya özel, konu bazlı ve hedeflenmiş bildirimler sunabilir; böylece kullanıcı deneyimini artırabilirsiniz. Bildirim entegrasyonunun başarılı olması, kullanıcı katılımını ve aktivitesini artırarak uygulamanın etkinliğini yükseltir. Gönderim sıklığına ve kullanıcı tercihlerine saygı göstererek kullanıcı dostu bir bildirim deneyimi sunmayı unutmayın. İleride uygulamanızın bildirim altyapısını genişletmek isterseniz bu temel adımları kullanarak daha sofistike bir ileti yönetim sistemi kurabilirsiniz.