ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • SwiftUI - TipKit
    SwiftUI 2026. 10. 3. 09:56

    안녕하세요. 그린입니다 🍏
    이번 포스팅에서는 SwiftUI의 TipKit 프레임워크에 대해 애플 공식 문서를 기준으로 정리해보겠습니다 🙋🏻

    Intro

    주제: TipKit

    프레임워크: TipKit

    지원 버전: iOS/iPadOS 17 · macOS 14 · tvOS 16 · watchOS 10 · visionOS 1 이상

    공식 문서: developer.apple.com/documentation/tipkit

    TipKit이란

    공식 문서는 TipKit을 이렇게 소개해요.

    Use TipKit to show contextual tips that highlight new, interesting, or unused features people haven't discovered on their own yet.

     

    즉, 사람들이 아직 스스로 발견하지 못한 기능을 가볍게 알려주는 용도예요.

    Tip 프로토콜로 팁의 내용과, 그 팁이 언제 나타나야 하는지의 조건을 정의하고, TipView로 그 기능에 시선을 끌게 만들어요.

     

    공식 문서는 사용 가이드도 명확히 제시해요.

    • 사람들을 부담주지 않도록 팁은 아껴서 써야 함
    • 스스로 발견하기 어려운, 눈에 잘 안 띄는 기능에만 써야 함
    • 앱을 쓸 때마다 매번 팁을 띄우지 말 것 — 불필요하게 나타나면 방해가 됨
    • 팁으로 앱 사용법을 처음부터 끝까지 안내하거나, 광고/프로모션 목적으로 쓰면 안 됨

    디자인 가이드는 Human Interface Guidelines의 Offering Help 문서를 참고하라고 안내하고 있어요.

     

    Tip 프로토콜 — 팁의 내용 정의하기

    공식 문서의 첫 예제 코드예요.

    import SwiftUI
    import TipKit
    
    // Define your tip's content.
    struct FavoriteLandmarkTip: Tip {
        var title: Text {
            Text("Save as a Favorite")
        }
    
        var message: Text? {
            Text("Your favorite landmarks always appear at the top of the list.")
        }
    
        var image: Image? {
            Image(systemName: "star")
        }
    }

    Tip은 Identifiable, Sendable을 채택하는 프로토콜이에요.

    팁이 유효하려면 title은 반드시 설정해야 하고, message와 image는 선택이에요.

    rules와 options 프로퍼티에 Rule과 Option 인스턴스를 전달해서 팁이 언제 나타날지 제어할 수 있어요.

     

    앱 시작 시 팁 로드하기 — Tips.configure(_:)

    팁을 화면에 띄우기 전에, 먼저 앱에 로드해야 해요.

    공식 문서는 앱 세션마다 한 번, 예를 들어 App의 init()에서 configure(_:)를 호출하는 걸 베스트 프랙티스로 안내해요.

     

    @main
    struct TipKitExamples: App {
        var body: some Scene {
            WindowGroup {
                ContentView()
            }
        }
    
        init() {
            do {
                // Configure and load all tips in the app.
                try Tips.configure()
            }
            catch {
                print("Error initializing tips: \(error)")
            }
        }
    }

    configure(_:)에는 ConfigurationOption 배열을 전달해서 세부 설정도 가능해요.

    • displayFrequency(_:): 팁이 뜬 뒤 다음 팁이 뜨기까지의 최소 간격을 설정. .immediate(기본값), .hourly, .daily, .weekly, .monthly 중 선택
    • datastoreLocation(_:): 팁 상태를 저장할 위치를 커스터마이즈
    • cloudKitContainer(_:): 팁 동기화에 쓸 CloudKit 컨테이너를 설정

    .monthly를 예로 들면, 공식 문서는 이렇게 설명해요.

    When you set this, the system shows no more than one tip per month.

     

    팁 띄우기 — Inline과 Popover

    팁을 보여주는 방법은 두 가지예요.

    공식 문서는 가능하면 인라인(Inline) 방식을 먼저 쓰라고 권장해요.

    사람들이 상호작용하고 싶어할 UI 요소를 가리지 않기 때문이에요.

     

    인라인 팁 — TipView

    struct InlineView: View {
        // Create an instance of your tip content.
        let inlineTip = InlineTip()
    
        var body: some View {
            VStack(spacing: 20) {
                Text("A TipView embeds itself directly in the view.")
    
                // Place your tip near the feature you want to highlight.
                TipView(inlineTip, arrowEdge: .bottom)
                Button {
                    // Invalidate the tip when someone uses the feature.
                    inlineTip.invalidate(reason: .actionPerformed)
                } label: {
                    Label("Favorite", systemImage: "star")
                }
                Spacer()
            }
            .padding()
        }
    }

     

    인라인 팁은 그 뷰 안에 직접 끼어들어 자리를 차지해요.

    그래서 팁이 나타나거나 사라질 때 주변 레이아웃이 그에 맞춰 바뀝니다.

     

    팝오버 팁 — popoverTip(_:arrowEdge:action:)

    struct PopoverView: View {
        let highlightTip = HighlightTip()
        
        var body: some View {
            Image(systemName: "wand.and.stars")
                .imageScale(.large)
                // Place the tip on the feature to highlight.
                .popoverTip(highlightTip)
                .onTapGesture {
                    tip.invalidate(reason: .actionPerformed)
                }
        }
    }

     

    팝오버 방식은 화면 위에 팁을 띄워서 아래 UI를 가려요.

    레이아웃을 바꾸고 싶지 않거나, 팁이 가리는 컨트롤과 상호작용할 필요가 없을 때 적합해요.

    팝오버는 이미 기능을 가리키고 있으니, 이미지는 생략하는 걸 고려해보라고도 안내합니다.

     

    팁 무효화하기 — invalidate(reason:)

    사람이 그 기능을 실제로 쓰고 나면, 팁을 프로그래밍적으로 무효화할 수 있어요.

     

    InvalidationReason에는 다음 네 가지 케이스가 있습니다.

    • actionPerformed: 팁이 설명하는 행동을 사용자가 실제로 수행함
    • displayCountExceeded: 팁이 최대 표시 횟수를 초과함
    • displayDurationExceeded: 팁이 최대 표시 기간을 초과함
    • tipClosed: 사용자가 팁이 떠 있는 동안 명시적으로 닫기 버튼을 눌러 닫음

    버튼 추가하기 — actions

    팁에 추가 정보나 옵션으로 안내하는 버튼을 넣을 수도 있어요.

    Tip의 actions 프로퍼티에 Action 인스턴스들을 정의하면 됩니다.

     

    struct PasswordResetTip: Tip {
        var title: Text {
            Text("Need Help?")
        }
    
        var message: Text? {
            Text("Do you need help logging in to your account?")
        }
    
        var image: Image? {
            Image(systemName: "lock.shield")
        }
    
        var actions: [Action] {
            Action(id: "reset-password", title: "Reset Password")
            Action(id: "faq", title: "View our FAQ")
        }
    }

     

     

    TipView에 전달한 클로저에서 action.id를 확인해서 각 버튼을 눌렀을 때의 동작을 분기할 수 있어요.

     

    TipView(passwordResetTip, arrowEdge: .bottom) { action in
        if action.id == "reset-password", let url = URL(string: "https://iforgot.apple.com") {
            openURL(url) { accepted in
                print(accepted ? "Success Reset" : "Failure")
            }
        }
        if action.id == "faq", let url = URL(string: "https://appleid.apple.com/faq") {
            openURL(url) { accepted in
                print(accepted ? "Success FAQ" : "Failure")
            }
        }
    }

     

     

    언제 보여줄지 제어하기 — Rule, Parameter, Event

    팁 구조체에 아무 rules도 정의하지 않으면, 그 팁은 닫히거나 display frequency 한계를 넘기 전까지 계속 나타나요.

     

    앱 상태 기반 — Parameter

    추적하고 싶은 앱 상태를 변수로 정의하고, Parameter 프로퍼티 래퍼로 감싼 뒤, rules 프로퍼티에서 #Rule 매크로로 조건을 정의해요.

    struct ParameterRuleTip: Tip {
        // Define the app state you want to track.
        @Parameter
        static var isLoggedIn: Bool = false
    
        var rules: [Rule] {
            // Define a rule based on the app state.
            #Rule(Self.$isLoggedIn) {
                // Set the conditions for when the tip displays.
                $0 == true
            }
        }
    }

     

    이렇게 정의해두면, 앱 상태가 바뀔 때(예: 버튼 탭으로 isLoggedIn을 토글) 팁이 그에 맞춰 나타나거나 사라져요.

    Button("Tap") {
        ParameterRuleTip.isLoggedIn.toggle()
    }

     

    사용자 행동 기반 — Event

    사용자 행동을 추적하고 싶다면, id를 가진 Event 상수를 정의하고 그 이벤트를 참조하는 #Rule을 정의해요.

     

    아래 예제는 이벤트가 3번 이상 발생하면 팁이 나타납니다.

    struct EventRuleTip: Tip {
        // Define the user interaction you want to track.
        static let didTriggerControlEvent = Event(id: "didTriggerControlEvent")
    
        var rules: [Rule] {
            #Rule(Self.didTriggerControlEvent) {
                $0.donations.count >= 3
            }
        }
    }

     

    TipKit은 이벤트가 발생한 횟수를 자동으로 디스크에 저장하고, 이 상태는 앱을 재시작해도 유지돼요.

     

    여러 조건 조합하기

    Parameter와 Event 기반 규칙을 여러 개 정의하면, rules 프로퍼티 안에서 이 규칙들은 AND로 결합돼요.

    아래 ComboTip은 로그인 상태(parameter)와 화면 진입 3회(event)를 모두 만족해야 나타납니다.

     

    struct ComboTip: Tip {
        @Parameter
        static var isLoggedIn: Bool = false
    
        static let enteredView = Event(id: "enteredView")
    
        var rules: [Rule] {
            // Note: These rules AND together.
            #Rule(Self.$isLoggedIn) {
                $0 == true
            }
            #Rule(Self.enteredView) {
                $0.donations.count >= 3
            }
        }
    }

     

    표시 빈도 제어하기 — Option

    Tip의 options 프로퍼티에 Option을 설정해서 표시 빈도를 제어할 수 있어요.

    • MaxDisplayCount: 시스템이 자동으로 팁을 무효화하기 전까지 보여줄 최대 횟수를 지정
    • MaxDisplayDuration: 팁이 무효화되기 전까지 표시되는 최대 시간을 지정
    • IgnoresDisplayFrequency: 사전에 설정된 display frequency 간격을 이 팁이 따를지 여부를 제어
    struct OptionTip: Tip {
        var options: [Option] {
            // Show this tip once.
            MaxDisplayCount(1)
        }
    }

     

    여러 팁을 순서대로 — TipGroup

    TipGroup은 여러 팁을 특정 순서로 보여주거나, 표시 가능한 첫 번째 팁만 보여줄 수 있는 클래스예요.

     

    Priority에는 두 가지 케이스가 있어요.

    • firstAvailable(기본값): 표시 가능한 첫 번째 팁을 보여줌
    • ordered: 이전 팁들이 모두 invalidated 상태가 됐을 때, 표시 가능한 팁을 보여줌
    init(_ priority: TipGroup.Priority = .firstAvailable, @Tips.GroupBuilder _ builder: () -> [any Tip])

     

    currentTip 프로퍼티로 현재 표시 가능한 팁을 가져올 수 있어요.

     

    팁 모양 커스터마이징 — TipViewStyle

    기본적으로 TipView는 MiniTipViewStyle(TipViewStyle.miniTip)로 그려져요.

    뷰 계층 전체에 커스텀 스타일을 적용하려면 TipViewStyle 프로토콜을 채택한 타입을 만들고, .tipViewStyle(_:) 모디파이어로 적용하면 됩니다.

     

    TipViewStyle의 유일한 요구사항은 makeBody(configuration:)예요.

    여기서 받는 TipViewStyleConfiguration에는 image, title, message, actions 프로퍼티가 있어서, 이를 조합해 원하는 모습으로 그릴 수 있어요.

     

    struct HeadlineTipViewStyle: TipViewStyle {
        func makeBody(configuration: TipViewStyleConfiguration) -> some View {
            VStack(alignment: .leading) {
                configuration.image?
                configuration.title?.font(.headline)
                configuration.message?.font(.subheadline)
    
                ForEach(configuration.actions) { action in
                    Button(action.title) { action.handler() }
                }
            }
        }
    }
    .tipViewStyle(HeadlineTipViewStyle())

     

    테스트하기 위한 API

    팁이 더 이상 표시 자격이 없어지면 앱에서 사라지는데, 이건 개발 중 팁의 배치와 모양을 테스트하기 어렵게 만들어요.

     

    TipKit은 이를 위한 테스트 전용 API를 제공합니다.

     

    // Show all defined tips in the app.
    Tips.showAllTipsForTesting()
    
    // Show some tips, but not all.
    Tips.showTipsForTesting([tip1, tip2, tip3])
    
    // Hide all tips defined in the app.
    Tips.hideAllTipsForTesting()
    
    // Purge all TipKit-related data.
    try Tips.resetDatastore()

     

    Conclusion

    TipKit은 Tip 프로토콜로 내용을 정의하고, TipView/popoverTip으로 보여주는 단순한 구조 위에, Parameter·Event 기반 Rule과 Option으로 "언제, 얼마나 자주" 보여줄지를 세밀하게 제어할 수 있는 프레임워크예요.

    직접 상태를 들고 조건문으로 온보딩 UI를 관리하던 걸, 선언적인 규칙으로 옮길 수 있게 됐다는 점이 핵심인 것 같습니다 🙌

     

    References

     

    TipKit | Apple Developer Documentation

    Display tips that help people discover features in your app.

    developer.apple.com

     

    Highlighting app features with TipKit | Apple Developer Documentation

    Bring attention to new features in your app by using tips.

    developer.apple.com

     

    Make features discoverable with TipKit - WWDC23 - Videos - Apple Developer

    Teach people how to use your app with TipKit! Learn how you can create effective educational moments through tips. We'll share how you...

    developer.apple.com

     

    Customize feature discovery with TipKit - WWDC24 - Videos - Apple Developer

    Focused on feature discovery, the TipKit framework makes it easy to display tips in your app. Now you can group tips so features are...

    developer.apple.com

    'SwiftUI' 카테고리의 다른 글

    SwiftUI - Canvas  (4) 2026.09.12
    What's new in SwiftUI (feat. WWDC 2026)  (1) 2026.06.29
    SwiftUI View hierarchy Debugging  (1) 2025.12.13
    SwiftUI Preview - Thunk  (1) 2025.11.29
    SwiftUI's diffing  (5) 2025.09.27
Designed by Tistory.