Live Activities i Dynamic Island: Vodič za ActivityKit u iOS 26
Vodič za izradu Live Activities i Dynamic Island u iOS 26 pomoću ActivityKit okvira. Postavljanje Widget ekstenzije, ActivityAttributes, sva tri stanja Dynamic Islanda, App Intents i testiranje u Xcode 26 simulatoru.
Live Activities su interaktivne obavijesti u realnom vremenu koje iOS 26 aplikacija prikazuje na zaključanom zaslonu, u Dynamic Islandu i (od iOS 26) na Apple Watchu putem Smart Stacka. Dodaju se pomoću ActivityKit okvira, izgled se piše u SwiftUI-u unutar Widget ekstenzije, a stanje se ažurira lokalno ili preko APNs push obavijesti. Ovaj vodič pokazuje kako od nule napraviti Live Activity za praćenje isporuke, dizajnirati sva tri stanja Dynamic Islanda, dodati App Intents interaktivnost i testirati sve u simulatoru.
ActivityKit je Appleov okvir za Live Activities; sam izgled se piše u SwiftUI-u unutar Widget ekstenzije, ne u glavnoj aplikaciji.
Dynamic Island ima tri stanja (compact leading/trailing, minimal i expanded), i svako se definira zasebno u ActivityConfiguration.
ActivityAttributes definiraju statični dio Live Activityja, dok ContentState sadrži dinamične vrijednosti koje se mijenjaju kroz ažuriranja.
Live Activity može trajati do 8 sati aktivno i još 4 sata u stanju "ended", nakon čega ga sustav automatski uklanja.
iOS 26 donosi ActivityContent.stale API, glađe animacije stanja i podršku za Live Activities na watchOS 26 Smart Stacku.
App Intents omogućuju direktne akcije unutar Live Activityja bez otvaranja aplikacije, što je savršeno za "Pauziraj", "Poništi" ili "Označi kao dostavljeno".
Što su Live Activities i kako rade u iOS 26?
Live Activities su posebna vrsta widgeta koja prikazuje podatke u realnom vremenu izvan aplikacije: na zaključanom zaslonu, na StandBy zaslonu, na Dynamic Islandu i, od iOS 26, u watchOS Smart Stacku. Za razliku od običnog widgeta koji se osvježava periodično na temelju vremenske linije, Live Activity ima eksplicitno stanje koje aplikacija ili poslužitelj mijenjaju kad se dogodi konkretan događaj: dostavljač napravi sljedeći korak, gol je zabijen, tajmer dođe do nule.
API se zove ActivityKit i uveden je u iOS 16.1. Radi na iPhoneima s A13 čipom i novijima. U iOS 26 dobio je nekoliko važnih dodataka o kojima ću posebno pisati u dijelovima ispod: novi ActivityContent.stale markers, poboljšani prijelazi za Dynamic Island, mogućnost da se Live Activity reflektira na Apple Watchu bez posebnog watchOS targeta, te bolje ponašanje pod low power modeom. Došao sam iz Objective-C ere gdje smo iste stvari radili remote-notification-content-extension trikovima; ActivityKit je konačno napravio ovo bez hakova.
Životni ciklus je jednostavan. Aplikacija poziva Activity.request(...) da ga pokrene, activity.update(...) da mu promijeni stanje, i activity.end(...) da ga završi. Sustav sam odlučuje gdje ga prikazati (Lock Screen, Dynamic Island, Smart Stack) na temelju istog SwiftUI opisa koji ste dali. Live Activity može trajati do 8 sati u aktivnom stanju i još 4 sata u ended stanju prije nego što ga sustav ukloni. Imajte to na umu kod duljih tokova.
Po čemu se Dynamic Island razlikuje od klasičnog Live Activityja?
Dynamic Island je isti Live Activity, prikazan u drugom prostoru. Ista ActivityConfiguration deklarira i Lock Screen izgled i tri Dynamic Island varijante, što znači jedan izvor istine i jedno mjesto za promjene. Razlika je u tome što Dynamic Island ima strogo ograničen prostor pa vam sustav prosljeđuje jednu od tri regije koje morate ispuniti.
Tri Dynamic Island stanja su:
Stanje
Kad se pojavljuje
Preporučeni sadržaj
Compact leading + trailing
Zadano stanje kad je Live Activity aktivan, a korisnik nije u aplikaciji
Kad postoji više aktivnih Live Activityja i vaš nije trenutno prioritetan
Samo jedna glyph ili brojka; sve mora stati u ~30×30 pt
Expanded
Kad korisnik dodirne i drži Dynamic Island
Puni prikaz s ikonom, progresom, akcijama; do 4 regije: leading, trailing, center, bottom
U iOS 26 Apple je poravnao animacije prijelaza između ova tri stanja. matchedGeometryEffect i implicitne .animation modifikatore ActivityKit sada honorira dosljedno između minimal → compact → expanded prijelaza, što ranije nije uvijek bio slučaj. Ako ste pisali nekakve DispatchQueue.main.asyncAfter workarounde za sinkronizaciju animacije, sad ih možete izbaciti. (Iskreno, jedva čekam da to obrišem iz svog starog koda.)
Postavljanje Widget ekstenzije za ActivityKit
Live Activity ne živi u glavnoj aplikaciji. Živi u Widget ekstenziji, zajedno s ostalim widgetima. Ako ekstenziju već imate, dodajte samo ActivityConfiguration. Ako nemate, dodajte novi target: File → New → Target → Widget Extension, uključite "Include Live Activity" checkbox, i Xcode 26 vam generira boilerplate.
Nakon toga otvorite Info.plistglavne aplikacije (ne ekstenzije!) i dodajte:
Prvi ključ omogućuje Live Activities. Drugi je opcionalan; dodajte ga samo ako morate ažurirati češće od otprilike jednog ažuriranja u minuti. Bez njega sustav agresivno throttla push updates u pozadini. Zajednički kod (ActivityAttributes tip) stavite u zajednički Swift file koji je član oba targeta, ili u zaseban framework. Inače će Xcode prijaviti "type not found" kad pokušate pokrenuti Activity iz glavne aplikacije.
Definiranje ActivityAttributes i ContentState
Svaki Live Activity ima dva sloja podataka: statični dio (koji se ne mijenja tijekom života aktiviteta) i dinamični dio (ContentState, koji se mijenja kroz ažuriranja). Oba spremate u tip koji implementira ActivityAttributes protokol.
import ActivityKit
import Foundation
struct DeliveryAttributes: ActivityAttributes {
struct ContentState: Codable, Hashable {
var status: DeliveryStatus
var etaMinutes: Int
var driverName: String
var stepsCompleted: Int
}
// Statični dio: postavlja se jednom, pri pokretanju
let orderNumber: String
let restaurantName: String
let totalSteps: Int
}
enum DeliveryStatus: String, Codable, Hashable {
case preparing
case onTheWay
case arrived
}
Tri stvari koje su me zezale prvi put. ContentState mora biti Codable i Hashable (ne Equatable, jer ga Hashable podrazumijeva). Sve što stavljate u state mora se serijalizirati u ~4 KB JSON payload (limit APNs push update-a). I enumeriranja moraju imati raw values ili custom Codable, jer im ContentState Diffing algoritam u iOS 26 to očekuje. Ovaj zadnji sam otkrio na najgori mogući način, u produkciji.
SwiftUI izgled za Lock Screen i Dynamic Island
Sav izgled deklarirate u jednom Widget tipu unutar Widget ekstenzije. ActivityConfiguration traži dvije stvari: closure za Lock Screen (i StandBy) prikaz, i dynamicIsland closure koji vraća sva tri stanja.
Nekoliko stvari na koje pazim u svakom Live Activity projektu. Prvo, monospacedDigit() na brojkama koje se često mijenjaju (etaMinutes, kola sata) da se ne trza layout kad "1" postane "10". Drugo, context.state za dinamične vrijednosti i context.attributes za statične. Treće, držanje compact i minimal stanja vrlo jednostavnima jer sustav često skalira Dynamic Island, a tanke glyph ilustracije se najbolje ponašaju. Za dizajn koji koristi transition efekte pogledajte SwiftUI SensoryFeedback za haptički odziv u iOS 26; kombinacija vibro-signala i vizualnog prijelaza podiže percepciju kvalitete znatno više nego što bi trebala.
Pokretanje, ažuriranje i završavanje Live Activityja
Iz glavne aplikacije poziva se Activity.request(...). Vraća Activity objekt koji držite negdje (npr. u @Observable servisu) da ga kasnije možete ažurirati. U iOS 26 API je konzistentno async, pa zaboravite ranije completion handlere.
Par sitnica koje me nauče projekti u produkciji. staleDate obavezno postavljajte; nakon njega sustav prikazuje activity zasivljen s natpisom "Zastarjelo" i to je bolje UX nego lažiranje svježih vrijednosti. Kod end(...) koristite .after(...) policy da korisnik vidi rezultat nekoliko minuta prije nego što nestane. .immediate ubija activity odmah, što najčešće nije ono što želite. Za navigaciju iz Live Activityja u aplikaciju iskoristite deep linking pattern iz vodiča za SwiftUI NavigationStack u iOS 26.
Ažuriranje putem APNs push obavijesti
Za većinu produkcijskih tokova ažurirat ćete Live Activity s poslužitelja, ne iz aplikacije, jer aplikacija u pozadini najčešće ne radi kad se stanje mijenja. ActivityKit ima svoj push token za to, koji je odvojen od klasičnog device tokena.
// Nakon što je Activity kreiran s pushType: .token,
// pretplatimo se na push token za taj konkretan Activity
Task {
guard let activity else { return }
for await tokenData in activity.pushTokenUpdates {
let token = tokenData.map { String(format: "%02x", $0) }.joined()
// Pošaljite ovaj token vašem backendu, vezan uz orderNumber
await BackendClient.shared.register(pushToken: token, for: activity.attributes.orderNumber)
}
}
Payload koji šaljete s poslužitelja ima apns-push-type: liveactivity header, apns-topic: com.tvrtka.aplikacija.push-type.liveactivity, i JSON tijelo s aps.timestamp, aps.event (update ili end) i aps.content-state koji mora točno odgovarati vašem ContentState tipu. Detaljnu specifikaciju formata pogledajte u Apple dokumentaciji za ActivityKit push obavijesti. Za low-latency scenarije (sport, dionice) u iOS 26 postoji i apns-priority: 10 u kombinaciji s NSSupportsLiveActivitiesFrequentUpdates. Ali oprez: sustav mjeri prosjek i previše frequent updateova može uzrokovati da vaš activity bude throttled do kraja svog života. Ovaj scenarij sam vidio uživo dok sam optimizirao live-score aplikaciju, i vjerujte mi, korisnici se ne vraćaju.
Interaktivnost pomoću App Intents
Od iOS 17 Live Activity može imati gumbe koji direktno pokreću akciju bez otvaranja aplikacije. Realizirano je kroz App Intents. Definirate AppIntent, umećete Button(intent:) u ekspanded regiju, i sustav pokreće intent u pozadini glavne aplikacije.
LiveActivityIntent (a ne obični AppIntent) je bitan. Samo taj protokol garantira da perform() radi u kontekstu glavne aplikacije i ima direktan pristup share containerima. Ako ste već implementirali App Intents za Siri i Shortcuts, možete ih ponovno iskoristiti; dubinski pregled kako pisati intents nalazi se u vodiču App Intents u iOS 26 za Siri, Shortcuts i Apple Intelligence. Za kompletnu referencu novih iOS 26 API-ja vezanih uz Live Activities pogledajte i službenu ActivityKit dokumentaciju.
Kako testirati Live Activity u Xcode 26 simulatoru?
Xcode 26 simulator konačno podržava Dynamic Island. Na iPhone 15 Pro simulatoru i novijima pojavljuje se pravi otok koji reagira na pritisak i dugi pritisak. Za testiranje pokrenite aplikaciju, izazovite Activity.request(...) iz nekog debug gumba, pa pritisnite Cmd+L da simulirate zaključavanje i vidite Lock Screen prikaz.
Za push updateove postoji sjajan trik. Xcode 26 čita .apns JSON datoteke iz projektnog navigatora; kliknete desnim tipkom miša na datoteku pa Send Push Notification to iOS Device. To radi i za Live Activity ako u JSON dodate Simulator Target Bundle polje s bundle ID-em vaše widget ekstenzije i Simulator Live Activity ID s ID-em aktivnog activityja (dobijete ga iz activity.id). Šablonu i punu proceduru za push simulaciju pronaći ćete u Appleovom vodiču za testiranje Live Activities.
Često postavljana pitanja
Koliko dugo Live Activity može biti aktivan?
Aktivan Live Activity može trajati do 8 sati. Nakon toga sustav ga automatski završi. Ako pozovete end(...) ranije, activity može ostati u "ended" stanju još do 4 sata prije nego što potpuno nestane s Lock Screena, što je korisno za prikaz konačnog stanja korisniku.
Trebam li push notification permission za Live Activities?
Ne. Live Activities koriste odvojen sustav autorizacije koji je zadano uključen. Korisnik ih može isključiti u Settings → Face ID & Passcode → Live Activities ili za pojedinu aplikaciju u Settings → [Aplikacija] → Live Activities. Za push updateove potreban je posebni Live Activity push token, ne klasični device token.
Koja je razlika između Widget i Live Activity?
Widget se osvježava periodično prema TimelineProvideru koji vraća unaprijed pripremljenu vremensku liniju. Live Activity ima eksplicitno stanje koje mijenja aplikacija ili poslužitelj kroz update(...) pozive i vidljiv je izvan Home Screena, na Lock Screenu i Dynamic Islandu. Live Activity se pojavljuje samo dok je događaj u tijeku.
Zašto se moj Live Activity ne pojavljuje u Dynamic Islandu?
Najčešći razlozi su tri: uređaj nema Dynamic Island (samo iPhone 14 Pro i noviji), nedostaje NSSupportsLiveActivities u Info.plist-u glavne aplikacije, ili je istovremeno aktivno više Live Activityja pa je vaš u minimal stanju. Provjerite i da ActivityConfiguration stvarno vraća DynamicIsland; bez toga, Lock Screen radi, a Island ne.
Podržavaju li Live Activities Apple Watch u iOS 26?
Da. iOS 26 automatski reflektira Live Activity u watchOS 26 Smart Stack koristeći isti SwiftUI izgled iz kompaktnih Dynamic Island regija, bez potrebe za posebnim watchOS targetom. Ako vam treba prilagođen watchOS izgled, dodajte supplementalActivityFamilies([.small]) modifikator i vratite WatchLayoutView u odgovarajućem branchu.
Praktičan vodič kroz TipKit u iOS 26: kako izraditi savjete, definirati pravila, koristiti TipGroup, sinkronizirati stanje preko iClouda i lokalizirati poruke uz radne SwiftUI primjere.
App Intents u iOS 26 omogućuje da jedan Swift tip radi kao Siri naredba, interaktivni widget i Apple Intelligence akcija. Kompletan vodič s primjerima za Swift 6.
Naučite kako scrollTargetBehavior, scrollPosition, containerRelativeFrame i scrollTransition zamjenjuju stari ScrollViewReader i omogućuju moderne paging carousele u SwiftUI.