Tüm Yazılar
KategoriBackend
Okuma Süresi
21 dk
Yayın Tarihi
...
Kelime Sayısı
1.528kelime

Kahveni hazırla - bu içerikli bir makale!

Firestore ileri seviye sorgular, offline persistence, güvenlik kuralları, Cloud Functions, Remote Config ve maliyet optimizasyonu stratejileri.

Firebase İleri Seviye: Offline-First, Security Rules ve Cloud Functions

Firebase, Google'ın Backend-as-a-Service (BaaS) platformudur. Firestore, Authentication, Cloud Functions, Remote Config — tümü entegre çalışır. Bu rehberde temel CRUD'un ötesine geçerek, production-grade Firebase kullanımını öğreneceksin.

💡 Hızlı Not: Firebase iOS SDK 10.0+ ile Swift Concurrency (async/await) tam desteklenir. Bu rehber güncel API'ları kullanır.

İçindekiler

  1. Firestore İleri Seviye Sorgular
  2. Offline-First Architecture
  3. Security Rules Deep Dive
  4. Cloud Functions ile Backend Logic
  5. Authentication: Custom Claims ve MFA
  6. Remote Config ve A/B Testing
  7. Firebase Extensions
  8. Maliyet Optimizasyonu
  9. Monitoring ve Analytics
  10. Best Practices

Firestore İleri Seviye Sorgular {#firestore-advanced}

swift
1import FirebaseFirestore
2 
3// Composite query (birden fazla alan filtresi)
4func getActiveExpensiveProducts() async throws -> [Product] {
5 let snapshot = try await Firestore.firestore()
6 .collection("products")
7 .whereField("isActive", isEqualTo: true)
8 .whereField("price", isGreaterThan: 100)
9 .order(by: "price", descending: true)
10 .limit(to: 20)
11 .getDocuments()
12 
13 return snapshot.documents.compactMap { try? $0.data(as: Product.self) }
14}
15 
16// Collection group query (tüm subcollection'ları tara)
17func getAllReviews(rating: Int) async throws -> [Review] {
18 let snapshot = try await Firestore.firestore()
19 .collectionGroup("reviews")
20 .whereField("rating", isGreaterThanOrEqualTo: rating)
21 .getDocuments()
22 
23 return snapshot.documents.compactMap { try? $0.data(as: Review.self) }
24}
25 
26// Pagination ile cursor-based sayfalama
27class PaginatedFetcher {
28 private var lastDocument: DocumentSnapshot?
29 
30 func fetchNextPage() async throws -> [Product] {
31 var query = Firestore.firestore()
32 .collection("products")
33 .order(by: "createdAt", descending: true)
34 .limit(to: 20)
35 
36 if let last = lastDocument {
37 query = query.start(afterDocument: last)
38 }
39 
40 let snapshot = try await query.getDocuments()
41 lastDocument = snapshot.documents.last
42 
43 return snapshot.documents.compactMap { try? $0.data(as: Product.self) }
44 }
45}
46 
47// Real-time listener
48func listenToOrders() -> ListenerRegistration {
49 return Firestore.firestore()
50 .collection("orders")
51 .whereField("userId", isEqualTo: Auth.auth().currentUser!.uid)
52 .addSnapshotListener { snapshot, error in
53 guard let documents = snapshot?.documents else { return }
54 let orders = documents.compactMap { try? $0.data(as: Order.self) }
55 self.orders = orders
56 }
57}

Offline-First Architecture {#offline-first}

swift
1// Firestore otomatik offline persistence (varsayılan açık, 100MB cache)
2let settings = FirestoreSettings()
3settings.cacheSettings = PersistentCacheSettings(sizeBytes: 200 * 1024 * 1024) // 200MB
4Firestore.firestore().settings = settings
5 
6// Offline yazma — queue'ya alınır, bağlantı gelince sync olur
7func saveOrder(_ order: Order) async throws {
8 try Firestore.firestore()
9 .collection("orders")
10 .document(order.id)
11 .setData(from: order)
12 // Offline'da bile çalışır - queue'ya alınır
13}
14 
15// Cache vs server source belirt
16func getProduct(id: String, source: FirestoreSource = .default) async throws -> Product {
17 let doc = try await Firestore.firestore()
18 .collection("products")
19 .document(id)
20 .getDocument(source: source) // .cache, .server, .default
21 .data(as: Product.self)
22 return doc
23}

Security Rules Deep Dive {#security-rules}

javascript
1// firestore.rules
2rules_version = '2';
3service cloud.firestore {
4 match /databases/{database}/documents {
5 // Users - sadece kendi profili
6 match /users/{userId} {
7 allow read: if request.auth != null;
8 allow write: if request.auth.uid == userId;
9 }
10 
11 // Products - herkes okuyabilir, admin yazabilir
12 match /products/{productId} {
13 allow read: if true;
14 allow write: if request.auth.token.admin == true;
15 
16 // Validation
17 allow create: if request.resource.data.name is string
18 && request.resource.data.name.size() > 0
19 && request.resource.data.price is number
20 && request.resource.data.price > 0;
21 }
22 
23 // Orders - sadece kendi siparişleri
24 match /orders/{orderId} {
25 allow read: if request.auth.uid == resource.data.userId;
26 allow create: if request.auth.uid == request.resource.data.userId
27 && request.resource.data.items.size() > 0;
28 allow update: if false; // Sipariş değiştirilemez
29 }
30 }
31}

Cloud Functions ile Backend Logic {#cloud-functions}

typescript
1// functions/src/index.ts
2import * as functions from 'firebase-functions';
3import * as admin from 'firebase-admin';
4admin.initializeApp();
5 
6// Yeni sipariş oluşturulduğunda
7export const onOrderCreated = functions.firestore
8 .document('orders/{orderId}')
9 .onCreate(async (snap, context) => {
10 const order = snap.data();
11 
12 // Stok düş
13 for (const item of order.items) {
14 await admin.firestore()
15 .collection('products')
16 .doc(item.productId)
17 .update({ stock: admin.firestore.FieldValue.increment(-item.quantity) });
18 }
19 
20 // Email gönder
21 await sendOrderConfirmation(order.userId, context.params.orderId);
22 
23 // Push notification
24 const user = await admin.firestore().collection('users').doc(order.userId).get();
25 const token = user.data()?.fcmToken;
26 if (token) {
27 await admin.messaging().send({
28 token,
29 notification: {
30 title: 'Sipariş Onayı',
31 body: 'Sipariş #' + context.params.orderId + ' alındı!',
32 },
33 });
34 }
35 });
36 
37// Scheduled function - günlük temizlik
38export const dailyCleanup = functions.pubsub
39 .schedule('every 24 hours')
40 .onRun(async () => {
41 const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
42 const old = await admin.firestore()
43 .collection('logs')
44 .where('createdAt', '<', thirtyDaysAgo)
45 .get();
46 const batch = admin.firestore().batch();
47 old.docs.forEach(doc => batch.delete(doc.ref));
48 await batch.commit();
49 });

Authentication: Custom Claims ve MFA {#auth-advanced}

swift
1// Custom claims ile role-based auth
2// Cloud Function ile claim ata
3// admin.auth().setCustomUserClaims(uid, { admin: true })
4 
5// iOS'ta claim kontrol
6func checkAdminAccess() async throws -> Bool {
7 guard let user = Auth.auth().currentUser else { return false }
8 let result = try await user.getIDTokenResult()
9 return result.claims["admin"] as? Bool ?? false
10}

Remote Config ve A/B Testing {#remote-config}

swift
1import FirebaseRemoteConfig
2 
3func setupRemoteConfig() {
4 let config = RemoteConfig.remoteConfig()
5 config.setDefaults(["new_feature_enabled": false as NSObject])
6 config.fetchAndActivate { status, error in
7 let isEnabled = config.configValue(forKey: "new_feature_enabled").boolValue
8 if isEnabled { self.showNewFeature() }
9 }
10}

Maliyet Optimizasyonu {#cost-optimization}

Teknik
Tasarruf
Nasıl
Pagination
%60-80
limit(to:) kullan, hepsini çekme
Cache
%30-50
Offline persistence aktif
Denormalization
%40
Join azalt, veriyi düzleştir
Listener temizliği
%20
removeListener ekran kapanınca
Cloud Functions batch
%50
Bireysel yerine toplu işlem

Firebase Extensions {#extensions}

Firebase Extensions, hazır çözümlerle geliştirme sürecini hızlandırır:

bash
1# Firebase Extensions kurulumu
2firebase ext:install firebase/firestore-send-email
3firebase ext:install firebase/storage-resize-images
4firebase ext:install firebase/firestore-translate-text

Popüler extension'lar ve kullanım alanları:

Extension
Kullanım
Avantaj
Resize Images
Storage'a yüklenen görselleri otomatik boyutlandır
CDN maliyetini %60 düşürür
Send Email
Firestore trigger ile otomatik email
Mailgun/SendGrid entegrasyonu
Translate Text
Doküman field'larını otomatik çevir
100+ dil desteği
Algolia Search
Firestore verisini full-text search index'le
Firestore'un limitli arama yetersizliğini çözer
Stripe Payments
Ödeme entegrasyonu
PCI compliance hazır

Firestore Data Modeling Kalıpları {#data-modeling}

NoSQL veritabanında doğru data modeling, performans ve maliyet açısından kritiktir:

swift
1// YANLIS: Derin nesting ve fazla subcollection
2// users/{userId}/orders/{orderId}/items/{itemId}/reviews/{reviewId}
3 
4// DOGRU: Flat yapı + denormalization
5// users/{userId}
6// orders/{orderId} → userId field'ı ile ilişki
7// orderItems/{itemId} → orderId field'ı ile ilişki
8 
9// Denormalization örneği - sipariş içine ürün bilgisi göm
10struct Order: Codable {
11 let id: String
12 let userId: String
13 let status: String
14 let totalAmount: Double
15 let items: [OrderItem] // Embed, ayrı sorgu gerekmez
16 
17 struct OrderItem: Codable {
18 let productId: String
19 let productName: String // Denormalized
20 let productImage: String // Denormalized
21 let quantity: Int
22 let unitPrice: Double
23 }
24}
25 
26// Aggregation ile sayaç tutma (okuma maliyetini düşürür)
27func incrementProductViewCount(productId: String) async throws {
28 let ref = Firestore.firestore().collection("products").document(productId)
29 try await ref.updateData([
30 "viewCount": FieldValue.increment(Int64(1)),
31 "lastViewedAt": FieldValue.serverTimestamp()
32 ])
33}

Veri Modeli Karar Tablosu

Senaryo
Yaklaşım
Neden
Kullanıcı profili + siparişleri
Ayrı collection, userId ile bağla
Siparişler bağımsız sorgulansın
Sipariş + ürün bilgisi
Denormalize (ürün bilgisini göm)
Tek okuma yeterli olsun
Chat mesajları
Subcollection (rooms/messages)
Sadece ilgili oda yüklensin
Global ayarlar
Tek doküman
Az okuma, az maliyet
Etiketler/kategoriler
Array field
Basit filtreleme yeterli

Best Practices {#best-practices}

  1. Security rules her zaman yaz — boş bırakma
  2. Offline persistence aktif tut
  3. Listener'ları temizle — memory leak önle
  4. Denormalization — NoSQL'de join yok, veriyi düzleştir
  5. Cloud Functions — hassas logic client'ta olmasın
  6. Composite index — çoklu alan sorgularında Firestore Console'dan index oluştur
  7. Batch write — birden fazla yazma işlemini tek transaction'da yap
  8. TTL policy — eski dökümanları otomatik sil, maliyet ve boyut kontrolü sağla

Easter Egg

Gizli bir bilgi buldun!

Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?

ALTIN İPUCU

Bu yazının en değerli bilgisi

Bu ipucu, yazının en önemli çıkarımını içeriyor.

Okuyucu Ödülü

Tebrikler! Bu yazıyı sonuna kadar okuduğun için sana özel bir hediyem var: **Kaynaklar:** - [Firebase Documentation](https://firebase.google.com/docs) - [Firestore Security Rules](https://firebase.google.com/docs/firestore/security/get-started) - [Cloud Functions](https://firebase.google.com/docs/functions)

Etiketler

#firebase#firestore#cloud-functions#authentication#offline-first#ios#security
Muhittin Çamdalı

Muhittin Çamdalı

Senior iOS Developer

12+ yıllık deneyime sahip iOS Developer. Swift, SwiftUI ve modern iOS mimarileri konusunda uzman. Apple platformlarında performanslı ve kullanıcı dostu uygulamalar geliştiriyorum.

iOS Geliştirme Haberleri

Haftalık Swift tips, SwiftUI tricks ve iOS best practices. Spam yok, sadece değerli içerik.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.

Paylaş

Bunu da begenebilirsiniz