스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
TipKit으로 기능 노출하기
TipKit으로 사용자에게 앱 사용법을 알려주세요! 팁을 통해 효과적으로 유용한 정보를 전달하는 방법을 알아봅니다. 또한 자격 규칙을 설정해 적합한 대상에게 팁을 전달하는 법, 팁 표시 빈도를 제어하는 법과 성공적인 상호 작용을 위한 테스트 전략을 공유합니다.
챕터
- 0:00 - Intro
- 1:27 - Create a tip
- 5:23 - Eligibility rules
- 9:19 - Display and dismissal
- 12:35 - Test tips
- 13:56 - Wrap-up
리소스
관련 비디오
WWDC24
-
다운로드
♪ ♪
안녕하세요, 엘리 가토지입니다 Apple의 교육 디자인 매니저죠 TipKit으로 기능을 노출하는 방법을 알아볼게요 오늘 다룰 내용은 첫 번째 팁을 만드는 법과 규칙을 추가해 팁을 보여 줄 대상을 정하는 법 팁이 나타나고 사라지는 시간을 관리하여 앱 내 교육 메시지의 표시 빈도를 제어하는 법 그리고 팁을 테스트하는 법입니다 시작하기 전에 TipKit을 간단히 소개하죠
사용자가 좋아할 만한 기능을 만들었다면 우선 그 기능을 찾을 수 있게 해 줘야겠죠 TipKit은 앱에서 팁을 표시하는 새로운 프레임워크입니다
애플의 사용자 교육을 담당하는 교육 제품 팀은 교육을 염두에 두고 TipKit을 디자인했습니다 예를 들어 TipKit은 사용자에게 새 기능을 가르치거나 숨겨진 기능을 발견하도록 돕거나 작업을 더 빠르게 마치는 방법을 알려 주죠 사용 가능한 기기는 iPhone과 iPad Mac, Apple Watch Apple TV입니다 이제 팁을 만들어 보겠습니다 Backyard Birds 앱에는 뒤뜰 즐겨찾기 기능이 있습니다 즐겨찾기를 사용하면 가장 좋아하는 뒤뜰로 쉽게 이동할 수 있죠 팁을 만들기에 적합한 기능입니다 팁을 적용할 기능을 정했으니 Xcode로 넘어가겠습니다 뒤뜰 즐겨찾기 기능에 새로운 팁을 정의해 보죠 팁은 제목과 메시지로 구성되니 이 두 가지를 추가합니다 유용한 팁은 제목으로 직접적인 동작 어구를 사용해 어떤 기능인지 알려 줍니다 메시지에는 기억하기 쉬운 필요 정보나 안내가 담겨 있어 사용자가 기능의 쓰임새를 알고 나중에 스스로 작업을 수행할 수 있죠
효과적인 팁의 몇 가지 예시입니다 세 가지 모두 실행 가능하고 지침을 제공하며 기억하기 쉽죠 다음은 TipKit 사용이 부적절한 경우입니다 첫 번째는 홍보성 메시지 두 번째는 오류 메시지이므로 둘 다 교육적인 내용이 아닙니다 세 번째는 기능 개선을 알리는 메시지인데 사용자가 취할 행동이 없죠 마지막은 유용하지만 너무 복잡해서 기억하기 어렵고요
제 팁으로 돌아와서 제목과 메시지는 이미 정했으니 팁 뷰의 기본 구조를 입력하고 TipsCenter를 구성하겠습니다 TipsCenter는 TipKit의 주요 기능을 활성화합니다 예를 들어 팁 및 관련 이벤트가 앱 실행 간에 지속되게 하고 쉽게 팁을 테스트할 수 있죠 TipsCenter는 기본 공유 인스턴스를 제공합니다 그 인스턴스를 추가해 보죠
TipsCenter를 구성하면 팁을 볼 수 있습니다 나쁘지 않지만 더 개선해 보죠 앱과 어울리게 텍스트 색상을 남색으로 바꾸고 아이콘을 추가해 주의를 끌고 팁과 기능을 시각적으로 연결하겠습니다 즐겨찾기 관련 팁이므로 별 모양을 선택했습니다 기능의 설정을 사용자화할 수 있는 경우에는 동작 버튼을 추가해 보세요 동작 버튼을 쓰면 해당 설정으로 바로 이동하여 빠르게 설정을 조정할 수 있죠 또는 사용자에게 특히 유용한 정보가 더 있다면 온보딩 절차 등의 추가 리소스를 링크하는 방법도 있습니다 좋아요, 교육용 팁이 멋지게 완성됐습니다 이제 이 팁을 잘 처리하고 배치하는 방법을 알아보죠 팁 뷰의 유형은 두 가지입니다 하나는 팝오버 뷰로 팁이 앱 UI 위에 표시됩니다 버튼 및 기타 요소를 직접 가리킬 수 있으며 현재 앱 화면을 바꾸지 않으면서 사용자에게 지시할 때 특히 유용합니다 tvOS에서는 팝오버 뷰가 단독으로 사용되죠 두 번째는 인라인 뷰입니다 주변에 맞게 앱 UI를 일시적으로 조정하여 UI가 가려지지 않죠 둘 중 어떤 뷰에서든 팁은 관련 버튼 및 요소와 가까운 곳에 나타나는 게 좋습니다 뒤뜰 즐겨찾기에는 팝오버 뷰를 사용하죠 오른쪽 상단의 별 버튼을 가리키도록 배치합니다 앱의 첫 번째 팁이 완성됐습니다 보기에도 좋고 상황에도 잘 맞죠 하지만 적합한 대상에게 적시에 도달하는 TipKit의 기능을 최대한 활용하려면 몇 가지 규칙을 추가해야 합니다 지금부터 팁을 향상하는 방법을 찰리가 알려 드리겠습니다 고마워요, 엘리 저는 찰리 파크스입니다 교육 제품 팀의 소프트웨어 엔지니어링 매니저죠 새로운 기능을 발견하면 놀랍고 기쁜 마음이 듭니다 하지만 그러려면 사용자가 관심 있는 기능이고 기능에 대한 안내가 성가시거나 뜬금없게 느껴지지 않아야 하죠 뒤뜰 즐겨찾기 팁은 유용하지만 모두를 위한 팁은 아닙니다 사용자가 이미 그 기능을 알 수도 있죠 또한 앱을 자주 사용하지 않는 사용자는 관심이 없을 수 있습니다 Apple은 앱 내 교육의 대상이 그 기능이 필요한 사용자여야 한다고 생각합니다 또한 사용자가 앱에서 뭔가를 이루는 걸 방해하지 않아야 하죠 적시에 관련성 높은 대상에게만 팁을 나타내기 위해 TipKit은 단독 및 공동으로 사용 가능한 자격 규칙을 제공하여 팁이 표시되는 정확한 시점을 결정합니다 규칙 유형은 크게 두 가지이며 하나는 매개변수 기반 규칙입니다 매개변수 기반 규칙은 영구적이며 표현식을 작성할 Swift 값 유형에 기반한 팁 표시에 가장 적합합니다 두 번째는 이벤트 기반 규칙입니다 이벤트 기반 규칙을 쓰면 사용자가 어떤 작업을 수행해야 팁을 볼 자격이 생기는지 정의할 수 있습니다
뒤뜰 즐겨찾기 팁의 경우 가장 먼저 사용자가 계정에 로그인했는지 확인해야 합니다 이를 위해 매개변수 기반 규칙을 구현하겠습니다 우선 매개변수의 초깃값을 false로 설정합니다 그런 다음 팁의 규칙에 이 값을 추가합니다 좋아요, 팁을 표시할 대상의 범위를 좁혔지만 여기서 더 좁혀 보겠습니다 저는 팁을 제공하기 전에 사용자가 자연스럽게 기능을 발견하길 바랍니다 따라서 이벤트 기반 규칙을 생성하여 사용자가 뒤뜰 상세 뷰를 세 번 이상 확인한 경우에만 팁을 표시하도록 하겠습니다
먼저 이벤트를 생성합니다 규칙이 true로 평가되기 전에 이벤트가 트리거되는 횟수를 계산하도록 설정하겠습니다 이 경우에는 사용자가 뒤뜰 상세 뷰를 세 번 이상 확인한 뒤에 팁을 표시하죠 마지막으로 이벤트를 기부해야 합니다 BackyardDetailView에서 뷰가 표시될 때마다 이벤트를 기부하겠습니다 규칙이 완성되고 있군요 어떻게 더 다듬을 수 있을까요? 지금까지는 뒤뜰 상세 뷰를 자주 방문하지만 즐겨찾기를 설정하지 않은 사용자에게 초점을 맞췄는데요 이번에는 앱을 꾸준히 사용하는 사용자에게만 팁을 표시해 보겠습니다
이벤트 기반 규칙에 날짜 쿼리 수정자를 추가해 뒤뜰 상세 뷰를 지난 5일 동안 세 번 방문한 경우에만 true로 평가하겠습니다 TipKit의 또 다른 강력한 기능은 사용자 지정 기부를 만드는 건데요 이를 위해 이벤트를 기부할 때마다 관련 유형을 추가하고 해당 유형을 기반으로 이벤트를 쿼리합니다 관련 유형을 사용하면 이벤트 기반 규칙을 강화하여 특정 뒤뜰 상세 뷰를 방문한 경우에만 일치시킬 수 있습니다
DetailViewDonation을 만들고 특정 뒤뜰 뷰의 ID를 제공합니다 그런 다음 기부에서 사용자가 현재 위치한 뒤뜰 뷰의 ID를 포함합니다 두 가지가 모두 설정되면 고유한 뒤뜰 ID를 기반으로 이벤트를 쿼리하도록 규칙을 업데이트합니다 사용자 지정 기부를 정의할 때는 저장되는 데이터의 크기를 고려해야 합니다 크기가 클수록 쿼리 실행 속도가 느려지고 성능이 저하되죠 TipKit의 규칙은 작성이 쉬우며 가장 필요한 사용자에게 팁을 표시하는 유용하고 간단한 방법입니다 규칙은 필요에 따라 포괄적이거나 구체적일 수 있고 적합한 사용자를 적시에 공략하기 위해 조합되기도 하죠 앱에 나타난 팁이 계속 남아 있을 필요는 없습니다 사용자가 그 기능을 사용해 봤다면 팁이 금방 사라지는 게 좋죠 또한 팁 여러 개를 한 번에 표시하지 않는 게 좋습니다 너무 부담스럽고 사용자의 작업을 방해할 수 있으니까요 TipKit에는 여러 가지 표시 및 닫기 동작이 있어 유용하고 적절한 때만 팁을 나타낼 수 있습니다 팁을 몇 가지 더 추가해서 Backyard Birds 앱에 팁이 다섯 개 있다고 해 보죠 유용한 팁이어도 한꺼번에 뜨면 실효성이 떨어지는데요 TipKit으로 팁의 표시 빈도를 설정하면 적절한 간격으로 팁을 나타낼 수 있습니다
TipsCenter에서 시간이 얼마나 지난 후에 다른 팁을 표시할지 정할 수 있습니다 24시간마다 팁을 표시하려면 .daily를 쓰고 60분마다 표시하려면 .hourly를 씁니다 유효한 TimeInterval 값을 제공해 간격을 마음대로 정할 수도 있죠 교육이 즉시 필요하다고 생각하는 경우에는 .immediate 수정자를 쓰면 됩니다 그러면 사용자가 볼 자격이 있는 팁이 자격이 있는 순간에 나타나죠 다른 팁이 최근에 나타났거나 화면에 표시돼 있어도요 TipsCenter 수준에서 표시 빈도를 무시하는 것보다 팁별로 한 번씩 무시하는 게 더 유용할 수 있습니다 그러려면 .ignoresDisplayFrequency 옵션을 특정 팁에 추가하면 되죠 사용자에게 자격이 생기는 순간 해당 팁만 나타나고 나머지 팁은 계속해서 TipsCenter 수준에서 설정한 간격을 따릅니다 팁이 유용하게 쓰일 동안만 화면에 표시되는 게 좋습니다 팁에 설명된 기능을 사용자가 사용해서 작업을 수행했거나 팁을 볼 자격이 있지만 관심이 없는 경우에는 팁이 사라져야 하죠 팁을 표시하기 위한 모든 규칙을 사용자가 충족했다고 해 봅시다 로그인 상태이고 뒤뜰 상세 뷰를 지난 5일 동안 세 번 열었으며 뒤뜰에 즐겨찾기 버튼을 누른 적이 없는 거죠 사용자가 팁을 보고 즐겨찾기 버튼을 누르면 사유 .userPerformedAction과 함께 무효화 메서드를 호출하여 원하는 결과가 수행됐음을 나타냅니다 그러면 팁이 사라지죠 팁을 닫는 또 다른 방법은 정의된 .maxDisplayCount보다 많이 표시하는 겁니다 이 경우 팁을 다섯 번 표시하고도 아무런 동작이 없다면 다음번에 사용자가 뒤뜰 상세 뷰에 들어가도 팁을 표시하지 않죠 TipKit에 내장된 기능으로 팁을 사라지게 하는 방법을 알아봤습니다 앱을 가장 잘 아는 건 여러분이므로 적합한 상호작용이나 기준에 따라 팁을 사라지게 해 보세요 .invalidate() 메서드를 쓰면 되죠 또한 TipKit은 iCloud로 팁 상태를 동기화하므로 한 기기에서 나타난 팁이 다른 기기에서 나타나지 않습니다 예를 들어 사용자가 iPad와 iPhone 모두에 앱을 설치했고 양 기기에서 기능이 동일하다면 양쪽 모두에서 기능에 대해 교육하지 않는 게 좋죠 이제 엘리가 TipKit의 테스트 API 및 다음 단계를 설명하겠습니다 TipKit은 손쉬운 테스트를 위해 편리한 API를 제공합니다 이 API로 자격 규칙을 만들어서 필요에 따라 팁을 표시하거나 숨길 수 있죠 설정된 규칙을 충족하지 않아도 앱의 모든 팁을 검사할 수 있습니다 TipsCenter 구성에 .showAllTips를 추가하세요 테스트하는 동안 특정 팁만 표시하려면 .showTips를 사용하고 특정 팁 ID를 전달하세요 또는 .hideTips를 사용해 특정 팁이 나타나지 않게 하세요 앱의 다른 기능에 집중할 수 있도록 팁을 표시하지 않으려면 .hideAllTips를 씁니다 또한 .resetDatastore를 사용해 TipKit 데이터 스토어의 모든 정보를 제거하고 앱의 각 빌드에 새것 같은 상태를 설정할 수 있죠 API를 통해 호출할 수 있는 모든 테스트 옵션은 실행 인수로도 쓸 수 있으며 프로젝트 스킴에 추가 가능합니다 옵션 중 하나를 사용해 부분 점검을 하거나 전체 기능을 테스트할 수 있죠 이제 모든 준비가 끝났습니다 팁을 만들고 규칙을 추가하고 팁의 표시 빈도를 설정했으며 모든 게 예상대로 작동하는지 테스트했죠 요약하자면 팁은 사용자가 앱의 기능을 발견하게 도와줍니다 팁은 짧고 지시적이며 실행 가능해야 함을 잊지 마세요 또한 쉽고 강력한 규칙으로 적합한 대상을 정해야 합니다 TipKit의 샘플 코드는 developer.apple.com에서 사용 가능합니다 TipKit 휴먼 인터페이스 가이드라인에서 멋진 팁을 만들기 위한 추가 정보를 살펴보세요 이제 여러분 앱의 발견성을 검토해 보시고 새 기능을 개발할 때 TipKit을 활용해 보세요 교육 제품 팀을 대표하여 시청해 주셔서 감사합니다
-
-
1:55 - Create a tip
struct FavoriteBackyardTip: Tip { var title: Text { Text("Save as a Favorite") } var message: Text { Text("Your favorite backyards always appear at the top of the list.") } }
-
2:48 - Configure TipsCenter
@main struct BackyardBirdsApp: App { var body: some Scene { WindowGroup { ContentView() } } // ... init() { TipsCenter.shared.configure() } }
-
3:18 - Add actions and an asset to a tip
struct FavoriteBackyardTip: Tip { var title: Text { Text("Save as a Favorite").foregroundColor(.indigo) } var message: Text { Text("Your favorite backyards always appear at the top of the list.") } var asset: Image { Image(systemName: "star") } var actions: [Action] { [ Tip.Action( id: "learn-more", title: "Learn More" ) ] } }
-
4:53 - Create a popover view
private let favoriteBackyardTip = FavoriteBackyardTip() // ... .toolbar { ToolbarItem { Button { backyard.isFavorite.toggle() } label: { Label("Favorite", systemImage: "star") .symbolVariant( backyard.isFavorite ? .fill : .none ) } .popoverMiniTip(tip: favoriteBackyardTip) } }
-
6:38 - Add a parameter based rule
struct FavoriteBackyardTip: Tip { @Parameter static var isLoggedIn: Bool = false // ... var rules: Predicate<RuleInput...> { // User is logged in #Rule(Self.$isLoggedIn) { $0 == true } } }
-
7:16 - Add an event based rule
struct FavoriteBackyardTip: Tip { @Parameter static var isLoggedIn: Bool = false static let enteredBackyardDetailView: Event = Event<DetailViewDonation>( id: "entered-backyard-detail-view" ) // ... var rules: Predicate<RuleInput...> { // User is logged in #Rule(Self.$isLoggedIn) { $0 == true } // User has entered any backyard detail view at least 3 times #Rule(Self.enteredBackyardDetailView) { $0.count >= 3 } } }
-
7:34 - Donate the event when a view appears
.onAppear { FavoriteBackyardTip.enteredBackyardDetailView.donate() }
-
7:59 - Filter event donations in an event based rule
// User has entered any backyard detail view at least 3 times in the past 5 days #Rule(Self.enteredBackyardDetailView) { $0.donations.filter { $0.date > Date.now.addingTimeInterval(-5 * 60 * 60 * 24) } .count >= 3 }
-
8:34 - Create a custom donation
// Create the associated type extension BackyardDetailTip { struct DetailViewDonation: DonationValue { let backyardID: Int } } // Donate the unique id of the backyard detail being viewed .onAppear { BackyardFavoriteTip.enteredBackyardDetailView.donate( with: .init(backyardID: backyard.id) ) } struct FavoriteBackyardTip: Tip { // ... var rules: Predicate<RuleInput...> { // Update the rule to specify a backyardID #Rule(Self.enteredBackyardDetailView) { $0.donations.filter { $0.date > Date.now.addingTimeInterval(-5 * 60 * 60 * 24) } .largestSubset(by: \.backyardID) .count >= 3 } } }
-
9:57 - Configure display frequency
// One tip per day. TipsCenter.shared.configure { DisplayFrequency(.daily) } // One tip per hour. TipsCenter.shared.configure { DisplayFrequency(.hourly) } // Custom configuration. Only show one tip every five days. let fiveDays: TimeInterval = 5 * 24 * 60 * 60 TipsCenter.shared.configure { DisplayFrequency(fiveDays) } // No frequency control. Show all tips as soon as eligible. TipsCenter.shared.configure { DisplayFrequency(.immediate) }
-
10:34 - Turn off display frequency controls for a tip
struct FavoriteBackyardTip: Tip { // ... var options: [Option] { [.ignoresDisplayFrequency(true)] } }
-
11:27 - Invalidate a tip
Button { backyard.isFavorite.toggle() // When user taps the favorite button, dismiss the tip favoriteBackyardTip.invalidate(reason: .userPerformedAction) } label: { Label("Favorite", systemImage: "star") .symbolVariant(backyard.isFavorite ? .fill : .none) } .popoverMiniTip(tip: favoriteBackyardTip)
-
11:41 - Configure max display count on a tip
struct FavoriteBackyardTip: Tip { // ... var options: [Option] { [.maxDisplayCount(5)] } }
-
12:46 - Programmatically call testing API
// Show all defined tips in the app TipsCenter.showAllTips() // Show some tips, but not all TipsCenter.showTips([tip1, tip2, tip3]) // Hide some tips, but not all TipsCenter.hideTips([tip1, tip2, tip3]) // Hide all tips defined in the app TipsCenter.hideAllTips() // Purge all TipKit related data TipsCenter.resetDatastore()
-
13:31 - Configure launch arguments in your scheme
// Show all defined tips in the app com.apple.TipKit.ShowAllTips 1 // Show some tips, but not all com.apple.TipKit.ShowTips tipID,otherTipID // Hide some tips, but not all com.apple.TipKit.HideAllTips 1 // Hide all tips defined in the app com.apple.TipKit.HideTips tipID,otherTipID // Purge all TipKit related data com.apple.TipKit.ResetDatastore 1
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.