TipKit: Vodič za prikaz savjeta i onboarding u iOS 26

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.

TipKit Vodič iOS 26: Savjeti i Onboarding

Ažurirano: 5. rujna 2026.

TipKit je Appleov framework za prikaz kontekstualnih savjeta unutar iOS, iPadOS, macOS, watchOS i tvOS aplikacija, tako da korisnici otkriju skrivene funkcionalnosti bez klasičnog tutoriala. Predstavljen je na WWDC-u 2023., a u iOS 26 dobio je značajna poboljšanja: grupiranje savjeta (TipGroup), naprednu sinkronizaciju putem iClouda, kompaktne varijante za widgete i Control Center te bolju integraciju s App Intents. U ovom vodiču pokazat ću vam, korak po korak, kako implementirati TipKit u SwiftUI i UIKit projektima. Iskreno, kad sam ga prvi put ubacio u produkcijsku aplikaciju, uštedio mi je barem tjedan dana rada na custom onboardingu.

  • TipKit zahtijeva iOS 17+, a u iOS 26 uveden je TipGroup za redoslijed prikaza više savjeta i .compact stil za tijesne prostore.
  • Svaki savjet je struct koji implementira Tip protokol i definira title, message, opcionalnu image te rules i options.
  • Postoje dvije glavne varijante prikaza: TipView (inline u sadržaju) i .popoverTip() modifier (plutajući oblačić iznad elementa).
  • Pravila (#Rule macro) kombiniraju parametarska i događajna ograničenja. Savjet se pojavljuje samo kada su svi uvjeti zadovoljeni.
  • Tips.configure() mora se pozvati jednom pri pokretanju aplikacije s opcijama za displayFrequency i datastoreLocation.
  • Za lokalizaciju koristite LocalizedStringResource u title i message. TipKit automatski preuzima prijevode iz String Catalog.

Što je TipKit i kada ga koristiti

TipKit je deklarativni framework za prikaz malih obrazovnih poruka (savjeta) koje korisnicima pomažu otkriti funkcionalnosti koje bi inače propustili. Za razliku od velikih onboarding tutoriala koji zaustavljaju korisnika prije nego što uopće vidi aplikaciju, savjeti se pojavljuju u kontekstu. Točno onda kad korisnik dođe do zaslona gdje ta funkcionalnost stvarno postoji. Prema Appleovim Human Interface Guidelines za pomoć u aplikacijama, savjeti su najkorisniji kada uvode nove značajke nakon ažuriranja, otkrivaju skrivene geste (npr. dugo držanje) ili objašnjavaju nesvakidašnja ponašanja korisničkog sučelja.

Framework rješava probleme koje su prije razvojni timovi morali graditi ručno: praćenje je li savjet već prikazan, ograničavanje učestalosti pojavljivanja, sinkronizaciju između uređaja i A/B testiranje varijanti. U iOS 26 TipKit je dobio i novu TipGroup arhitekturu koja rješava klasičan problem "koji savjet se prvi prikazuje kada tri odjednom postanu vidljiva". Više o tome kasnije. Ako pišete aplikaciju koja cilja iOS 17 ili noviji, TipKit je gotovo uvijek bolji izbor od custom komponenti zbog dosljednog dizajna, pristupačnosti i integracije sa sustavskim preferencijama korisnika.

Postavljanje TipKit-a u Xcode 26 projektu

TipKit je sistemski framework, tako da ga ne trebate instalirati putem Swift Package Managera. Otvorite svoj SwiftUI projekt u Xcode 26, provjerite da je minimalni deployment target postavljen na iOS 17.0 ili više, i uvezite framework tamo gdje ga trebate:

import TipKit
import SwiftUI

Sljedeći korak je konfiguracija centralnog spremišta stanja. Ovo se obavlja jednom, obično u App strukturi ili u @main ulaznoj točki. Konfiguracija odlučuje kako često se savjeti mogu prikazivati te gdje se pohranjuje stanje (koji su savjeti prikazani, na koji gumb je korisnik kliknuo).

@main
struct MyApp: App {
    init() {
        try? Tips.configure([
            .displayFrequency(.immediate),
            .datastoreLocation(.applicationDefault)
        ])
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

Opcija .displayFrequency(.immediate) znači da će se svaki kvalificirani savjet prikazati čim su njegova pravila zadovoljena. Ako želite ograničiti korisnika na jedan savjet dnevno, koristite .displayFrequency(.daily), a za potpuno prilagođen razmak .displayFrequency(.hourly) ili prilagođeni TimeInterval. Za razvoj i testiranje često je korisno postaviti .immediate i dodavati #Rule uvjete za kontrolu prikaza.

Kako izraditi prvi savjet u SwiftUI-ju

Svaki savjet u TipKit-u je Swift struct koji implementira Tip protokol. Minimalna implementacija zahtijeva samo svojstvo title, a preporuča se dodati i message te image za jasniju komunikaciju. Evo primjera savjeta koji objašnjava kako spremiti članak u favorite:

struct FavoriteArticleTip: Tip {
    var title: Text {
        Text("Spremite članak u favorite")
    }

    var message: Text? {
        Text("Dugo držite gumb srca kako biste dodali članak u kolekciju za offline čitanje.")
    }

    var image: Image? {
        Image(systemName: "heart.fill")
    }
}

Da biste ga prikazali unutar SwiftUI hijerarhije, instancirajte tip i predajte ga TipView-u ili .popoverTip() modifieru. Za inline verziju iznad liste članaka:

struct ArticleListView: View {
    private let favoriteTip = FavoriteArticleTip()

    var body: some View {
        List {
            TipView(favoriteTip, arrowEdge: .bottom)
            ForEach(articles) { article in
                ArticleRow(article: article)
            }
        }
    }
}

Za popover koji strši prema određenom gumbu:

Button {
    toggleFavorite()
} label: {
    Image(systemName: "heart")
}
.popoverTip(favoriteTip, arrowEdge: .top)

Nakon što se savjet prikaže, TipKit automatski bilježi njegovo stanje. Kada korisnik dodirne "x" za zatvaranje, savjet se neće ponovno pojaviti (osim ako ručno ne resetirate stanje).

Pravila prikaza: parametri i događaji

Pravila (rules) su ono što TipKit čini pametnim. Umjesto da ručno pratite "je li korisnik već tri puta otvorio zaslon" i "je li tekstno polje prazno", to opisujete deklarativno pomoću #Rule macroa iz Swift 6. Postoje dvije vrste pravila: parametarska koja prate @Parameter vrijednosti i događajna koja broje Event pojave.

Evo primjera savjeta koji se prikazuje samo za korisnike koji su otvorili aplikaciju barem pet puta, ali još nisu kliknuli na gumb za dijeljenje:

struct ShareTip: Tip {
    @Parameter static var isPremiumUser: Bool = false

    static let appOpenEvent = Event(id: "appOpened")
    static let shareTappedEvent = Event(id: "shareTapped")

    var title: Text { Text("Podijelite svoju kolekciju") }
    var message: Text? { Text("Vaši prijatelji mogu vidjeti vaše favorite putem AirDropa.") }
    var image: Image? { Image(systemName: "square.and.arrow.up") }

    var rules: [Rule] {
        #Rule(Self.$isPremiumUser) { $0 == true }
        #Rule(Self.appOpenEvent) { $0.donations.count >= 5 }
        #Rule(Self.shareTappedEvent) { $0.donations.count == 0 }
    }
}

Događaje "donirate" pomoću metode donate():

await ShareTip.appOpenEvent.donate()

Parametre postavljate izravno kao statičku vrijednost kad god se promijeni stanje korisnika (npr. nakon uspješne kupnje pretplate):

ShareTip.$isPremiumUser.wrappedValue = purchaseManager.isSubscribed

TipView vs PopoverTip: koju varijantu odabrati

Iako oba oblika prikazuju isti Tip model, njihova primjena i vizualna težina bitno se razlikuju. Sljedeća tablica sažima ključne dimenzije za odabir:

KriterijTipView (inline)PopoverTip (plutajući)
Zauzima prostor u layoutuDa, pomiče sadržajNe, lebdi iznad
Idealno zaPrazna stanja, vrhove listaTočkasto uvođenje značajke uz gumb
Vidljivost streliceOpcionalnaUvijek pokazuje na sidrište
Ponašanje pri scrolluSkrola zajedno sa sadržajemAutomatski se zatvara pri pomicanju
Prekida interakcijuNeDjelomično, dok je otvoren
Preporučeni maksimum na zaslonu1 istovremeno1 istovremeno
iOS 26 kompaktni stilPodržan (.tipViewStyle(.compact))Nije podržan

U praksi biram TipView kada želim edukativnu poruku koja ostaje na zaslonu dok korisnik istražuje (npr. da objasnim prazan popis), a PopoverTip za precizno "poznaješ li ovu ikonicu?" iskustvo iznad konkretnog gumba u toolbaru. Ako ciljate iOS 26, isprobajte i novi .compact stil za TipView unutar Control Center widgeta ili Live Activity. Smanjuje visinu na oko 44 pt i uklanja slikovni element.

Grupiranje savjeta pomoću TipGroup u iOS 26

Do iOS-a 26 razvojni timovi su ručno rješavali sukobe kada je više savjeta ispunilo uvjete istovremeno. Obično dodavanjem "prethodni je zatvoren" pravila u svaki naredni. Novi TipGroup tip iz službene TipKit dokumentacije uvodi deklarativan način definiranja redoslijeda i pravila prioriteta. U mojem zadnjem projektu upravo je ovo bilo mjesto gdje smo prije imali najviše bugova.

@MainActor
struct OnboardingTips {
    static let group = TipGroup(.ordered) {
        WelcomeTip()
        FavoriteArticleTip()
        ShareTip()
        SyncTip()
    }
}

Zatim u pogledu prikazujete trenutni aktivni savjet iz grupe:

if let current = OnboardingTips.group.currentTip {
    TipView(current, arrowEdge: .bottom)
}

Konstruktor prihvaća dvije politike: .ordered koji prikazuje savjete redom kojim su navedeni i .firstAvailable koji uzima prvi čija su pravila zadovoljena bez obzira na poredak. Ordered je idealan za linearan onboarding, dok firstAvailable dobro funkcionira za kontekstualne savjete gdje svi ravnopravno kandidiraju. Grupe rade i s .popoverTip() modifierima. Jednostavno provjerite group.currentTip prije prosljeđivanja.

Kako lokalizirati savjete s TipKit-om

Ako aplikacija podržava više jezika, koristite LocalizedStringResource umjesto običnog stringa. TipKit će prilikom prikaza automatski dohvatiti prijevod iz String Cataloga ili Localizable.xcstrings datoteke.

var title: Text {
    Text("tip.favorite.title", bundle: .main, comment: "Naslov savjeta za favorite")
}

var message: Text? {
    Text("tip.favorite.message", bundle: .main, comment: "Objašnjenje kako dodati favorit")
}

U Xcodeu 26 preporuča se koristiti String Catalog (.xcstrings) jer automatski izvlači ključeve iz izvornog koda pri buildu. Za više detalja o Swiftovom sustavu lokalizacije pogledajte i vodič o App Intents integraciji sa Siri i Apple Intelligence, gdje je slična tehnika ključna za lokalizaciju glasovnih naredbi.

Akcije, gumbi i zatvaranje savjeta

Savjeti mogu imati do dvije akcije, obično "Saznaj više" i "Nemoj mi prikazivati". Akcije se definiraju kao Tip.Action instance s jedinstvenim ID-em:

var actions: [Action] {
    Action(id: "learn-more", title: "Saznaj više")
    Action(id: "dismiss", title: "Ne prikazuj ponovno")
}

Na strani pogleda reagirate na klikove pomoću closure verzije TipView-a:

TipView(favoriteTip) { action in
    if action.id == "learn-more" {
        openHelpURL()
    } else if action.id == "dismiss" {
        favoriteTip.invalidate(reason: .actionPerformed)
    }
}

Metoda invalidate(reason:) označava savjet kao "gotov". Više se neće prikazivati bez obzira na pravila. Alternativni razlozi uključuju .tipClosed (korisnik je dodirnuo "x") i .maxDisplayCountExceeded (dosegnut je maksimalni broj prikaza). Ako trebate resetirati sve savjete tijekom razvoja, pozovite try Tips.resetDatastore(). Vrlo je korisno prilikom testiranja različitih rubnih slučajeva.

Sinkronizacija stanja putem iClouda

Ako korisnik ima vašu aplikaciju na iPhoneu i iPadu, ne želite mu pokazati isti savjet dva puta. TipKit rješava ovo kroz CloudKitTipsDatastoreLocation. Konfigurirajte ga jednom pri pokretanju:

try? Tips.configure([
    .displayFrequency(.immediate),
    .datastoreLocation(.groupContainer(identifier: "group.dev.swiftcrafted.app"))
])

Za pravu iCloud sinkronizaciju u iOS 26 (novo!) koristite:

try? Tips.configure([
    .cloudKitContainer(identifier: "iCloud.dev.swiftcrafted.tips"),
    .displayFrequency(.immediate)
])

Preduvjet je da vaš App ID ima omogućen CloudKit capability i da korisnik bude prijavljen u svoj iCloud račun. Sinkronizacija je eventualno konzistentna, pa može potrajati nekoliko minuta da se stanje propagira preko uređaja. Za offline scenarije TipKit se ponaša ispravno: koristi lokalno stanje sve dok se sinkronizacija ne završi. Ako se koristite SwiftDatom za perzistenciju podataka, isti CloudKit container može poslužiti i za TipKit i za vaše modele.

Testiranje i debugiranje savjeta

Tijekom razvoja često trebate prisiliti savjet da se prikaže bez čekanja da se ispune sva pravila. TipKit nudi dva korisna alata. Prvi je Tips.showAllTipsForTesting() koji zaobilazi pravila i prikazuje svaki savjet čim je vidljiv u hijerarhiji pogleda:

#if DEBUG
Tips.showAllTipsForTesting()
#endif

Drugi je Tips.hideAllTipsForTesting() za slučaj kada testirate UI i ne želite da savjeti smetaju screenshot testovima. Za granularniju kontrolu koristite Tips.showTipForTesting(_:) s konkretnim tipom.

Preporučam pisanje unit testova koji verificiraju logiku pravila. Iskoristite Swift Testing framework i #expect makro kako biste provjerili da se savjet aktivira nakon točno pet donacija događaja. Za snimanje ekrana Marketing tim voli imati "svi savjeti otvoreni" scenarij, a showAllTipsForTesting() u UITest launch argumentima to rješava elegantno. Meni je taj mali trik uštedio sate ručnog resetiranja stanja između snimanja.

Često postavljana pitanja

Radi li TipKit na iOS 16 ili starijim verzijama?

Ne. TipKit zahtijeva iOS 17, iPadOS 17, macOS 14, watchOS 10 ili tvOS 17 kao minimum. Za starije verzije morate implementirati vlastito rješenje ili koristiti biblioteke treće strane poput SwiftUI overlay-a s @AppStorage praćenjem stanja.

Kako trajno sakriti savjet nakon što ga korisnik zatvori?

Pozovite tip.invalidate(reason: .actionPerformed) ili .tipClosed kada korisnik dovrši povezanu radnju ili dodirne gumb za zatvaranje. Nakon invalidate poziva TipKit neće ponovno prikazati taj savjet, čak i ako njegova pravila kasnije budu ponovno zadovoljena.

Koja je razlika između TipView i PopoverTip u SwiftUI-ju?

TipView je inline komponenta koja zauzima prostor u layoutu, idealna za popise i prazna stanja. PopoverTip (dostupan kroz .popoverTip() modifier) prikazuje plutajući oblačić iznad ciljanog elementa i automatski se zatvara pri scrollu.

Mogu li resetirati sve savjete tijekom razvoja?

Da. Pozovite try Tips.resetDatastore() nakon Tips.configure() ili prije njega. Ovo obriše sve zabilježene događaje, parametre i statuse savjeta. Preporučuje se koristiti isključivo iza #if DEBUG zaštite.

Kako se TipKit lokalizira na hrvatski i druge jezike?

Koristite Text("ključ", comment: "opis") ili LocalizedStringResource unutar title i message svojstava tipa. Zatim dodajte prijevode u Localizable.xcstrings String Catalog. TipKit automatski koristi trenutni jezik sustava pri renderiranju.

Podržava li TipKit A/B testiranje varijanti savjeta?

Nema ugrađen A/B mehanizam, ali možete definirati više struktura koje implementiraju Tip protokol i odabirati koju instancirati na temelju @Parameter vrijednosti povezane s korisničkim eksperimentom. Kombiniranjem s TipGroup(.firstAvailable) dobiva se elegantno rješenje bez custom logike.

Editorial Team
O Autoru Editorial Team

Our team of expert writers and editors.