iOS Push Notifications Mülakatı 2026: APNs, token'lar ve sorun giderme

iOS mülakatlarına hazırlık için kapsamlı rehber: Push Notifications, APNs, token yönetimi ve sorun giderme. Sık sorulan sorular ve ayrıntılı yanıtlar.

iOS Push Notifications APNs mimarisi, token'lar ve sorun giderme

Push Notifications iOS mülakatlarında kritik bir konu olmaya devam ediyor. APNs'nin nasıl çalıştığını anlamak, device token'ları yönetmek ve yaygın sorunları gidermek Apple ekosistemine dair derin bilgiyi gösterir. Bu rehber en sık sorulan mülakat sorularını ele alıyor.

Mülakatın anahtar noktası

Mülakatçılar, cihaz kaydından bildirim teslimine kadar tüm yaşam döngüsünü ve her aşamada doğru hata yönetimini kavramış adayları arıyor.

APNs mimarisi: iOS bildirimlerinin temeli

Apple Push Notification service (APNs), Apple cihazlarına push bildirim teslimini yöneten merkezi servistir. Mimarisini bilmek mülakat sorularına etkili yanıt vermek için şarttır.

APNs nasıl çalışır?

İletişim akışı üç ana aktörü kapsar: iOS uygulaması, APNs ve backend sunucusu. Sürecin tamamı şöyledir:

AppDelegate.swiftswift
import UIKit
import UserNotifications

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        // Request notification authorization
        UNUserNotificationCenter.current().requestAuthorization(
            options: [.alert, .badge, .sound]
        ) { granted, error in
            guard granted else { return }
            // Register with APNs
            DispatchQueue.main.async {
                application.registerForRemoteNotifications()
            }
        }
        return true
    }
}

APNs kaydı iki adımda gerçekleşir: kullanıcı izni alınır, ardından registerForRemoteNotifications() çağrılır.

Device token yönetimi

Device token, APNs tarafından belirli bir cihazı hedeflemek için üretilen benzersiz bir tanımlayıcıdır. Bu token değişebilir ve uygulamanın her açılışında backend sunucusuna gönderilmelidir.

AppDelegate.swiftswift
extension AppDelegate {

    // Callback when APNs provides the token
    func application(
        _ application: UIApplication,
        didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
    ) {
        // Convert token to hexadecimal string
        let tokenString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
        print("Device Token: \(tokenString)")

        // Send to backend server
        sendTokenToServer(tokenString)
    }

    // Callback on registration failure
    func application(
        _ application: UIApplication,
        didFailToRegisterForRemoteNotificationsWithError error: Error
    ) {
        print("Failed to register: \(error.localizedDescription)")
    }

    private func sendTokenToServer(_ token: String) {
        // Implement HTTP request to backend
    }
}

Token Data olarak gelir ve sunucuya gönderilmeden önce onaltılık bir karakter dizisine dönüştürülmelidir.

APNs hakkında sık sorulan mülakat soruları

iOS mülakatları genellikle APNs'ye dair teorik ve pratik soruları birlikte içerir. En yaygın olanlar ve ayrıntılı yanıtları aşağıda yer alıyor.

Soru 1: APNs sandbox ile production arasındaki fark nedir?

APNs ortamları

APNs, farklı endpoint'lere sahip iki ayrı ortam sunar. Bir ortamda üretilen token'lar diğerinde çalışmaz.

| Ortam | Endpoint | Kullanım | |-------|----------|----------| | Sandbox | api.sandbox.push.apple.com | Debug, TestFlight | | Production | api.push.apple.com | App Store |

Soru 2: Token süresinin dolması nasıl yönetilir?

Device token'lar farklı nedenlerle değişebilir: sistem geri yükleme, yeni cihaza kurulum veya APNs'nin periyodik yenilemesi. Sunucu, APNs'nin hata yanıtlarını doğru şekilde işlemelidir.

NotificationService.swiftswift
enum APNsError: Int {
    case badDeviceToken = 400
    case unregistered = 410
    case payloadTooLarge = 413
    case tooManyRequests = 429
    case internalServerError = 500

    var shouldRemoveToken: Bool {
        // Remove token only if device is no longer registered
        return self == .unregistered || self == .badDeviceToken
    }
}

struct APNsResponse {
    let statusCode: Int
    let deviceToken: String

    func handleError() {
        guard let error = APNsError(rawValue: statusCode) else { return }

        if error.shouldRemoveToken {
            // Remove token from database
            TokenRepository.shared.remove(deviceToken)
        }
    }
}

Sunucu tarafında APNs hata yönetimi, token veritabanını temiz tutmak ve gereksiz isteklerden kaçınmak için kritiktir.

Soru 3: Silent notification nasıl uygulanır?

Silent notification, uygulamayı arka planda uyandırarak kullanıcıya herhangi bir uyarı göstermeden işlem yapmayı sağlar.

AppDelegate.swiftswift
func application(
    _ application: UIApplication,
    didReceiveRemoteNotification userInfo: [AnyHashable: Any],
    fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
    // Check if this is a silent notification
    guard let aps = userInfo["aps"] as? [String: Any],
          aps["content-available"] as? Int == 1 else {
        completionHandler(.noData)
        return
    }

    // Perform background task
    performBackgroundTask { result in
        switch result {
        case .success:
            completionHandler(.newData)
        case .failure:
            completionHandler(.failed)
        }
    }
}

Silent notification'ın JSON payload'u aps nesnesi içinde "content-available": 1 içermelidir.

iOS mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

Notification Service Extension: gelişmiş özelleştirme

Notification Service Extensions, bildirim içeriğinin görüntülenmeden önce değiştirilmesini sağlar. Bu özellik mülakatlarda sık sık ele alınır.

Service Extension oluşturma

NotificationService.swift (in Extension target)swift
import UserNotifications

class NotificationService: UNNotificationServiceExtension {

    var contentHandler: ((UNNotificationContent) -> Void)?
    var bestAttemptContent: UNMutableNotificationContent?

    override func didReceive(
        _ request: UNNotificationRequest,
        withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
    ) {
        self.contentHandler = contentHandler
        bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContent

        guard let bestAttemptContent = bestAttemptContent else {
            contentHandler(request.content)
            return
        }

        // Modify content
        if let imageURLString = bestAttemptContent.userInfo["image-url"] as? String,
           let imageURL = URL(string: imageURLString) {
            downloadImage(from: imageURL) { attachment in
                if let attachment = attachment {
                    bestAttemptContent.attachments = [attachment]
                }
                contentHandler(bestAttemptContent)
            }
        } else {
            contentHandler(bestAttemptContent)
        }
    }

    override func serviceExtensionTimeWillExpire() {
        // Called when time limit (30 seconds) is exceeded
        if let contentHandler = contentHandler,
           let bestAttemptContent = bestAttemptContent {
            contentHandler(bestAttemptContent)
        }
    }

    private func downloadImage(
        from url: URL,
        completion: @escaping (UNNotificationAttachment?) -> Void
    ) {
        URLSession.shared.downloadTask(with: url) { localURL, _, error in
            guard let localURL = localURL, error == nil else {
                completion(nil)
                return
            }

            let tempDirectory = FileManager.default.temporaryDirectory
            let fileName = url.lastPathComponent
            let destinationURL = tempDirectory.appendingPathComponent(fileName)

            try? FileManager.default.moveItem(at: localURL, to: destinationURL)

            let attachment = try? UNNotificationAttachment(
                identifier: fileName,
                url: destinationURL,
                options: nil
            )
            completion(attachment)
        }.resume()
    }
}

Extension içeriği değiştirmek için 30 saniyeye sahiptir. Bu sınır aşılırsa serviceExtensionTimeWillExpire() metodu çağrılır.

Push Notifications sorun giderme

Push notification hata ayıklama mülakatlarda tekrar eden bir konudur. Adayların araçları ve teşhis tekniklerini bilmesi beklenir.

Kayıt durumunu doğrulama

DebugHelper.swiftswift
struct PushNotificationDebugger {

    static func checkNotificationStatus() async {
        let center = UNUserNotificationCenter.current()
        let settings = await center.notificationSettings()

        print("=== Push Notification Status ===")
        print("Authorization: \(settings.authorizationStatus.description)")
        print("Alert: \(settings.alertSetting.description)")
        print("Badge: \(settings.badgeSetting.description)")
        print("Sound: \(settings.soundSetting.description)")
        print("Notification Center: \(settings.notificationCenterSetting.description)")
    }
}

extension UNAuthorizationStatus {
    var description: String {
        switch self {
        case .notDetermined: return "Not Determined"
        case .denied: return "Denied"
        case .authorized: return "Authorized"
        case .provisional: return "Provisional"
        case .ephemeral: return "Ephemeral"
        @unknown default: return "Unknown"
        }
    }
}

Bu debug fonksiyonu bildirimlerin yetkilendirme durumunu hızla kontrol etmeyi sağlar.

Sık karşılaşılan hatalar ve çözümler

Bilinmesi gereken yaygın hatalar

Mülakatçılar sık sık yaygın hataları ve çözümlerini sorar. Aşağıdakiler akılda tutulması gerekenlerdir.

| Hata | Sebep | Çözüm | |------|-------|-------| | Geçersiz token | Yanlış ortam (sandbox/prod) | Provisioning profile'ı kontrol et | | Bildirim gelmedi | Düşük güç modu açık | Şarjlı pille test et | | Extension çağrılmadı | mutable-content eksik payload | "mutable-content": 1 ekle | | Background fetch başarısız | Uygulama kullanıcı tarafından kapatıldı | Kullanıcıyı sınırlamadan haberdar et |

APNs'yi doğrudan test etme

Geliştirme sırasında bildirimleri test etmek için curl aracı doğrudan APNs'ye istek göndermeyi sağlar:

TestPayload.swiftswift
struct APNsTestPayload {

    static let silentNotification = """
    {
        "aps": {
            "content-available": 1
        },
        "custom-data": "test"
    }
    """

    static let richNotification = """
    {
        "aps": {
            "alert": {
                "title": "New message",
                "body": "Message content"
            },
            "mutable-content": 1,
            "sound": "default"
        },
        "image-url": "https://example.com/image.jpg"
    }
    """
}

Bu test payload'ları, uygulamanın farklı bildirim türleriyle davranışını doğrulamak için kullanışlıdır.

Production için en iyi uygulamalar

En iyi uygulamalar üzerine sorular adayın production deneyimini ölçmeye yardımcı olur.

Ağ hatalarını ele alma

PushTokenManager.swiftswift
actor PushTokenManager {

    private var pendingToken: String?
    private var retryCount = 0
    private let maxRetries = 3

    func registerToken(_ token: String) async {
        pendingToken = token
        await sendTokenWithRetry()
    }

    private func sendTokenWithRetry() async {
        guard let token = pendingToken else { return }

        do {
            try await APIClient.shared.registerPushToken(token)
            pendingToken = nil
            retryCount = 0
        } catch {
            retryCount += 1
            if retryCount < maxRetries {
                // Retry with exponential backoff
                let delay = UInt64(pow(2.0, Double(retryCount))) * 1_000_000_000
                try? await Task.sleep(nanoseconds: delay)
                await sendTokenWithRetry()
            }
        }
    }
}

Bir actor kullanmak, eşzamanlı ortamlarda token yönetiminde thread güvenliğini sağlar.

Token'ın yerel kalıcılığı

TokenStorage.swiftswift
struct TokenStorage {

    private static let tokenKey = "com.app.pushToken"

    static func save(_ token: String) {
        UserDefaults.standard.set(token, forKey: tokenKey)
    }

    static func retrieve() -> String? {
        UserDefaults.standard.string(forKey: tokenKey)
    }

    static func hasTokenChanged(_ newToken: String) -> Bool {
        guard let savedToken = retrieve() else { return true }
        return savedToken != newToken
    }
}

Token'ı yerel olarak saklamak, token değişmediğinde gereksiz ağ çağrılarını engeller.

Sonuç

iOS Push Notifications'a hâkim olmak Apple ekosistemine ve istemci-sunucu etkileşimine derinlemesine hâkimiyetin kanıtıdır. Mülakatlarda akılda tutulması gereken anahtar noktalar:

✅ APNs mimarisi: kayıttan teslimata uzanan tam akışı kavramak

✅ Device token'lar: yaşam döngüsü ve token değişimlerinin yönetimi

✅ Notification Service Extension: 30 saniyelik sınırla içerik özelleştirme

✅ Sorun giderme: yaygın hataları ve çözümlerini bilmek

✅ Silent notifications: background fetch için content-available

✅ En iyi uygulamalar: yeniden deneme mantığı, yerel kalıcılık, hata yönetimi

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

Etiketler

#ios
#push-notifications
#apns
#swift
#interview

Paylaş

İlgili makaleler