Pushizo Docs
SDK rehberleri

iOS SDK

Swift 5.9, iOS 15+; SPM ve CocoaPods. APNs push, Notification Service Extension ile zengin medya ve teslimat onayı, in-app, bildirim kutusu, tercihler.

Hedefler: Pushizo (uygulama), PushizoExtension (Notification Service Extension), PushizoCore (ortak tipler; import Pushizo yeniden dışa aktarır). Swizzling kullanılmaz; APNs token'ı açıkça SDK'ya iletilir. Örnek: examples/ios-swiftui.

Kurulum

  1. Apple Developer → Keys → APNs anahtarı (.p8), Key ID, Team ID. Dashboard → Kanallar → iOS Push → .p8, Key ID, Team ID, Bundle ID, ortam (sandbox Xcode build, production TestFlight/App Store) → Doğrula.
  2. Xcode → Signing & Capabilities → Push Notifications ve Background Modes → Remote notifications.
  3. Paket:
    • SPM: File → Add Package Dependencies → sdk/ios (ya da yayınlanmış depo). Uygulama hedefine Pushizo, NSE hedefine PushizoExtension.
    • CocoaPods: pod 'Pushizo' (NSE hedefinde pod 'Pushizo/Extension'); monorepo içinden :path => '<repo>/sdk/ios'.

Başlatma (AppDelegate)

import Pushizo

final class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
        Pushizo.initialize("app_…", launchOptions: launchOptions) { options in
            options.apiUrl = "https://api.example.com"
            options.requiresPrivacyConsent = false
            options.provisionalAuthorization = false   // sessiz (provisional) izin
            options.appGroup = "group.com.example.app"  // NSE ile paylaşılan depolama (isteğe bağlı)
            options.logLevel = .warn
        }
        Pushizo.Notifications.addClickListener { event in
            // event.notification, event.actionId, event.url
        }
        return true
    }
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        Pushizo.setDeviceToken(deviceToken)
    }
    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        Pushizo.setDeviceTokenError(error)
    }
}

SwiftUI'de @UIApplicationDelegateAdaptor(AppDelegate.self) ile bağlayın. SDK UNUserNotificationCenter.current().delegate olarak kendi delegesini atar (ön plan gösterimi ve tıklama → opened). Sandbox/production ortamı embedded.mobileprovision'dan tespit edilir; options.apnsEnvironment ile ezilebilir.

Notification Service Extension

File → New → Target → Notification Service Extension:

import PushizoExtension

final class NotificationService: UNNotificationServiceExtension {
    private let hs = PushizoNotificationServiceExtension(appId: "app_…", apiUrl: "https://api.example.com", appGroup: nil)
    override func didReceive(_ request: UNNotificationRequest, withContentHandler handler: @escaping (UNNotificationContent) -> Void) {
        hs.didReceive(request, withContentHandler: handler)
    }
    override func serviceExtensionTimeWillExpire() { hs.serviceExtensionTimeWillExpire() }
}

Pushizo görünür bildirimlerde mutable-content: 1 gönderir; NSE görseli indirir, rozet artırmayı uygular ve received (confirmed delivery) kaydını yapar. NSE yoksa Mesaj İzleme'de yalnızca sent ve opened görünür.

API

Pushizo.setConsentGiven(true)
Pushizo.login("user_42", identityHash: nil); Pushizo.logout()

Pushizo.User.addTag("plan", "pro"); Pushizo.User.addTags(["a": "1"]); Pushizo.User.removeTag("plan"); Pushizo.User.getTags()
Pushizo.User.setLanguage("tr"); Pushizo.User.id; Pushizo.User.externalId

Pushizo.User.pushSubscription.optIn() / optOut() / id / token
let state = await Pushizo.User.pushSubscription.state()
Pushizo.User.pushSubscription.addObserver { state in … }

Pushizo.Notifications.requestPermission { granted in … }
let granted = await Pushizo.Notifications.permission()
let permissionToken = Pushizo.Notifications.addPermissionObserver { granted in … }   // HSListenerToken
Pushizo.Notifications.removePermissionObserver(permissionToken)
let fgToken = Pushizo.Notifications.addForegroundLifecycleListener { e in e.preventDefault() }
let clickToken = Pushizo.Notifications.addClickListener { e in … }
Pushizo.Notifications.removeClickListener(clickToken)

Pushizo.Session.sendEvent("purchase", properties: ["sku": "A1"], value: 49.9)
Pushizo.Debug.setLogLevel(.debug)
  • addPermissionObserver etkin izin (tam ya da provisional) değiştiğinde ana iş parçacığında çağrılır: requestPermission sonuçlandığında ve uygulama yeniden etkinleştiğinde (izin Ayarlar'dan değişmiş olabilir). İlk durum bildirilmez.
  • Dinleyici ekleyen metotlar HSListenerToken döndürür (@discardableResult); kaldırma belirteçle yapılır.
  • requestPermission [.alert, .badge, .sound] (+ .provisional) ister; critical alert izni için critical alert rehberi.

E-posta ve SMS

Pushizo.User.addEmail("Ali@Example.com")       // → ali@example.com; geçersizse günlüğe hata, istek yapılmaz
Pushizo.User.removeEmail("ali@example.com")
Pushizo.User.addSms("+90 (532) 123-45-67")       // → +905321234567
Pushizo.User.removeSms("+905321234567")
Pushizo.User.emails; Pushizo.User.smsNumbers
HSContacts.normalizeEmail(text) != nil              // formda ön doğrulama

Kayıt POST /sdk/v1/apps/{app}/subscriptions (channel: email|sms): kimlikli kullanıcıda external_id (+ identity_hash), anonimde cihazın mevcut aboneliği user.subscription_id. Login sonrası e-posta/SMS abonelikleri de aynı kullanıcıya bağlanır; logout'ta abonelikler önceki kullanıcıda kalır.

Kullanıcı tercihleri

let result = try await Pushizo.User.getPreferences()      // result.preferences: [HSPreference], result.categories: [HSMessageCategory]
try await Pushizo.User.setPreferences([
    HSPreferenceInput(category: "promos", optedIn: false),                  // tüm kanallarda kapat
    HSPreferenceInput(category: "orders", channel: "ios_push", optedIn: true),
])
Pushizo.User.getPreferences { result in … }                 // geri çağrı sürümleri

HSPreference: category, channel (nil = tüm kanallar), optedIn, explicit, source, updatedAt. Abonelik henüz yoksa HSError.noSubscription, geçersiz girdi HSError.invalidArgument.

In-app mesajlar

Pushizo.InAppMessages.addTrigger("level", "gold")   // addTriggers, removeTrigger(s), clearTriggers(), getTriggers()
Pushizo.InAppMessages.setScreen("cart")             // SwiftUI: .onAppear { … } / .onDisappear { setScreen(nil) }
Pushizo.Session.sendEvent("cart_viewed")
Pushizo.InAppMessages.paused = true
Pushizo.InAppMessages.addClickListener { e in /* e.messageId, e.actionId, e.url */ }

final class InAppLogger: HSInAppLifecycleListener {      // tüm metotlar isteğe bağlı
    func onDidDisplay(_ message: HSInAppMessage) { print(message.id) }
}
Pushizo.InAppMessages.addLifecycleListener(logger)   // zayıf referansla tutulur

Çizim ayrı bir UIWindow üstünde UIKit görünümleriyle yapılır (SwiftUI ile uyumlu); dinamik yazı boyutu, açık/koyu tema, VoiceOver ve "Hareketi azalt" desteklenir. deeplink aksiyonu UIApplication.open ile açılır. Tetik kuralları (gruplar arası VEYA, grup içi VE; session_start, event, screen, trigger, session_duration), sıklık ve aksiyonlar (dismiss, url, deeplink, tags, push_prompt, outcome) tüm SDK'larda aynıdır; ortak vektörler sdk/testdata/messaging-vectors.json. Özel çizim: Pushizo.InAppMessages.setRenderer(HSInAppRenderer).

Bildirim kutusu

let page = try await Pushizo.Inbox.list(limit: 20, cursor: nil)   // items, unreadCount, nextCursor, hasMore
let item = try await Pushizo.Inbox.markRead(page.items[0].id)
let remaining = try await Pushizo.Inbox.markAllRead()
try await Pushizo.Inbox.archive(item.id)
let unread = try await Pushizo.Inbox.unreadCount()
// Hatalar: HSError.notInitialized | .noSubscription | .noUser | .invalidArgument, HSApiError (ör. 403 identity_hash)

Push aboneliği yoksa (ör. simülatör) in-app ve bildirim kutusu ilk e-posta/SMS aboneliğini kullanır.

Bildirim merkezi

Pushizo.Notifications.clearAll()                             // tüm teslim edilmiş bildirimler + rozet
Pushizo.Notifications.removeGroupedNotifications("orders")   // aynı thread-id'li bildirimler
Pushizo.Notifications.removeNotification(identifier: id)     // UNNotificationRequest.identifier

thread_id, ios_relevance_score ve target_content_identifier sunucu tarafından aps içine yazılır ve sistem uygular. Aksiyon düğmeleri için UNNotificationCategory dinamik kaydedilir.

Davranışlar

  • Bölgesel API kökü (ADR-0032 §4): options.apiUrl varsayılan bırakılırsa config api_url benimsenir ve UserDefaults'ta (App Group verildiyse NSE ile paylaşılır) saklanır; 421 HS_WRONG_CELL yanıtında details.api_url köküne geçilip istek bir kez yeniden denenir. Yalnızca https kabul edilir.
  • Token hex → POST/PATCH /subscriptions; oturum: didBecomeActive → session_start, didEnterBackground → session_end (duration_seconds).
  • Çevrimdışı kuyruk UserDefaults (App Group verilmişse paylaşımlı); NSE'nin received kayıtları uygulama açılınca gönderilir.

Test

  • Birim: swift test ya da Xcode (Tests/PushizoTests); macOS gerekir.
  • Cihaz: examples/ios-swiftui/README.md. Simülatörde APNs kaydı yoktur; .apns dosyası sürükleyerek gösterim/tıklama test edilir.

Sorun giderme

BelirtiNeden / çözüm
didFailToRegisterPush Notifications yeteneği yok veya simülatör.
BadDeviceTokenOrtam uyumsuz (sandbox build ↔ production anahtarı). Dashboard'daki ortamı değiştirin.
UnregisteredUygulama silinmiş/token eski; abonelik otomatik invalid olur.
Görsel gelmiyorNSE hedefi yok veya PushizoExtension eklenmemiş.
Yerel API'ye erişilemiyorGerçek cihazda LAN IP + Info.plist NSAppTransportSecurity → NSAllowsLocalNetworking.
In-app mesaj gelmiyorAbonelik yok (push ya da e-posta/SMS), tetik sağlanmıyor, sıklık dolmuş ya da paused; .debug günlükte "in-app listesi yenilendi" satırına bakın.
Inbox.list → HSError.noSubscriptionAbonelik henüz kaydedilmedi (simülatörde önce addEmail ile e-posta aboneliği oluşturun).

Bu sayfada