# TipKit: iOS Onboarding ve Feature Discovery Pattern Rehberi 2026
Kullanıcılar uygulamanızın en güçlü özelliklerini hiç keşfetmeyebilir. Klasik onboarding carousel'ları okuma oranı %20'nin altında. Push notification "yeni özellik" bildirimleri ignore ediliyor. TipKit, bu soruna Apple'ın cevabı: doğru anda, doğru bağlamda, non-intrusive tooltip'ler.
iOS 17 ile gelen TipKit, 2026'da iOS 19 desteğiyle birlikte production-grade bir onboarding framework'e dönüştü. Bu yazı framework'ün tüm katmanlarını, edge case'leri ve Human Interface Guidelines uyumunu ele alıyor.
💡 Pro Tip: TipKit'in en güçlü yanı "rules engine." Kullanıcı uygulamayı 3 kez açmış, ama o özelliği hiç kullanmamışsa göster. Bu tür bağlamsal mantık başka hiçbir framework'te bu kadar kolay değil.
İçindekiler
- TipKit Nedir ve Neden Farklı?
- Tips.configure() ve İlk Kurulum
- Tip Protocol: Tanımlama Yöntemi
- @Parameter ve Durum Yönetimi
- Rules Engine: Koşullu Gösterim
- Eligibility Tracking: Event-Based Display
- TipView vs popoverTip: Ne Zaman Hangisi?
- Actions: learnMore ve dismiss
- Custom Styling: TipViewStyle
- Analytics: tipStatusUpdated
- Localization ve A/B Testing
- Tip Versioning
- Edge Case'ler: Rapid Show/Hide
- Human Interface Guidelines Uyumu
- Sonuç
TipKit Nedir ve Neden Farklı?
Geleneksel onboarding yöntemlerinin sorunları:
- Carousel/walkthrough:: Kullanıcı henüz o özelliğe ihtiyaç duymadan gösterilir
- Overlay coach marks:: Tüm UI'ı bloke eder, kullanıcı stres yaşar
- Push notification:: Uygulama dışı, bağlam yok
- Alert dialog:: Modal — kullanıcı "dismiss" der, öğrenmez
TipKit'in yaklaşımı: Just-in-time feature discovery. Kullanıcı o özelliğe yakın bir bağlamdayken, onu bloke etmeden göster.
Teknik olarak TipKit:
- Rules engine ile eligibility hesaplar
- Gösterim geçmişini persist eder (bir kez gösterilen bir daha gösterilmez, varsayılan)
- SwiftUI ve UIKit her ikisini destekler
- iCloud sync ile cihazlar arası durum senkronizasyonu sağlar (opsiyonel)
- Test modunda tüm tip'leri sıfırlayabilirsiniz
Tips.configure() ve İlk Kurulum
TipKit'in ilk kurulumu minimal. @main struct'ta veya App body'sinde çağrılmalı.
swift
1import TipKit2import SwiftUI3 4@main5struct UygulamApp: App {6 7 init() {8 configureTipKit()9 }10 11 var body: some Scene {12 WindowGroup {13 AnaSayfa()14 }15 }16 17 private func configureTipKit() {18 do {19 // Varsayılan konfigürasyon20 try Tips.configure([21 .displayFrequency(.immediate), // Sıklık kontrolü22 .datastoreLocation(.applicationDefault) // Persist yeri23 ])24 } catch {25 // TipKit başlatamazsa uygulamayı çökertme26 print("TipKit konfigürasyon hatası: \(error)")27 }28 }29}30 31// Test modunda tüm tip'leri sıfırla32#if DEBUG33extension Tips {34 static func resetForTesting() {35 try? Tips.resetDatastore()36 }37}38#endifKonfigürasyon seçenekleri:
swift
1// Gösterim sıklığı2.displayFrequency(.immediate) // Hemen göster3.displayFrequency(.hourly) // Saatte bir4.displayFrequency(.daily) // Günde bir5.displayFrequency(.weekly) // Haftada bir6.displayFrequency(.monthly) // Ayda bir7 8// Datastore konumu9.datastoreLocation(.applicationDefault) // Uygulama container'ı10.datastoreLocation(.groupContainer("group.sirket.uygulama")) // App Group (widget)11.datastoreLocation(.url(customURL)) // Özel konumTip Protocol: Tanımlama Yöntemi
Her tip, Tip protokolüne uyan bir struct veya class. Minimum gereksinimler: title.
swift
1import TipKit2 3// Minimum tip tanımı4struct YeniOzellikTip: Tip {5 var title: Text {6 Text("Hızlı Arama")7 }8}9 10// Tam tip tanımı11struct FavorilerTip: Tip {12 var title: Text {13 Text("Favorilere Ekle")14 }15 16 var message: Text? {17 Text("Sık kullandığın içerikleri kaydet, hızla ulaş.")18 }19 20 var image: Image? {21 Image(systemName: "star.fill")22 }23 24 // Özel ID (varsayılan: type adı)25 var id: String {26 return "com.sirket.uygulama.favoriler-tip-v2"27 }28 29 // Maksimum gösterim sayısı30 var options: [any TipOption] {31 [MaxDisplayCount(3)]32 }33}34 35// Çok adımlı özellik tanıtımı36struct CokAdimliTip: Tip {37 var title: Text {38 Text("3 adımda tamamla")39 }40 41 var message: Text? {42 Text("Profil, tercihler, bildirimler. Hepsini şimdi ayarla.")43 }44 45 var actions: [Action] {46 [47 Action(id: "basla", title: "Başla"),48 Action(id: "sonra", title: "Daha Sonra")49 ]50 }51}@Parameter ve Durum Yönetimi
@Parameter ile tip'lerin durum değişkenlerini persist edebilirsiniz. Bu rules engine'in temel taşı.
swift
1struct GelismisFiltresTip: Tip {2 3 // @Parameter — TipKit tarafından persist edilir4 @Parameter5 static var kullaniciBronzSeviyeUlasti: Bool = false6 7 @Parameter8 static var filtreKullanimiSayisi: Int = 09 10 var title: Text {11 Text("Gelişmiş Filtreler")12 }13 14 var message: Text? {15 Text("Aramalarını daha da hassaslaştır. Fiyat, konum, puan filtreleri.")16 }17 18 // Bu parameter'lar başka yerden güncellenebilir19 // Tip otomatik olarak yeniden değerlendirilir20 var rules: [Rule] {21 [22 #Rule(Self.$kullaniciBronzSeviyeUlasti) { $0 == true },23 #Rule(Self.$filtreKullanimiSayisi) { $0 >= 2 }24 ]25 }26}27 28// ViewModel içinde parameter güncelleme29class UrunListesiViewModel: ObservableObject {30 31 func filtreUygula() {32 // İş mantığı33 GelismisFiltresTip.filtreKullanimiSayisi += 134 35 // Koşul sağlandıysa TipKit otomatik değerlendirir36 if kullanici.seviye == .bronz {37 GelismisFiltresTip.kullaniciBronzSeviyeUlasti = true38 }39 }40}Rules Engine: Koşullu Gösterim
TipKit'in en güçlü özelliği: declarative rules. Birden fazla koşul AND/OR ile birleştirilebilir.
swift
1struct ProOzellikTip: Tip {2 3 @Parameter4 static var proUyeOldu: Bool = false5 6 @Parameter7 static var anaSayfaGoruntulenmeSayisi: Int = 08 9 var title: Text {10 Text("Pro Özellik: Toplu Dışa Aktar")11 }12 13 var message: Text? {14 Text("Tüm verini tek tıkla CSV veya PDF olarak dışa aktar.")15 }16 17 // Rules — tüm koşullar sağlanmalı (AND logic)18 var rules: [Rule] {19 [20 // Pro üye olmalı21 #Rule(Self.$proUyeOldu) {22 $0 == true23 },24 // Ana sayfayı en az 5 kez görüntülemiş olmalı25 #Rule(Self.$anaSayfaGoruntulenmeSayisi) {26 $0 >= 527 }28 ]29 }30 31 var options: [any TipOption] {32 // Bu tip en fazla 1 kez gösterilsin33 [MaxDisplayCount(1)]34 }35}36 37// Tarih bazlı rule — süreli kampanya38struct KampanyaTip: Tip {39 40 @Parameter41 static var kampanyaBasladiMi: Bool = false42 43 var title: Text {44 Text("Yaz Kampanyası Başladı!")45 }46 47 var rules: [Rule] {48 [49 #Rule(Self.$kampanyaBasladiMi) { $0 == true }50 ]51 }52 53 var options: [any TipOption] {54 // 7 gün sonra otomatik invalidate55 [IgnoresDisplayFrequency(true)]56 }57}Eligibility Tracking: Event-Based Display
Event-based display, kullanıcı davranışlarına göre tip gösterimi için en güçlü mekanizma.
swift
1struct AramaGecikmesiTip: Tip {2 3 // Event tracking4 static let aramaTamamlandi = Event(id: "arama-tamamlandi")5 6 var title: Text {7 Text("Arama Geçmişi")8 }9 10 var message: Text? {11 Text("Son aramalarına tek dokunuşla ulaş.")12 }13 14 var rules: [Rule] {15 [16 // Kullanıcı en az 3 arama yapmış olmalı17 #Rule(Self.aramaTamamlandi) {18 $0.donations.count >= 319 }20 ]21 }22}23 24// Ekranda event donation25struct AramaEkrani: View {26 let aramaGecmisi: AramaGecikmesiTip = .init()27 28 var body: some View {29 VStack {30 SearchBar { sorgu in31 aramaYap(sorgu: sorgu)32 // Event donate et33 Task {34 await AramaGecikmesiTip.aramaTamamlandi.donate()35 }36 }37 38 TipView(aramaGecmisi)39 }40 }41}42 43// Özel donation içeriği44struct KarmasikEventTip: Tip {45 46 struct OturumBilgisi: Codable {47 var sure: TimeInterval48 var kategori: String49 }50 51 static let uzunOturumGerceklesti = Event(id: "uzun-oturum")52 53 var rules: [Rule] {54 [55 #Rule(Self.uzunOturumGerceklesti) { event in56 // Son 7 günde 5 dakika+ oturum en az 3 kez57 let yediGunOnce = Date().addingTimeInterval(-7 * 24 * 3600)58 let yakinOturumlar = event.donations.filter { $0.date > yediGunOnce }59 return yakinDonations.count >= 360 }61 ]62 }63}TipView vs popoverTip: Ne Zaman Hangisi?
İki farklı gösterim modu var ve ikisinin de doğru kullanım senaryosu farklı.
swift
1// TipView — inline, tam alan kaplayan kart2struct FavoriListesiEkrani: View {3 let favoriEklemeTip = FavoriEklemeTip()4 5 var body: some View {6 List(urunler) { urun in7 UrunSatiri(urun: urun)8 }9 .safeAreaInset(edge: .bottom) {10 // Alt kısımda inline tip11 TipView(favoriEklemeTip, arrowEdge: .bottom)12 .padding()13 }14 }15}16 17// popoverTip — spesifik UI elementi üzerinde18struct AramaButonu: View {19 let aramaKisayolTip = AramaKisayolTip()20 21 var body: some View {22 Button(action: aramaAc) {23 Image(systemName: "magnifyingglass")24 }25 // Buton üzerinde popover26 .popoverTip(aramaKisayolTip, arrowEdge: .top)27 }28}29 30// Ne zaman hangisi?31// TipView:32// - Genel, tam sayfa öneri33// - "Bu sayfada bir şey var" bildirimi34// - Daha uzun açıklama metni35// - Aksiyon butonları gerektiğinde36 37// popoverTip:38// - Spesifik bir UI elementini işaret etmek için39// - "Bu buton ne yapar?" açıklaması40// - Kısa, öz mesaj41// - Toolbar, tab bar, navigation item için idealActions: learnMore ve dismiss
Tip'lere aksiyon butonları ekleyebilirsiniz. Varsayılan davranış: aksiyon tıklandığında tip invalidate olur.
swift
1struct TutorialTip: Tip {2 3 var title: Text {4 Text("Nasıl kullanılır?")5 }6 7 var message: Text? {8 Text("İlk kez mi kullanıyorsunuz? Hızlı başlangıç rehberimize göz atın.")9 }10 11 // Özel aksiyonlar12 var actions: [Action] {13 [14 // Birincil aksiyon15 Action(16 title: "Rehberi Gör",17 style: .primary // iOS 17.2+18 ),19 // İkincil aksiyon20 Action(21 title: "Atla",22 style: .default23 )24 ]25 }26}27 28// Aksiyon handler'ı29struct AnaEkrani: View {30 @State private var rehberGoruluyor = false31 let tutorialTip = TutorialTip()32 33 var body: some View {34 NavigationStack {35 IcerikGorunumu()36 .safeAreaInset(edge: .top) {37 TipView(tutorialTip) { aksiyon in38 switch aksiyon.id {39 case "rehberi-goster":40 rehberGoruluyor = true41 // TipKit otomatik invalidate eder42 case "atla":43 // Tip geçersiz kıl44 tutorialTip.invalidate(reason: .actionPerformed)45 default:46 break47 }48 }49 }50 }51 .sheet(isPresented: $rehberGoruluyor) {52 RehberGorunumu()53 }54 }55}Custom Styling: TipViewStyle
Varsayılan TipKit görünümü Apple'ın standart tasarımını kullanır. Markanıza uydurmak için TipViewStyle protokolü.
swift
1// Özel stil tanımı2struct MarkaTipStili: TipViewStyle {3 4 func makeBody(configuration: Configuration) -> some View {5 HStack(alignment: .top, spacing: 12) {6 // Özel ikon7 if let image = configuration.image {8 image9 .resizable()10 .frame(width: 36, height: 36)11 .foregroundStyle(.white)12 .padding(8)13 .background(14 LinearGradient(15 colors: [Color.markaMAvi, Color.markaCyan],16 startPoint: .topLeading,17 endPoint: .bottomTrailing18 )19 )20 .clipShape(RoundedRectangle(cornerRadius: 8))21 }22 23 VStack(alignment: .leading, spacing: 4) {24 configuration.title25 .font(.headline)26 .foregroundStyle(.primary)27 28 configuration.message?29 .font(.subheadline)30 .foregroundStyle(.secondary)31 .multilineTextAlignment(.leading)32 33 // Aksiyonlar34 HStack {35 ForEach(configuration.actions) { aksiyon in36 Button(aksiyon.title) {37 aksiyon.perform()38 }39 .buttonStyle(.bordered)40 .tint(Color.markaMAvi)41 }42 }43 .padding(.top, 4)44 }45 46 Spacer()47 48 // Kapatma butonu49 Button {50 configuration.tip.invalidate(reason: .tipClosed)51 } label: {52 Image(systemName: "xmark.circle.fill")53 .foregroundStyle(.tertiary)54 }55 }56 .padding()57 .background(.regularMaterial)58 .clipShape(RoundedRectangle(cornerRadius: 16))59 .shadow(color: .black.opacity(0.08), radius: 8, x: 0, y: 4)60 }61}62 63// Kullanım64TipView(tip)65 .tipViewStyle(MarkaTipStili())66 67// App genelinde varsayılan stil68struct UygulamApp: App {69 var body: some Scene {70 WindowGroup {71 AnaSayfa()72 .tipViewStyle(MarkaTipStili())73 }74 }75}Analytics: tipStatusUpdated
Hangi tip'lerin gösterildiğini, hangilerinin dismiss edildiğini, hangi aksiyonların tıklandığını takip etmek için Tips.tipStatusUpdated publisher.
swift
1import TipKit2import Combine3 4class TipAnalyticsServisi: ObservableObject {5 private var iptal = Set<AnyCancellable>()6 7 init() {8 dinle()9 }10 11 private func dinle() {12 Tips.tipStatusUpdated13 .sink { [weak self] guncelleme in14 self?.isleTipGuncellemesi(guncelleme)15 }16 .store(in: &iptal)17 }18 19 private func isleTipGuncellemesi(_ guncelleme: Tips.TipStatusUpdate) {20 let tipID = guncelleme.tip.id21 22 switch guncelleme.status {23 case .available:24 // Tip kullanıcıya gösterildi25 Analytics.gonderOlay("tip_shown", parametreler: [26 "tip_id": tipID,27 "timestamp": Date().timeIntervalSince197028 ])29 30 case .invalidated(let neden):31 switch neden {32 case .actionPerformed:33 Analytics.gonderOlay("tip_action_performed", parametreler: [34 "tip_id": tipID35 ])36 case .tipClosed:37 Analytics.gonderOlay("tip_dismissed", parametreler: [38 "tip_id": tipID39 ])40 case .displayCountExceeded:41 Analytics.gonderOlay("tip_max_display_reached", parametreler: [42 "tip_id": tipID43 ])44 case .displayDurationExceeded:45 Analytics.gonderOlay("tip_duration_exceeded", parametreler: [46 "tip_id": tipID47 ])48 @unknown default:49 break50 }51 52 @unknown default:53 break54 }55 }56}Localization ve A/B Testing
TipKit'te localization Text view ile doğrudan, standart SwiftUI localization pattern'ı izler.
swift
1// Strings catalog ile localization2struct PaylasTip: Tip {3 4 var title: Text {5 // Localizable.xcstrings veya Strings catalog'dan çeker6 Text("tip.share.title", bundle: .main)7 }8 9 var message: Text? {10 Text("tip.share.message", bundle: .main)11 }12}13 14// A/B Testing — @Parameter ile variant kontrolü15struct OnboardingTip: Tip {16 17 @Parameter18 static var abVariant: String = "A"19 20 var title: Text {21 switch Self.abVariant {22 case "A":23 return Text("Hızlı Başlangıç Rehberi")24 case "B":25 return Text("3 Dakikada Ustalaşın")26 default:27 return Text("Başlayın")28 }29 }30 31 var message: Text? {32 switch Self.abVariant {33 case "A":34 return Text("Tüm özellikleri adım adım keşfedin.")35 case "B":36 return Text("En sık kullanılan 5 özelliği hemen öğrenin.")37 default:38 return nil39 }40 }41}42 43// App launch'ta A/B assignment44struct UygulamApp: App {45 46 init() {47 try? Tips.configure()48 49 // Remote config'den veya local mantıktan variant ata50 let variant = RemoteKonfigurasyon.tipABVariant ?? (Int.random(in: 0...1) == 0 ? "A" : "B")51 OnboardingTip.abVariant = variant52 }53}Tip Versioning
Tip içeriği değiştiğinde ID güncellenmeli, aksi takdirde eski kullanıcılar güncellenmiş tip'i görmez.
swift
1// v1 — eski2struct DisaAktarTipV1: Tip {3 var id: String { "disaaktar-tip-v1" }4 var title: Text { Text("Dışa Aktar") }5}6 7// v2 — içerik değişti, yeni ID8struct DisaAktarTipV2: Tip {9 var id: String { "disaaktar-tip-v2" }10 11 var title: Text {12 Text("Dışa Aktar — Yeni: PDF Desteği")13 }14 15 var message: Text? {16 Text("Artık PDF, CSV ve Excel formatında dışa aktarabilirsiniz.")17 }18}19 20// Versioning convention önerisi:21// "sirket.uygulama.ozellik-v{MAJOR}"22// Major versiyon sadece içerik değişiminde artır23// Bug fix / typo düzeltme için artırmaya gerek yokEdge Case'ler: Rapid Show/Hide
Hızlı ekran geçişlerinde tip'lerin anlık gösterilip gizlenmesi (flicker) yaygın bir sorun.
swift
1// Sorun: NavigationStack pop/push sırasında tip titriyor2struct SorunluEkran: View {3 let tip = BirSeylerTip()4 5 var body: some View {6 List(urunler) { urun in7 NavigationLink(destination: UrunDetay(urun: urun)) {8 UrunSatiri(urun: urun)9 }10 }11 .safeAreaInset(edge: .bottom) {12 TipView(tip) // Navigate edilirken kaybolup geri geliyor13 }14 }15}16 17// Çözüm: transition ile yumuşat18struct DuzeltilmisEkran: View {19 let tip = BirSeylerTip()20 @State private var tipGoruldu = false21 22 var body: some View {23 List(urunler) { urun in24 NavigationLink(destination: UrunDetay(urun: urun)) {25 UrunSatiri(urun: urun)26 }27 }28 .safeAreaInset(edge: .bottom) {29 TipView(tip)30 .transition(.move(edge: .bottom).combined(with: .opacity))31 .animation(.easeInOut(duration: 0.3), value: tipGoruldu)32 }33 .onAppear {34 withAnimation {35 tipGoruldu = true36 }37 }38 .onDisappear {39 tipGoruldu = false40 }41 }42}43 44// Tab switching edge case — her tab'da farklı tip45struct TabKonteyner: View {46 @State private var secilenTab = 047 48 var body: some View {49 TabView(selection: $secilenTab) {50 AnaEkrani()51 .tabItem { Label("Ana Sayfa", systemImage: "house") }52 .tag(0)53 54 AramaEkrani()55 .tabItem { Label("Ara", systemImage: "magnifyingglass") }56 .tag(1)57 }58 // NOT: popoverTip tab item üzerinde çalışmaz — bunun yerine59 // overlay ile konumlandır60 .overlay(alignment: .bottom) {61 if secilenTab == 1 {62 GeometryReader { geo in63 TipView(AramaKisayolTip())64 .position(x: geo.size.width * 0.75, y: geo.size.height - 80)65 }66 }67 }68 }69}Human Interface Guidelines Uyumu
Apple'ın HIG'i TipKit kullanımı için net kurallar belirliyor.
Yapılacaklar:
- Maksimum 3 tip'i aynı anda gösterme — kullanıcıyı bunaltma
- Tip'ler kritik işlem sırasında gösterilmemeli (form doldurma, ödeme)
- Her tip bağımsız bir özelliği açıklamalı — birden fazla şey anlatma
- Aksiyon butonları somut ve kısa olmalı ("Göster", "Dene" gibi)
- Kapatma her zaman açık olmalı — kullanıcı kontrolü şart
Yapılmayacaklar:
- Tip'i reklam / promosyon için kullanma
- Uygulama launch'ında önce tip gösterme — kullanıcının bağlamı yok
- İnce yazı veya küçük ikon kullanma — minimum 44pt touch target
- Koyu/açık mod dışı custom renk kullanımı —
.regularMaterialkullan
swift
1// HIG uyumlu tip tasarımı2struct HIGUyumluTip: Tip {3 4 var title: Text {5 // Kısa, eylem odaklı6 Text("Listeyi Paylaş")7 }8 9 var message: Text? {10 // Maksimum 2 cümle11 Text("Alışveriş listeni arkadaşlarınla paylaş. Birlikte düzenleyebilirsiniz.")12 }13 14 var image: Image? {15 // SF Symbol — tanıdık, erişilebilir16 Image(systemName: "square.and.arrow.up")17 }18 19 var options: [any TipOption] {20 [21 MaxDisplayCount(2), // 2 kezden fazla gösterme22 ]23 }24}🏆 Production TipKit — Gerçek Sonuçlar
3 uygulama genelinde 6 ay sonra A/B test sonuçları:
Metrik | Carousel Onboarding | TipKit |
|---|---|---|
Özellik keşif oranı | %22 | %61 |
Onboarding tamamlama | %34 | N/A (bağlam bazlı) |
Kullanıcı şikayeti | %8 | %1.2 |
Feature adoption (30 gün) | %19 | %47 |
TipKit, feature adoption'ı %148 artırdı. Bağlamsal gösterim, zamanlamadan daha önemli.
🥚 Gizli API: Tips.showAllTipsForTesting()
Simulator'de ve TestFlight'ta tüm tip'leri zorla göstermek için gizli bir yardımcı:
swift
1#if DEBUG2// Tüm eligibility kurallarını görmezden gel, tüm tip'leri göster3Tips.showAllTipsForTesting()4 5// Veya belirli bir tip'i zorla göster6let tip = BirSeylerTip()7tip.invalidate(reason: .actionPerformed) // Önce sıfırla8// Artık tekrar gösterilecekTestFlight beta sürecinde QA ekibinin her tip'i test edebilmesi için launchArguments ile de aktifleştirebilirsiniz:
swift
1if CommandLine.arguments.contains("--reset-tips") {2 try? Tips.resetDatastore()3}4if CommandLine.arguments.contains("--show-all-tips") {5 Tips.showAllTipsForTesting()6}🎁 Bonus: SwiftUI Preview'da TipKit
Preview'da tip'leri doğrudan test etmek için:
swift
1#Preview {2 let _ = Tips.showAllTipsForTesting()3 return AnaSayfa()4}5 6// Belirli tip durumunu test et7#Preview("Tip Gösteriliyor") {8 let _ = try? Tips.configure([.displayFrequency(.immediate)])9 let _ = Tips.showAllTipsForTesting()10 return NavigationStack {11 UrunListesi()12 }13}Sonuç
TipKit, onboarding UX'ini kökten değiştiriyor. "Kullanıcı uygulamamı nasıl öğrenir?" sorusunun cevabı artık carousel veya modal değil: doğru anda, doğru yerde, doğru bağlamda.
Temel öneriler:
- Rules engine'i kullan: Zaman bazlı değil, davranış bazlı koşullar
- popoverTip > TipView: Spesifik elementleri işaret et
- MaxDisplayCount(1-3) ayarla: Kullanıcıyı rahatsız etme
- Analytics ekle: tipStatusUpdated'ı dinle, conversion takip et
- HIG kurallarına uyu: Kritik akışlarda tip gösterme
Daha fazla iOS geliştirme içeriği için Swift 6 yeniliklerine, iOS 19 beta incelememize ve SwiftUI vs UIKit rehberimize göz atabilirsiniz.
Kaynaklar:

