Ana Sayfa / Blog / Live Activities ve Dynamic Island: 2 projede pratik deneyim

Live Activities ve Dynamic Island: 2 projede pratik deneyim

Live Activities iOS 16 ile geldi, Dynamic Island iPhone 14 Pro ile. Fitness app ve teslimat takibi uygulamasında implementation yaparken öğrendiğim ActivityKit pattern'leri.

Live Activities iOS 16.1 ile production’a hazır oldu. Uygulamanızın anlık bilgisini kilit ekranında ve Dynamic Island’da göstermek için. Spor maçı skoru, Uber sürücü konumu, yemek siparişi durumu, timer gibi şeyler için ideal.

2 projede Live Activities implement ettim: bir fitness app’inde antrenman süresi takibi, bir teslimat app’inde kurye konumu. Bu yazıda öğrendiklerim.

Live Activity nedir, ne değildir

Live Activity:

  • Kilit ekranında veya Dynamic Island’da görünür
  • Sürekli güncellenir (push notification ile veya local’den)
  • Maksimum 8 saat açık kalır (ya da 12 saat idle)
  • Uygulamanız aktif olmak zorunda değil

Ne değil:

  • Widget değil (widget static + timeline-based, Live Activity dynamic + event-driven)
  • Persistent notification değil (hem görünüm hem davranış farklı)
  • Arka plan iş yapan bir mekanizma değil (background task ayrı)

ActivityKit framework’ü

SwiftUI tabanlı, deklaratif bir framework. 4 ana bölüm:

  1. ActivityAttributes: activity’nin şeklini tanımlayan struct (statik ve dinamik state dahil)
  2. ActivityKit.Activity: runtime’da activity instance’ı yönetir (start, update, end)
  3. WidgetConfiguration (ActivityConfiguration): activity UI’ını tanımlar
  4. Dynamic Island: expanded, compact leading/trailing, minimal view’ları ayrı olarak tanımlanır

Basit örnek:

struct WorkoutAttributes: ActivityAttributes {
    struct ContentState: Codable, Hashable {
        var duration: TimeInterval
        var caloriesBurned: Int
    }
    var workoutType: String
}

Başlatma: uygulamanın tetiklediği an

Live Activity’yi siz kullanıcıyı arayıp başlatamazsınız. Kullanıcı uygulamanızı açıp bir aksiyon yapmalı (“antrenmanı başlat”, “siparişi onayla”).

let attributes = WorkoutAttributes(workoutType: "Koşu")
let state = WorkoutAttributes.ContentState(duration: 0, caloriesBurned: 0)

do {
    let activity = try Activity<WorkoutAttributes>.request(
        attributes: attributes,
        content: .init(state: state, staleDate: nil),
        pushType: .token
    )
    print("Activity started: \(activity.id)")
} catch {
    print("Failed: \(error)")
}

pushType: .token diyorsanız activity’nin remote push update’lerini alacağı anlamı. Push notification ile update için APNS token’ını toplamanız gerekiyor.

Güncelleme: 3 farklı yol

1. Local update: uygulamanız açıkken direct Activity.update() çağırırsınız.

await activity.update(using: newState)

Kullanıcı app’in içindeyken uygulanır. App suspended olunca local update olmaz.

2. Push notification: APNS üzerinden Live Activity’ye özel payload gönderirsiniz. Apple’ın ayrı bir APNS endpoint’i var: liveactivity-sandbox.push.apple.com.

Payload:

{
    "aps": {
        "timestamp": 1234567890,
        "event": "update",
        "content-state": {
            "duration": 1200,
            "caloriesBurned": 150
        }
    }
}

Bu yöntem en güçlüsü ama backend iş gerektiriyor. APNS p12 cert veya token-based auth, apns-push-type: liveactivity header, apns-topic: com.example.app.push-type.liveactivity topic.

3. Background task: uygulamanın periyodik update’i. BGTaskScheduler veya location-based background fetch. Sınırlı rate.

Dynamic Island UI tasarımı

Dynamic Island 3 state’te görünür:

  • Minimal: sadece sol veya sağda icon. Başka bir Live Activity varken
  • Compact: leading + trailing kısa içerik
  • Expanded: long press ile genişliyor, daha fazla bilgi

Her state ayrı ayrı tasarlanmalı:

DynamicIsland {
    expandedRegion(.leading) { ... }
    expandedRegion(.trailing) { ... }
    expandedRegion(.center) { ... }
    expandedRegion(.bottom) { ... }
} compactLeading: {
    ...
} compactTrailing: {
    ...
} minimal: {
    ...
}

Compact için tasarım disiplini şart: 15x15pt veya en fazla 20x20pt alanda bilgiyi vermek. İkon + 1-2 karakter genelde ideal. Cursor ile Dynamic Island’a geçiş animasyonları sistem tarafından yapılır.

Frekans ve update limit’leri

Push notification ile update’in rate limit’i var. Apple documentation çok net söylemiyor ama production tecrübem:

  • Saniyede 1’den sık update etmeyin
  • 4-8 dakikada en az 1 update yoksa sistem “stale” işaretler
  • Kritik state change’de update, atomic değişimde değil

Teslimat uygulamasında GPS her 5 saniyede update’i Live Activity’ye push etmek overkill oldu, battery drain yarattı. 30 saniyede 1 update + mesafe değişimi belirli eşiği geçince update yapısına geçtik. Battery impact %80 azaldı.

End: activity’yi bitirme

Activity bittiğinde explicitly end etmeniz gerekiyor, yoksa 8 saat maksimum süre dolana kadar kilit ekranında kalır.

await activity.end(
    using: finalState,
    dismissalPolicy: .after(.now + 3600) // 1 saat sonra dismiss
)

dismissalPolicy seçenekleri:
.default: sistem karar verir
.immediate: hemen kaldır
.after(date): belirli tarihte

Teslimat uygulamasında sipariş teslim olduktan sonra “Teslim edildi” state’i 10 dakika görünsün istedik, sonra kalksın. .after(.now + 600) ile çözdük.

Hata durumları ve fallback

Kullanıcı Settings’ten Live Activity izni verebilir veya reddedebilir. ActivityAuthorizationInfo().areActivitiesEnabled ile kontrol etmelisiniz. İzin yoksa kullanıcıya Settings yönlendirmesi veren bir fallback UI iyi pattern.

Token’ı APNS’e kaydettikten sonra Apple’dan invalid token hatası alabilirsiniz. Activity ended olmuş ama token backend’de duruyor olabilir. Her update deneme’sinde 410 Gone dönerse token’ı temizleyin.

Test etmenin zorluğu

Simulator Live Activity göstermiyor. Dynamic Island için iPhone 14 Pro veya yeni gerekiyor. Uzaktan push test etmek için APNS cert + push payload + real device + aktif Live Activity gerekli. Test döngüsü zahmetli.

Xcode 15+’dan Live Activity debug tooling biraz iyileşti ama yine de real device test paradigmasından kaçış yok.

Kullanıcı memnuniyeti etkisi

İki projede de Live Activity eklenince:

  • App open rate’i arttı (kullanıcı kilit ekranından durumunu görüyor, app’e girmeden)
  • Yalnız bu değil, kilit ekranına bakıp doğrulama yapıyor, ek güven hissediyor
  • App Store review’larında ‘Live Activity çok kullanışlı’ yorumları geldi

Live Activity ürününüzün ‘live status’ gösteren bir aksiyonu varsa yüksek ROI getiriyor. Yoksa zorlamaya gerek yok.

Bu konuda bir projeniz mi var?

Kısa bir özet bırakın, 24 saat içinde size dönüş yapayım.

İletişime Geç