CINXE.COM
Make features discoverable with TipKit - WWDC23 - Videos - Apple Developer
<!DOCTYPE html> <html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta charset="utf-8" /> <meta name="Author" content="Apple Inc." /> <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" /> <link rel="shortcut icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" /> <link rel="mask-icon" href="/apple-logo.svg" color="#333333"> <link rel="stylesheet" href="/assets/styles/global.dist.css?01172423298" type="text/css" /> <link rel="stylesheet" href="/assets/styles/dark-mode.css?17182448067" type="text/css" media="(prefers-color-scheme: dark)" data-color-scheme="dark" /> <link rel="stylesheet" href="/assets/styles/localization.css?39172414191" type="text/css" /> <script src="/assets/scripts/lib/jquery/jquery-3.6.0.min.js?17182448067"></script> <script src="/assets/scripts/settings.js?17182448067"></script> <script src="/assets/scripts/language-locales.js?17182448067"></script> <script src="/assets/scripts/DeveloperBreadcrumbs.js?05182434080"></script> <script async src="/assets/scripts/lib/jquery/jquery.retinate.js?17182448067"></script> <script async src="/assets/scripts/global.js?17182448067"></script> <script async src="/assets/scripts/global-logout.js?17182448067"></script> <link rel="stylesheet" href="https://www.apple.com/wss/fonts?family=SF+Pro&v=2" type="text/css" /> <link rel="stylesheet" href="https://www.apple.com/wss/fonts?family=SF+Pro+Icons&v=1" type="text/css" /> <link rel="stylesheet" href="https://www.apple.com/wss/fonts?family=SF+Mono&v=2" type="text/css" /> <link rel="stylesheet" href="https://www.apple.com/wss/fonts?family=Apple+Icons&v=1" type="text/css" /> <title>Make features discoverable with TipKit - WWDC23 - Videos - Apple Developer</title> <meta name="omni_page" content="Make features discoverable with TipKit - WWDC23 - Videos" /> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <link type="text/css" rel="stylesheet" href="/videos/styles/videos.css?2"> <link type="text/css" rel="stylesheet" href="/videos/styles/video-overlay.css?2"> <meta name="description" content="Teach people how to use your app with TipKit! Learn how you can create effective educational moments through tips. We'll share how you..."> <meta property="og:url" content="https://developer.apple.com/videos/play/wwdc2023/10229/" /> <meta property="og:title" content="Make features discoverable with TipKit - WWDC23 - Videos - Apple Developer" /> <meta property="og:description" content="Teach people how to use your app with TipKit! Learn how you can create effective educational moments through tips. We'll share how you..." /> <meta property="og:site_name" content="Apple Developer" /> <meta property="og:type" content="website" /> <meta property="og:video" content="https://devstreaming-cdn.apple.com/videos/wwdc/2023/10229/4/07E6CA29-01CD-4E03-A3FF-D7D8A3FB4CEF/cmaf.m3u8" /> <meta property="og:video_secure_url" content="https://devstreaming-cdn.apple.com/videos/wwdc/2023/10229/4/07E6CA29-01CD-4E03-A3FF-D7D8A3FB4CEF/cmaf.m3u8" /> <meta property="og:video:type" content="vnd.apple.mpegURL" /> <meta property="og:video:width" content="1280" /> <meta property="og:video:height" content="720" /> <meta property="og:image" content="https://devimages-cdn.apple.com/wwdc-services/images/D35E0E85-CCB6-41A1-B227-7995ECD83ED5/8293/8293_wide_250x141_2x.jpg" /> <meta property="og:locale" content="en_US" /> <meta itemprop="datePublished" content="2023-06-06"> <meta itemprop="uploadDate" content="2023-06-06"> <link rel="alternate" href="https://developer.apple.com/videos/play/wwdc2023/10229/" hreflang="en"> <link rel="alternate" href="https://developer.apple.com/kr/videos/play/wwdc2023/10229/" hreflang="ko-KR"> <link rel="alternate" href="https://developer.apple.com/cn/videos/play/wwdc2023/10229/" hreflang="zh-CN"> <link rel="alternate" href="https://developer.apple.com/jp/videos/play/wwdc2023/10229/" hreflang="ja-JP"> <link rel="canonical" href="https://developer.apple.com/videos/play/wwdc2023/10229/" /> </head> <body class="view-play theme-dark"> <script> const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; if (window.Settings.preferredColorScheme) { const colorTheme = (window.Settings.preferredColorScheme === 'auto') && !prefersDark ? 'light' : (window.Settings.preferredColorScheme === 'light') ? 'light' : 'dark' document.body.setAttribute('data-color-scheme', colorTheme); } else { document.body.setAttribute('data-color-scheme', prefersDark ? 'dark' : 'light'); } </script> <style> /* footer */ html body[data-color-scheme='dark'] #main section.section.section-resources.bg-alt {background-color:var(--fill-tertiary);color:var(--glyph-gray);} html body[data-color-scheme='dark'] .developer-router-links {background-color:var(--fill-tertiary);color:var(--glyph-gray);} html body[data-color-scheme='dark'] .footer {background-color:var(--fill-tertiary);color:var(--glyph-gray-tertiary);} /* body backgrounds */ body[data-color-scheme='dark'].dmf {background-color: #000;} html body[data-color-scheme='dark'].dmf .bg-alt {background-color:var(--fill-tertiary);} html body[data-color-scheme='dark'].dmf .bg-blue {background-color:var(--fill-blue-secondary);} html body[data-color-scheme='dark'].dmf .bg-green-blue {background:linear-gradient(135deg, #65976d 0%, #588ea4 100%)} html body[data-color-scheme='dark'].dmf .bg-yellow {background: linear-gradient(to bottom, var(--fill-tertiary) 0%, var(--fill-tertiary-alt) 100%);} html body[data-color-scheme='dark'].dmf .bg-light {background-color:var(--fill-secondary-alt);} html body[data-color-scheme='dark'].dmf .bg-gradient, html body[data-color-scheme='dark'].dmf .bg-grad {background:linear-gradient(to bottom, var(--dark) 0%, var(--fill-gray-secondary-alt) 100%);} html body[data-color-scheme='dark'].dmf .bg-grad-down {background:linear-gradient(to bottom, var(--dark) 0%, var(--fill-gray-secondary-alt) 100%);} html body[data-color-scheme='dark'].dmf .bg-grad-up {background:linear-gradient(to bottom, var(--fill-gray-secondary-alt) 0%, var(--dark) 100%);} html body[data-color-scheme='dark'].dmf .bg-blue-gradient, html body[data-color-scheme='dark'].dmf .bg-gradient-blue {background:linear-gradient(to bottom, var(--fill-blue-gradient-light) 0%, var(--fill-blue-gradient-dark) 100%);} html body[data-color-scheme='dark'].dmf .bg-blue-gradient-alt {background: linear-gradient(90deg, #061830 0%, #102d48 100%);} /* globalNav */ html body[data-color-scheme='dark'] #ac-globalnav {background: var(--fill-tertiary);} /* localNav */ html body[data-color-scheme='dark'] .localnav.localnav-scrim .localnav-background, html body[data-color-scheme='dark'] .localnav-scrim.localnav.localnav-background { background-color: rgba(29, 29, 31, 0.9); } @supports ((-webkit-backdrop-filter: initial) or (backdrop-filter: initial)) { html body[data-color-scheme='dark'] .localnav.localnav-scrim .localnav-background, html body[data-color-scheme='dark'] .localnav-scrim.localnav.localnav-background { background-color: rgba(29, 29, 31, 0.72); } } </style> <link rel="stylesheet" href="/assets/styles/globalnav.css?55202406207" type="text/css" /> <link rel="stylesheet" href="/assets/styles/suggest-lang.css?17182448067" type="text/css" /> <div id="suggest-lang" class="ribbon hide" lang="en"> <div class="ribbon-content-wrapper"> <div class="ribbon-content row"> <div class="column large-12 large-centered"> <p><a href="#" id="suggest-link" class="ribbon-link more">View in English</a></p> <button id="suggest-closer" class="icon icon-after icon-reset" aria-label="Dismiss language suggestion" tabindex="0"></button> </div> </div> </div> </div> <script src="/assets/scripts/suggest-lang.js?17182448067"></script> <aside id="ac-gn-segmentbar" class="ac-gn-segmentbar" lang="en-US" dir="ltr"> </aside> <input type="checkbox" id="ac-gn-menustate" class="ac-gn-menustate" /> <nav id="ac-globalnav" class="no-js" role="navigation" aria-label="Global" data-hires="false" data-analytics-region="global nav" lang="en-US" dir="ltr" data-www-domain="www.apple.com" data-store-locale="us" data-store-root-path="/us" data-store-api="/[storefront]/shop/bag/status" data-search-locale="en_US" data-search-suggestions-api="/search-services/suggestions/" data-search-defaultlinks-api="/search-services/suggestions/defaultlinks/" data-search-suggestions-enabled="false"> <div class="ac-gn-content"> <ul class="ac-gn-header"> <li class="ac-gn-item ac-gn-menuicon"> <label class="ac-gn-menuicon-label" for="ac-gn-menustate" aria-hidden="true"> <span class="ac-gn-menuicon-bread ac-gn-menuicon-bread-top"> <span class="ac-gn-menuicon-bread-crust ac-gn-menuicon-bread-crust-top"></span> </span> <span class="ac-gn-menuicon-bread ac-gn-menuicon-bread-bottom"> <span class="ac-gn-menuicon-bread-crust ac-gn-menuicon-bread-crust-bottom"></span> </span> </label> <a href="#ac-gn-menustate" role="button" class="ac-gn-menuanchor ac-gn-menuanchor-open" id="ac-gn-menuanchor-open"> <span class="ac-gn-menuanchor-label">Global Nav Open Menu</span> </a> <a href="#" role="button" class="ac-gn-menuanchor ac-gn-menuanchor-close" id="ac-gn-menuanchor-close"> <span class="ac-gn-menuanchor-label">Global Nav Close Menu</span> </a> </li> <li class="ac-gn-item ac-gn-apple"> <a class="ac-gn-link ac-gn-link-apple-developer" href="/" data-analytics-title="appledeveloper home" id="ac-gn-firstfocus-small"> <span class="ac-gn-link-text">Apple Developer</span> </a> </li> </ul> <div class="ac-gn-search-placeholder-container" role="search"> <div class="ac-gn-search ac-gn-search-small"> <a id="ac-gn-link-search-small" class="ac-gn-link" href="/search/" data-analytics-title="search" data-analytics-click="search" data-analytics-intrapage-link aria-label="Search"> <div class="ac-gn-search-placeholder-bar"> <div class="ac-gn-search-placeholder-input"> <div class="ac-gn-search-placeholder-input-text" aria-hidden="true"> <div class="ac-gn-link-search ac-gn-search-placeholder-input-icon"></div> <span class="ac-gn-search-placeholder">Search</span> </div> </div> <div class="ac-gn-searchview-close ac-gn-searchview-close-small ac-gn-search-placeholder-searchview-close"> <span class="ac-gn-searchview-close-cancel" aria-hidden="true">Cancel</span> </div> </div> </a> </div> </div> <ul class="ac-gn-list"> <li class="ac-gn-item ac-gn-apple"> <a class="ac-gn-link ac-gn-link-apple-developer" href="/" data-analytics-title="appledeveloper home" id="ac-gn-firstfocus"> <span class="ac-gn-link-text">Apple Developer</span> </a> </li> <li class="ac-gn-item ac-gn-item-menu ac-gn-news"> <a class="ac-gn-link ac-gn-link-news" href="/news/" data-analytics-title="news"> <span class="ac-gn-link-text">News</span> </a> </li> <li class="ac-gn-item ac-gn-item-menu ac-gn-discover"> <a class="ac-gn-link ac-gn-link-discover" href="/discover/" data-analytics-title="discover"> <span class="ac-gn-link-text">Discover</span> </a> </li> <li class="ac-gn-item ac-gn-item-menu ac-gn-design"> <a class="ac-gn-link ac-gn-link-design" href="/design/" data-analytics-title="design"> <span class="ac-gn-link-text">Design</span> </a> </li> <li class="ac-gn-item ac-gn-item-menu ac-gn-develop"> <a class="ac-gn-link ac-gn-link-develop" href="/develop/" data-analytics-title="develop"> <span class="ac-gn-link-text">Develop</span> </a> </li> <li class="ac-gn-item ac-gn-item-menu ac-gn-distribute"> <a class="ac-gn-link ac-gn-link-distribute" href="/distribute/" data-analytics-title="distribute"> <span class="ac-gn-link-text">Distribute</span> </a> </li> <li class="ac-gn-item ac-gn-item-menu ac-gn-dsupport"> <a class="ac-gn-link ac-gn-link-dsupport" href="/support/" data-analytics-title="dsupport"> <span class="ac-gn-link-text">Support</span> </a> </li> <li class="ac-gn-item ac-gn-item-menu ac-gn-account"> <a class="ac-gn-link ac-gn-link-account" href="/account/" data-analytics-title="account"> <span class="ac-gn-link-text">Account</span> </a> </li> <li class="ac-gn-item ac-gn-item-menu ac-gn-search" role="search"> <a id="ac-gn-link-search" class="ac-gn-link ac-gn-link-search" href="/search/" data-analytics-title="search" data-analytics-click="search" data-analytics-intrapage-link aria-label="Search"></a> </li> </ul> <aside id="ac-gn-searchview" class="ac-gn-searchview" role="search" data-analytics-region="search"> <div class="ac-gn-searchview-content"> <div class="ac-gn-searchview-bar"> <div class="ac-gn-searchview-bar-wrapper"> <form id="ac-gn-searchform" class="ac-gn-searchform" action="/search/" method="get"> <div class="ac-gn-searchform-wrapper"> <input id="ac-gn-searchform-input" class="ac-gn-searchform-input" type="text" name="q" aria-label="Search" placeholder="Search" autocorrect="off" autocapitalize="off" autocomplete="off" spellcheck="false" role="combobox" aria-autocomplete="list" aria-expanded="true" aria-owns="quicklinks suggestions" /> <button id="ac-gn-searchform-submit" class="ac-gn-searchform-submit" type="submit" disabled aria-label="Submit Search"></button> <button id="ac-gn-searchform-reset" class="ac-gn-searchform-reset" type="reset" disabled aria-label="Clear Search"> <span class="ac-gn-searchform-reset-background"></span> </button> </div> </form> <button id="ac-gn-searchview-close-small" class="ac-gn-searchview-close ac-gn-searchview-close-small" aria-label="Cancel Search"> <span class="ac-gn-searchview-close-cancel" aria-hidden="true"> Cancel </span> </button> </div> </div> <aside id="ac-gn-searchresults" class="ac-gn-searchresults hidden" data-string-quicklinks="Quick Links" data-string-suggestions="Suggested Searches" data-string-noresults=""> <section class="ac-gn-searchresults-section ac-gn-searchresults-section-defaultlinks"> <div class="ac-gn-searchresults-section-wrapper"> <div class="search-group-checkbox hidden"><input id="group-input" type="checkbox" name="group-filter" checked>Only search within “<span id="group-search-label"></span>”</div> <h3 class="ac-gn-searchresults-header ac-gn-searchresults-animated">Quick Links</h3> <ul class="ac-gn-searchresults-list" id="defaultlinks" role="listbox"> </ul> <span role="status" class="ac-gn-searchresults-count" aria-live="polite">5 Quick Links</span> </div> </section> </aside> </div> <button id="ac-gn-searchview-close" class="ac-gn-searchview-close" aria-label="Cancel Search"> <span class="ac-gn-searchview-close-wrapper"> <span class="ac-gn-searchview-close-left"></span> <span class="ac-gn-searchview-close-right"></span> </span> </button> </aside> </div> </nav> <div class="ac-gn-blur"></div> <div id="ac-gn-curtain" class="ac-gn-curtain"></div> <div id="ac-gn-placeholder" class="ac-nav-placeholder"></div> <script src="/assets/scripts/ac-globalnav.built.js?17182448067"></script> <link rel="stylesheet" href="/assets/styles/search.css?17182448067"> <script src="/assets/scripts/search.js?17182448067"></script> <!-- metrics --> <script> /* RSID: */ var s_account="awdappledeveloper" </script> <script src="/assets/metrics/scripts/analytics.js?072620243"></script> <script> s.pageName= AC && AC.Tracking && AC.Tracking.pageName(); s.channel="www.en.developer" s.channel="www.videos.developer"; /************* DO NOT ALTER ANYTHING BELOW THIS LINE ! **************/ var s_code=s.t();if(s_code)document.write(s_code) </script> <!-- /metrics --> <link rel="stylesheet" property="stylesheet" href="/assets/styles/localnav.css" type="text/css" /> <input type="checkbox" id="localnav-menustate" class="localnav-menustate"/> <nav id="localnav" class="localnav localnav-scrim theme-dark" data-sticky role="navigation"> <div class="localnav-wrapper"> <div class="localnav-background"></div> <div class="localnav-content"> <h2 class="localnav-title"> <a href="/videos/">Videos</a> </h2> <div class="localnav-menu"> <a href="#localnav-menustate" class="localnav-menucta-anchor localnav-menucta-anchor-open" id="localnav-menustate-open"> <span class="localnav-menucta-anchor-label">Open Menu</span> </a> <a href="#" class="localnav-menucta-anchor localnav-menucta-anchor-close" id="localnav-menustate-close"> <span class="localnav-menucta-anchor-label">Close Menu</span> </a> <div class="localnav-menu-tray"> <ul class="localnav-menu-items"> <li class="localnav-menu-item"> <a href="/videos/" class="localnav-menu-link">Collection</a> </li> <li class="localnav-menu-item"> <a href="/videos/topics/" class="localnav-menu-link">Topics</a> </li> <li class="localnav-menu-item"> <a href="/videos/all-videos/" class="localnav-menu-link">All Videos</a> </li> <li class="localnav-menu-item"> <a href="/videos/about/" class="localnav-menu-link">About</a> </li> </ul> </div> <div class="localnav-actions localnav-actions-center"> <div class="localnav-action localnav-action-menucta" aria-hidden="true"> <label for="localnav-menustate" class="localnav-menucta"> <span class="localnav-menucta-chevron"></span> </label> </div> </div> </div> </div> </div> </nav> <label id="localnav-curtain" for="localnav-menustate"></label> <script src="/assets/scripts/ac-localnav.built.js"></script> <script type="text/javascript" src="/assets/scripts/localnav.js"></script> <main id="main" class="main theme-dark" role="main"> <section class="bg-dark"> <section class="grid"> <section class="row"> <section class="column large-10 small-12 padding-top-small large-centered no-padding-bottom"> <!-- back link --> <p class="back-link smaller"> <a class="icon icon-before icon-chevronleft" href="/videos/">More Videos</a> </p> </section> </section> <!-- video player --> <section class="video-wrapper row"> <section class="column large-10 small-12 padding-top-small padding-bottom-small large-centered"> <div class="developer-video-player"> <div id="video-status-overlay" class="developer-video-overlay" style="display: none"> <div class="video-status-content"> <div id="video-status-text"></div> <img class="video-status-icon" id="icon-backward" src="/assets/elements/icons/symbols/gobackward5.svg" style="display: none" /> <img class="video-status-icon" id="icon-forward" src="/assets/elements/icons/symbols/goforward5.svg" style="display: none" /> </div> </div> <video id="video" class="video large-centered" controls="" src="https://devstreaming-cdn.apple.com/videos/wwdc/2023/10229/4/07E6CA29-01CD-4E03-A3FF-D7D8A3FB4CEF/cmaf.m3u8" data-id="wwdc2023-10229" data-locale="en"></video> </div> <div class="no-video-banner hidden"> <div class="no-video-copy"> <p>Streaming is available in most browsers,<br> and in the Developer app.</p> </div> </span> </div> </section> </section> </section> </section> <section class="grid"> <!-- tab menu --> <section class="tab-menu row divider-bottom"> <section class="column large-10 small-12 padding-top-small padding-bottom-small large-centered"> <ul class="tabs"> <li class="tab" data-supplement-id="details"><a href="#"><span class="smaller">Overview</span></a></li> <li class="tab" data-supplement-id="transcript"><a href="#"><span class="smaller">Transcript</span></a></li> <li class="tab right active" data-supplement-id="search"> <!-- mobile search --> <a href="#"><span><!--icon--></span></a> <!-- desktop search --> <section class="searchbar-wrapper desktop" style="opacity:0;"> <button class="search-icon"></button> <input id="searchDesktop" class="active" type="text" autocomplete="off" placeholder="Search this video…"> </section> </li> <li class="tab" data-supplement-id="sample-code"><a href="#"><span class="smaller">Code</span></a></li> <section class="searchbar-wrapper mobile"> <input class="active" type="text" autocomplete="off" placeholder="Search this video…"> </section> </ul> </section> </section> <!-- supplements --> <section class="row"> <section class="column large-10 small-12 large-centered no-padding-top no-padding-bottom"> <ul class="supplements hidden"> <!-- details --> <li class="supplement details " data-supplement-id="details"> <h1>Make features discoverable with TipKit</h1> <p>Teach people how to use your app with TipKit! Learn how you can create effective educational moments through tips. We'll share how you can build eligibility rules to reach the ideal audience, control tip frequency, and strategies for testing to ensure successful interactions.</p> <h2>Chapters</h2> <ul class="no-bullet"> <li>0:00 - <a class="jump-to-time" href="/videos/play/wwdc2023/10229/?time=0" data-start-time="0" data-chapter-end-time="86" data-chapter-lenght="86" data-chapter-index="1">Intro</a></li> <li>1:27 - <a class="jump-to-time" href="/videos/play/wwdc2023/10229/?time=87" data-start-time="87" data-chapter-end-time="322" data-chapter-lenght="235" data-chapter-index="2">Create a tip</a></li> <li>5:23 - <a class="jump-to-time" href="/videos/play/wwdc2023/10229/?time=323" data-start-time="323" data-chapter-end-time="558" data-chapter-lenght="235" data-chapter-index="3">Eligibility rules</a></li> <li>9:19 - <a class="jump-to-time" href="/videos/play/wwdc2023/10229/?time=559" data-start-time="559" data-chapter-end-time="754" data-chapter-lenght="195" data-chapter-index="4">Display and dismissal</a></li> <li>12:35 - <a class="jump-to-time" href="/videos/play/wwdc2023/10229/?time=755" data-start-time="755" data-chapter-end-time="835" data-chapter-lenght="80" data-chapter-index="5">Test tips</a></li> <li>13:56 - <a class="jump-to-time" href="/videos/play/wwdc2023/10229/?time=836" data-start-time="836" data-chapter-end-time="885" data-chapter-lenght="49" data-chapter-index="6">Wrap-up</a></li> </ul> <h2>Resources</h2> <ul class="links small"> <li class="download"> <ul class="options"> <li><a href="https://devstreaming-cdn.apple.com/videos/wwdc/2023/10229/4/07E6CA29-01CD-4E03-A3FF-D7D8A3FB4CEF/downloads/wwdc2023-10229_hd.mp4?dl=1">HD Video</a></li> <li><a href="https://devstreaming-cdn.apple.com/videos/wwdc/2023/10229/4/07E6CA29-01CD-4E03-A3FF-D7D8A3FB4CEF/downloads/wwdc2023-10229_sd.mp4?dl=1">SD Video</a></li> </ul> </li> </ul> <div class="divider-top margin-top-small padding-bottom-small"></div> <h2>Related Videos</h2> <h4>WWDC24</h4> <ul class="links small"> <li class="video"> <a href="/videos/play/wwdc2024/10070">Customize feature discovery with TipKit</a> </li> </ul> </li> <!-- transcript --> <li class="supplement transcript" data-supplement-id="transcript" data-shortcut-base-url="/videos/play/wwdc2023/10229/"> <section> <div class="margin-bottom-small download-transcript"><span id="get-transcript" class="icon icon-before icon-downloadcircle">Download</span></div> <p><span class="sentence"><span data-start="0.0">♪ ♪ </span></span><span class="sentence"><span data-start="10.0">Ellie: Hi, I'm Ellie Gattozzi, an Instructional Design Manager at Apple, </span></span><span class="sentence"><span data-start="15.0">and I'll be talking about making features discoverable with TipKit.</p><p></span></span><span class="sentence"><span data-start="19.0">You'll learn how to create your first tip, </span></span><span class="sentence"><span data-start="22.0">add rules to specify who should see the tip, </span></span><span class="sentence"><span data-start="26.0">manage when it's displayed and dismissed </span></span><span class="sentence"><span data-start="28.0">to control the frequency of educational messaging in your app, </span></span><span class="sentence"><span data-start="32.0">and test your tip. </span></span><span class="sentence"><span data-start="35.0">But before jumping in, a little background on TipKit.</p><p></span></span><span class="sentence"><span data-start="40.0">You work on features that you know people will love, </span></span><span class="sentence"><span data-start="43.0">but first, they need to discover them. </span></span><span class="sentence"><span data-start="46.0">TipKit is a new framework that makes it easy to show tips in your app.</p><p></span></span><span class="sentence"><span data-start="52.0">As the Instructional Products team that works on all of Apple's user education, </span></span><span class="sentence"><span data-start="57.0">we designed TipKit with education specifically in mind. </span></span><span class="sentence"><span data-start="61.0">For example, TipKit can teach someone about a brand-new feature, </span></span><span class="sentence"><span data-start="67.0">help with discovery of a hidden feature, </span></span><span class="sentence"><span data-start="70.0">or show a faster way to accomplish a task. </span></span><span class="sentence"><span data-start="73.0">And it's available across iPhone, iPad, </span></span><span class="sentence"><span data-start="79.0">Mac, Apple Watch, and Apple TV. </span></span><span class="sentence"><span data-start="85.0">With that, I'll get started creating a tip. </span></span><span class="sentence"><span data-start="90.0">There's a feature, Favoriting a Backyard, in the Backyard Birds app. </span></span><span class="sentence"><span data-start="95.0">Favoriting makes it easy for a person to </span></span><span class="sentence"><span data-start="97.0">get to the backyards they care about the most, </span></span><span class="sentence"><span data-start="100.0">which makes it a great candidate for tips. </span></span><span class="sentence"><span data-start="102.0">Once I've identified the feature for the tip, I'm ready to turn to Xcode. </span></span><span class="sentence"><span data-start="108.0">I'll begin by defining a new tip for the Favorite a Backyard feature. </span></span><span class="sentence"><span data-start="113.0">Tips are made up of a title and message, so I'll add those. </span></span><span class="sentence"><span data-start="117.0">Useful tips have direct action phrases as titles </span></span><span class="sentence"><span data-start="121.0">that say what the feature is </span></span><span class="sentence"><span data-start="123.0">and messages with easy to remember benefit info or instructions </span></span><span class="sentence"><span data-start="127.0">so users know why they'd want to use the feature </span></span><span class="sentence"><span data-start="130.0">and are later able to accomplish the task on their own.</p><p></span></span><span class="sentence"><span data-start="135.0">Here are a few examples of effective tips. </span></span><span class="sentence"><span data-start="138.0">All of these are actionable, instructional, and easy to remember. </span></span><span class="sentence"><span data-start="145.0">Here are just a few examples of when not to use TipKit. </span></span><span class="sentence"><span data-start="149.0">The first is promotional and the second is an error message, </span></span><span class="sentence"><span data-start="153.0">neither of which are educational. </span></span><span class="sentence"><span data-start="156.0">In the third, it's great that the feature is better, </span></span><span class="sentence"><span data-start="159.0">but people don't need to do anything. </span></span><span class="sentence"><span data-start="162.0">The last is useful, but too complicated to read and remember in the moment.</p><p></span></span><span class="sentence"><span data-start="168.0">Now back to my tip. </span></span><span class="sentence"><span data-start="170.0">I already have my title and message, </span></span><span class="sentence"><span data-start="173.0">so next, I'll put in the basic structure for the tip view and configure TipsCenter. </span></span><span class="sentence"><span data-start="179.0">TipsCenter enables key TipKit functionality, </span></span><span class="sentence"><span data-start="182.0">including having tips and their associated events persist between app launches </span></span><span class="sentence"><span data-start="188.0">and makes it easier to test tips. </span></span><span class="sentence"><span data-start="191.0">A default shared instance of TipsCenter is provided and is what I've added here.</p><p></span></span><span class="sentence"><span data-start="198.0">With TipsCenter configured, I can now see the tip. </span></span><span class="sentence"><span data-start="202.0">It's looking good, but it can look better. </span></span><span class="sentence"><span data-start="206.0">I'll change the text color to indigo so it matches the app, and put in an icon </span></span><span class="sentence"><span data-start="212.0">to help draw attention and visually associate the tip with the feature. </span></span><span class="sentence"><span data-start="216.0">I chose the star symbol since the tip is about favoriting. </span></span><span class="sentence"><span data-start="221.0">If your feature has settings that can be customized, </span></span><span class="sentence"><span data-start="225.0">you may want to add an action button. </span></span><span class="sentence"><span data-start="228.0">Action buttons can take people directly to those settings </span></span><span class="sentence"><span data-start="231.0">so they can make adjustments fast. </span></span><span class="sentence"><span data-start="234.0">Or if you feel like there's more info that people would find particularly useful, </span></span><span class="sentence"><span data-start="239.0">another option is to link to additional resources, </span></span><span class="sentence"><span data-start="242.0">such as an onboarding flow. </span></span><span class="sentence"><span data-start="245.0">OK, I have my instructional tip and it looks great. </span></span><span class="sentence"><span data-start="249.0">Now I'll identify the best treatment and placement for it. </span></span><span class="sentence"><span data-start="253.0">There are two types of tip views: </span></span><span class="sentence"><span data-start="255.0">The first is the popover view, </span></span><span class="sentence"><span data-start="258.0">which allows the tip to appear over your app's UI. </span></span><span class="sentence"><span data-start="262.0">It can point directly to a button or other element </span></span><span class="sentence"><span data-start="265.0">and is particularly useful for directing users </span></span><span class="sentence"><span data-start="268.0">without changing the current app screen. </span></span><span class="sentence"><span data-start="271.0">On tvOS, the popover view is exclusively used. </span></span><span class="sentence"><span data-start="276.0">The second is the in-line view, </span></span><span class="sentence"><span data-start="279.0">which adjusts the app's UI to temporarily fit around it </span></span><span class="sentence"><span data-start="283.0">so no UI is blocked. </span></span><span class="sentence"><span data-start="285.0">When using either view, </span></span><span class="sentence"><span data-start="287.0">it's helpful to have the tip appear close to the relevant button </span></span><span class="sentence"><span data-start="290.0">or element you're calling out.</p><p></span></span><span class="sentence"><span data-start="293.0">For favoriting a backyard, I'm going to go with the popover view </span></span><span class="sentence"><span data-start="298.0">and place it so it points at the star button in the top right corner.</p><p></span></span><span class="sentence"><span data-start="304.0">And that's my app's first tip. </span></span><span class="sentence"><span data-start="306.0">It's looking great and is in context. </span></span><span class="sentence"><span data-start="309.0">But to really take advantage of TipKit's power to reach </span></span><span class="sentence"><span data-start="312.0">the ideal audience at the best time, </span></span><span class="sentence"><span data-start="315.0">we'll want to add some rules. </span></span><span class="sentence"><span data-start="317.0">I'll pass it over to Charlie to talk about how to take tips to the next level.</p><p></span></span><span class="sentence"><span data-start="322.0">Charlie: Thanks, Ellie. </span></span><span class="sentence"><span data-start="323.0">Hi, I'm Charlie Parks, a Software Engineering Manager </span></span><span class="sentence"><span data-start="327.0">from the Instructional Products team. </span></span><span class="sentence"><span data-start="329.0">Discovering a new feature can invoke a feeling of surprise and delight, </span></span><span class="sentence"><span data-start="332.0">but only if the feature is of interest to the person, </span></span><span class="sentence"><span data-start="335.0">and any education about it doesn't come off as spammy or irrelevant. </span></span><span class="sentence"><span data-start="340.0">The tip about favoriting a backyard is useful, but it may not be for everyone, </span></span><span class="sentence"><span data-start="344.0">especially people who have already discovered the feature. </span></span><span class="sentence"><span data-start="347.0">It may also be of less interest to people who infrequently use the app. </span></span><span class="sentence"><span data-start="352.0">At Apple, we believe that in-app education </span></span><span class="sentence"><span data-start="354.0">should be focused on those who would benefit from it, </span></span><span class="sentence"><span data-start="357.0">and we aim to avoid getting in the way of individuals </span></span><span class="sentence"><span data-start="359.0">while they are trying to accomplish something in an app. </span></span><span class="sentence"><span data-start="362.0">To ensure a tip is displayed only for the most relevant audience </span></span><span class="sentence"><span data-start="366.0">at the most ideal time, </span></span><span class="sentence"><span data-start="367.0">TipKit provides a number of eligibility rules you can use alone, or together, </span></span><span class="sentence"><span data-start="372.0">to determine exactly when a tip should be displayed. </span></span><span class="sentence"><span data-start="377.0">There are two main types of rules. The first is a parameter-based rule. </span></span><span class="sentence"><span data-start="381.0">Parameter-based rules are persistent and are best suited for showing tips </span></span><span class="sentence"><span data-start="385.0">based on a Swift value type that you want to write an expression around. </span></span><span class="sentence"><span data-start="389.0">The second is an event-based rule. </span></span><span class="sentence"><span data-start="392.0">Event-based rules allow you to define an action </span></span><span class="sentence"><span data-start="394.0">that must be performed before a person becomes eligible for a tip.</p><p></span></span><span class="sentence"><span data-start="399.0">For my Favorite a Backyard tip, the first thing I want to do </span></span><span class="sentence"><span data-start="402.0">is to make sure the person is logged in to their account. </span></span><span class="sentence"><span data-start="405.0">I'll implement a parameter-based rule to accomplish that. </span></span><span class="sentence"><span data-start="408.0">First, I'll set the parameter's initial value to false. </span></span><span class="sentence"><span data-start="412.0">Then, I'll add this to the tip's rules. </span></span><span class="sentence"><span data-start="415.0">Great. I've narrowed down who will get the tip, </span></span><span class="sentence"><span data-start="418.0">but now I want to narrow it down further. </span></span><span class="sentence"><span data-start="420.0">I want people to be able to use the app </span></span><span class="sentence"><span data-start="422.0">and discover the feature organically before giving them a nudge. </span></span><span class="sentence"><span data-start="426.0">The way I'll do that is by creating an event-based rule </span></span><span class="sentence"><span data-start="429.0">that will make sure the tip only displays </span></span><span class="sentence"><span data-start="431.0">after a person has gone to the Backyard Detail View at least three times.</p><p></span></span><span class="sentence"><span data-start="437.0">So, first, I'm going to create the event. </span></span><span class="sentence"><span data-start="440.0">Then, I'll have the rule count the number of times I want the event to trigger </span></span><span class="sentence"><span data-start="444.0">before the rule evaluates to true. </span></span><span class="sentence"><span data-start="447.0">In this case, I want the person </span></span><span class="sentence"><span data-start="448.0">to enter the Backyard Detail View three times before I present the tip. </span></span><span class="sentence"><span data-start="454.0">The last thing I need to do is to donate the event. </span></span><span class="sentence"><span data-start="457.0">So in the BackyardDetailView, </span></span><span class="sentence"><span data-start="459.0">I'll donate the event any time the view appears. </span></span><span class="sentence"><span data-start="463.0">These rules are coming together, but what if I wanted to tighten them a bit further? </span></span><span class="sentence"><span data-start="467.0">So far, I've focused on people who frequently go to a Backyard Detail View, </span></span><span class="sentence"><span data-start="471.0">but have never favorited a backyard. </span></span><span class="sentence"><span data-start="474.0">Now, I also want to show the tip only to </span></span><span class="sentence"><span data-start="477.0">those individuals who regularly use the app.</p><p></span></span><span class="sentence"><span data-start="480.0">I'll add a date query modifier to my event-based rule </span></span><span class="sentence"><span data-start="483.0">that will ensure it will only ever evaluate to true </span></span><span class="sentence"><span data-start="486.0">if someone has gone to the Backyard Detail View three times in the past five days. </span></span><span class="sentence"><span data-start="492.0">Another very powerful TipKit feature is the ability to create custom donations </span></span><span class="sentence"><span data-start="497.0">by adding associated types to each event donation, </span></span><span class="sentence"><span data-start="500.0">and querying the events based on those types. </span></span><span class="sentence"><span data-start="504.0">Using associated types, I can further refine the event-based rule, </span></span><span class="sentence"><span data-start="508.0">so it matches only when someone has gone to a specific Backyard Detail View.</p><p></span></span><span class="sentence"><span data-start="514.0">First, I'll create a DetailViewDonation </span></span><span class="sentence"><span data-start="516.0">and supply the ID of the specific backyard view. </span></span><span class="sentence"><span data-start="520.0">Then, in my donation, </span></span><span class="sentence"><span data-start="522.0">I'll include the ID of the backyard view a person is currently in. </span></span><span class="sentence"><span data-start="526.0">Once those are both established, I'll update my rule to query for events </span></span><span class="sentence"><span data-start="530.0">based on a unique backyard ID. </span></span><span class="sentence"><span data-start="533.0">When defining these custom donations, </span></span><span class="sentence"><span data-start="535.0">keep in mind the size of the data being stored. </span></span><span class="sentence"><span data-start="537.0">The larger the size, the slower and less performant the query will execute. </span></span><span class="sentence"><span data-start="543.0">Rules in TipKit are easy to compose, and they provide a powerfully simple way </span></span><span class="sentence"><span data-start="547.0">to make sure that tips are shown to people who will get the most benefit from them. </span></span><span class="sentence"><span data-start="551.0">Rules can be as general or as specific as needed </span></span><span class="sentence"><span data-start="554.0">and can be combined to target the ideal audience at the best time. </span></span><span class="sentence"><span data-start="558.0">Once a tip appears in my app, I don't want it to stay on screen forever, </span></span><span class="sentence"><span data-start="562.0">and I don't want it to linger if a person has used the feature described in the tip. </span></span><span class="sentence"><span data-start="566.0">And if I have multiple tips, I don't want them to all appear at once </span></span><span class="sentence"><span data-start="569.0">because that could be overwhelming </span></span><span class="sentence"><span data-start="571.0">and get in the way of what the person is trying to do. </span></span><span class="sentence"><span data-start="574.0">TipKit provides several display and dismissal behaviors, </span></span><span class="sentence"><span data-start="577.0">so tips appear at a good cadence and only as long as they're useful. </span></span><span class="sentence"><span data-start="581.0">Let's say that I've added several more tips to my app, </span></span><span class="sentence"><span data-start="583.0">so now I have five tips in the Backyard Birds app. </span></span><span class="sentence"><span data-start="586.0">The tips are all useful but would be less effective if they all showed up at once. </span></span><span class="sentence"><span data-start="591.0">With TipKit, I can set the display frequency for the tips, </span></span><span class="sentence"><span data-start="594.0">so they appear at a more ideal cadence.</p><p></span></span><span class="sentence"><span data-start="598.0">In TipsCenter, I can specify the length of time that must pass </span></span><span class="sentence"><span data-start="602.0">before another tip can appear. </span></span><span class="sentence"><span data-start="604.0">I can use .daily if I want to have one tip show every 24 hours, </span></span><span class="sentence"><span data-start="608.0">.hourly if I want to have one tip show every 60 minutes, </span></span><span class="sentence"><span data-start="612.0">or I can specify a custom duration by supplying any valid TimeInterval value. </span></span><span class="sentence"><span data-start="618.0">And if I really feel that education is needed instantly, </span></span><span class="sentence"><span data-start="621.0">I can also use the .immediate modifier. </span></span><span class="sentence"><span data-start="623.0">This way, people will see any tips they are eligible for </span></span><span class="sentence"><span data-start="627.0">the moment that they're eligible for them, </span></span><span class="sentence"><span data-start="628.0">even if another tip recently appeared, or is on screen. </span></span><span class="sentence"><span data-start="633.0">Instead of ignoring display frequency at the TipsCenter level, </span></span><span class="sentence"><span data-start="636.0">it may be more useful to ignore it on a one-off, per-tip basis. </span></span><span class="sentence"><span data-start="641.0">To do so, I can add the .ignoresDisplayFrequency option </span></span><span class="sentence"><span data-start="645.0">to the specific tip. </span></span><span class="sentence"><span data-start="647.0">Now, only that specific tip will show the second someone is eligible, </span></span><span class="sentence"><span data-start="651.0">while the rest of the tips in my app will continue to follow </span></span><span class="sentence"><span data-start="653.0">the display cadence I set at the TipsCenter level. </span></span><span class="sentence"><span data-start="657.0">Once tips appear, I want them to stay on screen only as long as they're useful. </span></span><span class="sentence"><span data-start="661.0">So if someone uses the feature described in the tip, </span></span><span class="sentence"><span data-start="665.0">which means they've performed the action, </span></span><span class="sentence"><span data-start="667.0">or if they're eligible for the tip but still not interested in it, </span></span><span class="sentence"><span data-start="670.0">the tip should be dismissed. </span></span><span class="sentence"><span data-start="673.0">Let's say the person using the app has satisfied all of the rules </span></span><span class="sentence"><span data-start="676.0">for having the tip display: they're logged in, </span></span><span class="sentence"><span data-start="679.0">have opened the Backyard Detail View three times in the last five days, </span></span><span class="sentence"><span data-start="683.0">but have never tapped the favorite button on any backyard. </span></span><span class="sentence"><span data-start="687.0">Once they see the tip and tap the favorite button, </span></span><span class="sentence"><span data-start="690.0">I'll call the invalidate method with the reason ".userPerformedAction" </span></span><span class="sentence"><span data-start="694.0">to signify that the desired outcome has been performed </span></span><span class="sentence"><span data-start="697.0">and the tip will be dismissed. </span></span><span class="sentence"><span data-start="700.0">Another way a tip can be dismissed is if </span></span><span class="sentence"><span data-start="702.0">the tip is displayed more than the defined .maxDisplayCount. </span></span><span class="sentence"><span data-start="705.0">In this case, once the tip displays five times </span></span><span class="sentence"><span data-start="708.0">and no action has been taken, it should not display the next time </span></span><span class="sentence"><span data-start="712.0">the individual enters the Backyard Detail View. </span></span><span class="sentence"><span data-start="715.0">These are some of the ways that tips can be dismissed </span></span><span class="sentence"><span data-start="717.0">using built-in functionality that TipKit provides. </span></span><span class="sentence"><span data-start="721.0">But since you know your app the best, </span></span><span class="sentence"><span data-start="723.0">you can dismiss a tip based on any interaction or criteria you see fit </span></span><span class="sentence"><span data-start="727.0">by using the .invalidate() method. </span></span><span class="sentence"><span data-start="730.0">TipKit can also sync tip status via iCloud </span></span><span class="sentence"><span data-start="732.0">to ensure that tips seen on one device won't be seen on the other. </span></span><span class="sentence"><span data-start="736.0">For instance, if someone using the app </span></span><span class="sentence"><span data-start="739.0">has it installed on both an iPad and an iPhone, </span></span><span class="sentence"><span data-start="741.0">and the features are identical on both of those devices, </span></span><span class="sentence"><span data-start="744.0">it's probably best to not educate them on both devices about the feature. </span></span><span class="sentence"><span data-start="749.0">I'll hand it back to Ellie now to talk about testing APIs that TipKit provides, </span></span><span class="sentence"><span data-start="753.0">and some next steps. </span></span><span class="sentence"><span data-start="755.0">Ellie: To make testing easy, TipKit comes with some convenient APIs </span></span><span class="sentence"><span data-start="760.0">that you can use to work around eligibility rules </span></span><span class="sentence"><span data-start="763.0">so you can show or hide tips as needed. </span></span><span class="sentence"><span data-start="767.0">You can inspect all the tips in your app </span></span><span class="sentence"><span data-start="769.0">without needing to satisfy the rules set for them. </span></span><span class="sentence"><span data-start="772.0">To do so, add .showAllTips to TipsCenter's configuration. </span></span><span class="sentence"><span data-start="777.0">If you only want certain tips to show when testing, </span></span><span class="sentence"><span data-start="781.0">use .showTips and pass in the specific tip IDs, </span></span><span class="sentence"><span data-start="786.0">or use .hideTips to prevent specific tips from showing. </span></span><span class="sentence"><span data-start="791.0">And if you want to make sure that no tips show, </span></span><span class="sentence"><span data-start="794.0">so you can focus on a different feature in your app, </span></span><span class="sentence"><span data-start="797.0">use .hideAllTips. </span></span><span class="sentence"><span data-start="800.0">You can also use .resetDatastore to purge all info in the TipKit data store, </span></span><span class="sentence"><span data-start="806.0">setting a pristine state at each build of the app. </span></span><span class="sentence"><span data-start="810.0">All of the same testing options you can call via API </span></span><span class="sentence"><span data-start="814.0">are also available as launch arguments that you can add to your project's scheme. </span></span><span class="sentence"><span data-start="818.0">You can use any of these to do a quick spot check or test full functionality. </span></span><span class="sentence"><span data-start="824.0">And with that, you're all set. </span></span><span class="sentence"><span data-start="826.0">You've created your tip, added your rules, set the frequency for tips in your app, </span></span><span class="sentence"><span data-start="832.0">and now are testing so everything works as expected. </span></span><span class="sentence"><span data-start="836.0">To summarize, tips are great for helping users discover features in your app. </span></span><span class="sentence"><span data-start="842.0">Remember to keep your tips short, instructional, and actionable, </span></span><span class="sentence"><span data-start="847.0">and use the easy to define but powerful rules to target the ideal audience. </span></span><span class="sentence"><span data-start="853.0">TipKit's sample code will be available on developer.apple.com, </span></span><span class="sentence"><span data-start="858.0">and keep an eye out for the TipKit human interface guidelines </span></span><span class="sentence"><span data-start="862.0">to get additional info on creating great tips. </span></span><span class="sentence"><span data-start="865.0">Now go review your app for discoverability opportunities, </span></span><span class="sentence"><span data-start="869.0">and as you're working on new features, keep TipKit in mind. </span></span><span class="sentence"><span data-start="874.0">On behalf of the Instructional Products team, </span></span><span class="sentence"><span data-start="876.0">thanks for watching.</span></span></p> </section> </li> <!-- sample code --> <li class="supplement sample-code" data-supplement-id="sample-code"> <section> <ul class="no-bullet padding-top-small margin-bottom"> <li class="sample-code-main-container"> <button class="btn-copy-code" data-text-copied="Code Copied" data-text-default="Copy Code">Copy Code</button> <p>1:55 - <a class="jump-to-time-sample" href="/videos/play/wwdc2023/10229/?time=115" data-start-time="115">Create a tip</a></p> <pre class="code-source"><code><span class="syntax-keyword">struct</span> <span class="syntax-title class_">FavoriteBackyardTip</span>: <span class="syntax-title class_">Tip</span> { <span class="syntax-keyword">var</span> title: <span class="syntax-type">Text</span> { <span class="syntax-type">Text</span>(<span class="syntax-string">"Save as a Favorite"</span>) } <span class="syntax-keyword">var</span> message: <span class="syntax-type">Text</span> { <span class="syntax-type">Text</span>(<span class="syntax-string">"Your favorite backyards always appear at the top of the list."</span>) } }</code></pre> </li> <li class="sample-code-main-container"> <button class="btn-copy-code" data-text-copied="Code Copied" data-text-default="Copy Code">Copy Code</button> <p>2:48 - <a class="jump-to-time-sample" href="/videos/play/wwdc2023/10229/?time=168" data-start-time="168">Configure TipsCenter</a></p> <pre class="code-source"><code><span class="syntax-keyword">@main</span> <span class="syntax-keyword">struct</span> <span class="syntax-title class_">BackyardBirdsApp</span>: <span class="syntax-title class_">App</span> { <span class="syntax-keyword">var</span> body: <span class="syntax-keyword">some</span> <span class="syntax-type">Scene</span> { <span class="syntax-type">WindowGroup</span> { <span class="syntax-type">ContentView</span>() } } <span class="syntax-comment">// ...</span> <span class="syntax-keyword">init</span>() { <span class="syntax-type">TipsCenter</span>.shared.configure() } }</code></pre> </li> <li class="sample-code-main-container"> <button class="btn-copy-code" data-text-copied="Code Copied" data-text-default="Copy Code">Copy Code</button> <p>3:18 - <a class="jump-to-time-sample" href="/videos/play/wwdc2023/10229/?time=198" data-start-time="198">Add actions and an asset to a tip</a></p> <pre class="code-source"><code><span class="syntax-keyword">struct</span> <span class="syntax-title class_">FavoriteBackyardTip</span>: <span class="syntax-title class_">Tip</span> { <span class="syntax-keyword">var</span> title: <span class="syntax-type">Text</span> { <span class="syntax-type">Text</span>(<span class="syntax-string">"Save as a Favorite"</span>).foregroundColor(.indigo) } <span class="syntax-keyword">var</span> message: <span class="syntax-type">Text</span> { <span class="syntax-type">Text</span>(<span class="syntax-string">"Your favorite backyards always appear at the top of the list."</span>) } <span class="syntax-keyword">var</span> asset: <span class="syntax-type">Image</span> { <span class="syntax-type">Image</span>(systemName: <span class="syntax-string">"star"</span>) } <span class="syntax-keyword">var</span> actions: [<span class="syntax-type">Action</span>] { [ <span class="syntax-type">Tip</span>.<span class="syntax-type">Action</span>( id: <span class="syntax-string">"learn-more"</span>, title: <span class="syntax-string">"Learn More"</span> ) ] } }</code></pre> </li> <li class="sample-code-main-container"> <button class="btn-copy-code" data-text-copied="Code Copied" data-text-default="Copy Code">Copy Code</button> <p>4:53 - <a class="jump-to-time-sample" href="/videos/play/wwdc2023/10229/?time=293" data-start-time="293">Create a popover view</a></p> <pre class="code-source"><code><span class="syntax-keyword">private</span> <span class="syntax-keyword">let</span> favoriteBackyardTip <span class="syntax-operator">=</span> <span class="syntax-type">FavoriteBackyardTip</span>() <span class="syntax-comment">// ...</span> .toolbar { <span class="syntax-type">ToolbarItem</span> { <span class="syntax-type">Button</span> { backyard.isFavorite.toggle() } label: { <span class="syntax-type">Label</span>(<span class="syntax-string">"Favorite"</span>, systemImage: <span class="syntax-string">"star"</span>) .symbolVariant( backyard.isFavorite <span class="syntax-operator">?</span> .fill : .none ) } .popoverMiniTip(tip: favoriteBackyardTip) } }</code></pre> </li> <li class="sample-code-main-container"> <button class="btn-copy-code" data-text-copied="Code Copied" data-text-default="Copy Code">Copy Code</button> <p>6:38 - <a class="jump-to-time-sample" href="/videos/play/wwdc2023/10229/?time=398" data-start-time="398">Add a parameter based rule</a></p> <pre class="code-source"><code><span class="syntax-keyword">struct</span> <span class="syntax-title class_">FavoriteBackyardTip</span>: <span class="syntax-title class_">Tip</span> { <span class="syntax-meta">@Parameter</span> <span class="syntax-keyword">static</span> <span class="syntax-keyword">var</span> isLoggedIn: <span class="syntax-type">Bool</span> <span class="syntax-operator">=</span> <span class="syntax-literal">false</span> <span class="syntax-comment">// ...</span> <span class="syntax-keyword">var</span> rules: <span class="syntax-type">Predicate</span><<span class="syntax-type">RuleInput</span>...> { <span class="syntax-comment">// User is logged in</span> #<span class="syntax-type">Rule</span>(<span class="syntax-keyword">Self</span>.<span class="syntax-variable">$isLoggedIn</span>) { <span class="syntax-variable">$0</span> <span class="syntax-operator">==</span> <span class="syntax-literal">true</span> } } }</code></pre> </li> <li class="sample-code-main-container"> <button class="btn-copy-code" data-text-copied="Code Copied" data-text-default="Copy Code">Copy Code</button> <p>7:16 - <a class="jump-to-time-sample" href="/videos/play/wwdc2023/10229/?time=436" data-start-time="436">Add an event based rule</a></p> <pre class="code-source"><code><span class="syntax-keyword">struct</span> <span class="syntax-title class_">FavoriteBackyardTip</span>: <span class="syntax-title class_">Tip</span> { <span class="syntax-meta">@Parameter</span> <span class="syntax-keyword">static</span> <span class="syntax-keyword">var</span> isLoggedIn: <span class="syntax-type">Bool</span> <span class="syntax-operator">=</span> <span class="syntax-literal">false</span> <span class="syntax-keyword">static</span> <span class="syntax-keyword">let</span> enteredBackyardDetailView: <span class="syntax-type">Event</span> <span class="syntax-operator">=</span> <span class="syntax-type">Event</span><<span class="syntax-type">DetailViewDonation</span>>( id: <span class="syntax-string">"entered-backyard-detail-view"</span> ) <span class="syntax-comment">// ...</span> <span class="syntax-keyword">var</span> rules: <span class="syntax-type">Predicate</span><<span class="syntax-type">RuleInput</span>...> { <span class="syntax-comment">// User is logged in</span> #<span class="syntax-type">Rule</span>(<span class="syntax-keyword">Self</span>.<span class="syntax-variable">$isLoggedIn</span>) { <span class="syntax-variable">$0</span> <span class="syntax-operator">==</span> <span class="syntax-literal">true</span> } <span class="syntax-comment">// User has entered any backyard detail view at least 3 times</span> #<span class="syntax-type">Rule</span>(<span class="syntax-keyword">Self</span>.enteredBackyardDetailView) { <span class="syntax-variable">$0</span>.count <span class="syntax-operator">>=</span> <span class="syntax-number">3</span> } } }</code></pre> </li> <li class="sample-code-main-container"> <button class="btn-copy-code" data-text-copied="Code Copied" data-text-default="Copy Code">Copy Code</button> <p>7:34 - <a class="jump-to-time-sample" href="/videos/play/wwdc2023/10229/?time=454" data-start-time="454">Donate the event when a view appears</a></p> <pre class="code-source"><code>.onAppear { <span class="syntax-type">FavoriteBackyardTip</span>.enteredBackyardDetailView.donate() }</code></pre> </li> <li class="sample-code-main-container"> <button class="btn-copy-code" data-text-copied="Code Copied" data-text-default="Copy Code">Copy Code</button> <p>7:59 - <a class="jump-to-time-sample" href="/videos/play/wwdc2023/10229/?time=479" data-start-time="479">Filter event donations in an event based rule</a></p> <pre class="code-source"><code><span class="syntax-comment">// User has entered any backyard detail view at least 3 times in the past 5 days</span> #<span class="syntax-type">Rule</span>(<span class="syntax-keyword">Self</span>.enteredBackyardDetailView) { <span class="syntax-variable">$0</span>.donations.filter { <span class="syntax-variable">$0</span>.date <span class="syntax-operator">></span> <span class="syntax-type">Date</span>.now.addingTimeInterval(<span class="syntax-operator">-</span><span class="syntax-number">5</span> <span class="syntax-operator">*</span> <span class="syntax-number">60</span> <span class="syntax-operator">*</span> <span class="syntax-number">60</span> <span class="syntax-operator">*</span> <span class="syntax-number">24</span>) } .count <span class="syntax-operator">>=</span> <span class="syntax-number">3</span> }</code></pre> </li> <li class="sample-code-main-container"> <button class="btn-copy-code" data-text-copied="Code Copied" data-text-default="Copy Code">Copy Code</button> <p>8:34 - <a class="jump-to-time-sample" href="/videos/play/wwdc2023/10229/?time=514" data-start-time="514">Create a custom donation</a></p> <pre class="code-source"><code><span class="syntax-comment">// Create the associated type</span> <span class="syntax-keyword">extension</span> <span class="syntax-title class_">BackyardDetailTip</span> { <span class="syntax-keyword">struct</span> <span class="syntax-title class_">DetailViewDonation</span>: <span class="syntax-title class_">DonationValue</span> { <span class="syntax-keyword">let</span> backyardID: <span class="syntax-type">Int</span> } } <span class="syntax-comment">// Donate the unique id of the backyard detail being viewed</span> .onAppear { <span class="syntax-type">BackyardFavoriteTip</span>.enteredBackyardDetailView.donate( with: .<span class="syntax-keyword">init</span>(backyardID: backyard.id) ) } <span class="syntax-keyword">struct</span> <span class="syntax-title class_">FavoriteBackyardTip</span>: <span class="syntax-title class_">Tip</span> { <span class="syntax-comment">// ...</span> <span class="syntax-keyword">var</span> rules: <span class="syntax-type">Predicate</span><<span class="syntax-type">RuleInput</span>...> { <span class="syntax-comment">// Update the rule to specify a backyardID</span> #<span class="syntax-type">Rule</span>(<span class="syntax-keyword">Self</span>.enteredBackyardDetailView) { <span class="syntax-variable">$0</span>.donations.filter { <span class="syntax-variable">$0</span>.date <span class="syntax-operator">></span> <span class="syntax-type">Date</span>.now.addingTimeInterval(<span class="syntax-operator">-</span><span class="syntax-number">5</span> <span class="syntax-operator">*</span> <span class="syntax-number">60</span> <span class="syntax-operator">*</span> <span class="syntax-number">60</span> <span class="syntax-operator">*</span> <span class="syntax-number">24</span>) } .largestSubset(by: \.backyardID) .count <span class="syntax-operator">>=</span> <span class="syntax-number">3</span> } } }</code></pre> </li> <li class="sample-code-main-container"> <button class="btn-copy-code" data-text-copied="Code Copied" data-text-default="Copy Code">Copy Code</button> <p>9:57 - <a class="jump-to-time-sample" href="/videos/play/wwdc2023/10229/?time=597" data-start-time="597">Configure display frequency</a></p> <pre class="code-source"><code><span class="syntax-comment">// One tip per day.</span> <span class="syntax-type">TipsCenter</span>.shared.configure { <span class="syntax-type">DisplayFrequency</span>(.daily) } <span class="syntax-comment">// One tip per hour.</span> <span class="syntax-type">TipsCenter</span>.shared.configure { <span class="syntax-type">DisplayFrequency</span>(.hourly) } <span class="syntax-comment">// Custom configuration. Only show one tip every five days.</span> <span class="syntax-keyword">let</span> fiveDays: <span class="syntax-type">TimeInterval</span> <span class="syntax-operator">=</span> <span class="syntax-number">5</span> <span class="syntax-operator">*</span> <span class="syntax-number">24</span> <span class="syntax-operator">*</span> <span class="syntax-number">60</span> <span class="syntax-operator">*</span> <span class="syntax-number">60</span> <span class="syntax-type">TipsCenter</span>.shared.configure { <span class="syntax-type">DisplayFrequency</span>(fiveDays) } <span class="syntax-comment">// No frequency control. Show all tips as soon as eligible.</span> <span class="syntax-type">TipsCenter</span>.shared.configure { <span class="syntax-type">DisplayFrequency</span>(.immediate) }</code></pre> </li> <li class="sample-code-main-container"> <button class="btn-copy-code" data-text-copied="Code Copied" data-text-default="Copy Code">Copy Code</button> <p>10:34 - <a class="jump-to-time-sample" href="/videos/play/wwdc2023/10229/?time=634" data-start-time="634">Turn off display frequency controls for a tip</a></p> <pre class="code-source"><code><span class="syntax-keyword">struct</span> <span class="syntax-title class_">FavoriteBackyardTip</span>: <span class="syntax-title class_">Tip</span> { <span class="syntax-comment">// ...</span> <span class="syntax-keyword">var</span> options: [<span class="syntax-type">Option</span>] { [.ignoresDisplayFrequency(<span class="syntax-literal">true</span>)] } }</code></pre> </li> <li class="sample-code-main-container"> <button class="btn-copy-code" data-text-copied="Code Copied" data-text-default="Copy Code">Copy Code</button> <p>11:27 - <a class="jump-to-time-sample" href="/videos/play/wwdc2023/10229/?time=687" data-start-time="687">Invalidate a tip</a></p> <pre class="code-source"><code><span class="syntax-type">Button</span> { backyard.isFavorite.toggle() <span class="syntax-comment">// When user taps the favorite button, dismiss the tip</span> favoriteBackyardTip.invalidate(reason: .userPerformedAction) } label: { <span class="syntax-type">Label</span>(<span class="syntax-string">"Favorite"</span>, systemImage: <span class="syntax-string">"star"</span>) .symbolVariant(backyard.isFavorite <span class="syntax-operator">?</span> .fill : .none) } .popoverMiniTip(tip: favoriteBackyardTip)</code></pre> </li> <li class="sample-code-main-container"> <button class="btn-copy-code" data-text-copied="Code Copied" data-text-default="Copy Code">Copy Code</button> <p>11:41 - <a class="jump-to-time-sample" href="/videos/play/wwdc2023/10229/?time=701" data-start-time="701">Configure max display count on a tip</a></p> <pre class="code-source"><code><span class="syntax-keyword">struct</span> <span class="syntax-title class_">FavoriteBackyardTip</span>: <span class="syntax-title class_">Tip</span> { <span class="syntax-comment">// ...</span> <span class="syntax-keyword">var</span> options: [<span class="syntax-type">Option</span>] { [.maxDisplayCount(<span class="syntax-number">5</span>)] } }</code></pre> </li> <li class="sample-code-main-container"> <button class="btn-copy-code" data-text-copied="Code Copied" data-text-default="Copy Code">Copy Code</button> <p>12:46 - <a class="jump-to-time-sample" href="/videos/play/wwdc2023/10229/?time=766" data-start-time="766">Programmatically call testing API</a></p> <pre class="code-source"><code><span class="syntax-comment">// Show all defined tips in the app</span> <span class="syntax-type">TipsCenter</span>.showAllTips() <span class="syntax-comment">// Show some tips, but not all</span> <span class="syntax-type">TipsCenter</span>.showTips([tip1, tip2, tip3]) <span class="syntax-comment">// Hide some tips, but not all</span> <span class="syntax-type">TipsCenter</span>.hideTips([tip1, tip2, tip3]) <span class="syntax-comment">// Hide all tips defined in the app</span> <span class="syntax-type">TipsCenter</span>.hideAllTips() <span class="syntax-comment">// Purge all TipKit related data</span> <span class="syntax-type">TipsCenter</span>.resetDatastore()</code></pre> </li> <li class="sample-code-main-container"> <button class="btn-copy-code" data-text-copied="Code Copied" data-text-default="Copy Code">Copy Code</button> <p>13:31 - <a class="jump-to-time-sample" href="/videos/play/wwdc2023/10229/?time=811" data-start-time="811">Configure launch arguments in your scheme</a></p> <pre class="code-source"><code><span class="syntax-comment">// Show all defined tips in the app</span> com.apple.<span class="syntax-type">TipKit</span>.<span class="syntax-type">ShowAllTips</span> <span class="syntax-number">1</span> <span class="syntax-comment">// Show some tips, but not all</span> com.apple.<span class="syntax-type">TipKit</span>.<span class="syntax-type">ShowTips</span> tipID,otherTipID <span class="syntax-comment">// Hide some tips, but not all</span> com.apple.<span class="syntax-type">TipKit</span>.<span class="syntax-type">HideAllTips</span> <span class="syntax-number">1</span> <span class="syntax-comment">// Hide all tips defined in the app</span> com.apple.<span class="syntax-type">TipKit</span>.<span class="syntax-type">HideTips</span> tipID,otherTipID <span class="syntax-comment">// Purge all TipKit related data</span> com.apple.<span class="syntax-type">TipKit</span>.<span class="syntax-type">ResetDatastore</span> <span class="syntax-number">1</span></code></pre> </li> </ul> </section> </li> <!-- search --> <li class="supplement search active" data-supplement-id="search"> <section class="transcript-results-summary"> <p class="message smaller lighter"><!-- to be populated --></p> <p class="instructions smaller lighter text-center">Looking for something specific? Enter a topic above and jump straight to the good stuff.</p> <p class="error smaller lighter italic text-center" style="display:none;">An error occurred when submitting your query. Please check your Internet connection and try again.</p> </section> <!-- search results --> <ul class="transcript-results row"> </ul> </li> </ul> </section> </section> </section> <input id="analytics-meta" type="hidden" data-event-name="WWDC23" data-event-id="wwdc2023-10229" data-session-id="10229" data-video-name="Make features discoverable with TipKit" data-session-response-id=""> </main> <link rel="stylesheet" href="/assets/styles/footer.dist.css?17182448067"> <footer id="footer" class="footer" role="contentinfo" aria-labelledby="footer-label"> <div class="footer-content"> <h2 class="footer-label" id="footer-label">Developer Footer</h2> <developer-breadcrumbs> <li class="footer-breadcrumbs-item"><a href="/videos/">Videos</a></li> <li class="footer-breadcrumbs-item"><a href="/videos/wwdc2023/">WWDC23</a></li> <li class="footer-breadcrumbs-item">Make features discoverable with TipKit</li> </developer-breadcrumbs> <nav class="footer-directory" aria-label="Apple Developer Directory" role="navigation"> <!--googleoff: all--> <div class="footer-directory-column"> <input class="footer-directory-column-section-state" type="checkbox" id="footer-directory-column-section-state-platform" /> <div class="footer-directory-column-section"> <label class="footer-directory-column-section-label" for="footer-directory-column-section-state-platform"> <h3 class="footer-directory-column-section-title">Platforms</h3> </label> <a href="#footer-directory-column-section-state-platform" class="footer-directory-column-section-anchor footer-directory-column-section-anchor-open"> <span class="footer-directory-column-section-anchor-label">Open Menu</span> </a> <a href="#" class="footer-directory-column-section-anchor footer-directory-column-section-anchor-close"> <span class="footer-directory-column-section-anchor-label">Close Menu</span> </a> <ul class="footer-directory-column-section-list"> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/ios/">iOS</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/ipados/">iPadOS</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/macos/">macOS</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/tvos/">tvOS</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/visionos/">visionOS</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/watchos/">watchOS</a></li> </ul> </div> <input class="footer-directory-column-section-state" type="checkbox" id="footer-directory-column-section-state-tools" /> <div class="footer-directory-column-section"> <label class="footer-directory-column-section-label" for="footer-directory-column-section-state-tools"> <h3 class="footer-directory-column-section-title">Tools</h3> </label> <a href="#footer-directory-column-section-state-tools" class="footer-directory-column-section-anchor footer-directory-column-section-anchor-open"> <span class="footer-directory-column-section-anchor-label">Open Menu</span> </a> <a href="#" class="footer-directory-column-section-anchor footer-directory-column-section-anchor-close"> <span class="footer-directory-column-section-anchor-label">Close Menu</span> </a> <ul class="footer-directory-column-section-list"> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/swift/">Swift</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/swiftui/">SwiftUI</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/swift-playgrounds/">Swift Playgrounds</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/testflight/">TestFlight</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/xcode/">Xcode</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/xcode-cloud/">Xcode Cloud</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/sf-symbols/">SF Symbols</a></li> </ul> </div> </div> <div class="footer-directory-column"> <input class="footer-directory-column-section-state" type="checkbox" id="footer-directory-column-section-state-topics" /> <div class="footer-directory-column-section"> <label class="footer-directory-column-section-label" for="footer-directory-column-section-state-topics"> <h3 class="footer-directory-column-section-title">Topics & Technologies</h3> </label> <a href="#footer-directory-column-section-state-topics" class="footer-directory-column-section-anchor footer-directory-column-section-anchor-open"> <span class="footer-directory-column-section-anchor-label">Open Menu</span> </a> <a href="#" class="footer-directory-column-section-anchor footer-directory-column-section-anchor-close"> <span class="footer-directory-column-section-anchor-label">Close Menu</span> </a> <ul class="footer-directory-column-section-list"> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/accessibility/">Accessibility</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/accessories/">Accessories</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/app-extensions/">App Extensions</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/app-store/">App Store</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/audio/">Audio & Video</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/augmented-reality/">Augmented Reality</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/design/">Design</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/distribute/">Distribution</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/education/">Education</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/fonts/">Fonts</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/games/">Games</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/health-fitness/">Health & Fitness</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/in-app-purchase/">In-App Purchase</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/localization/">Localization</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/maps/">Maps & Location</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/machine-learning/">Machine Learning</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="https://opensource.apple.com">Open Source</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/security/">Security</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/safari/">Safari & Web</a></li> </ul> </div> </div> <div class="footer-directory-column"> <input class="footer-directory-column-section-state" type="checkbox" id="footer-directory-column-section-state-resources" /> <div class="footer-directory-column-section"> <label class="footer-directory-column-section-label" for="footer-directory-column-section-state-resources"> <h3 class="footer-directory-column-section-title">Resources</h3> </label> <a href="#footer-directory-column-section-state-resources" class="footer-directory-column-section-anchor footer-directory-column-section-anchor-open"> <span class="footer-directory-column-section-anchor-label">Open Menu</span> </a> <a href="#" class="footer-directory-column-section-anchor footer-directory-column-section-anchor-close"> <span class="footer-directory-column-section-anchor-label">Close Menu</span> </a> <ul class="footer-directory-column-section-list"> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/documentation/">Documentation</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/learn/">Tutorials</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/download/">Downloads</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/forums/">Forums</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/videos/">Videos</a></li> </ul> </div> <input class="footer-directory-column-section-state" type="checkbox" id="footer-directory-column-section-state-support" /> <div class="footer-directory-column-section"> <label class="footer-directory-column-section-label" for="footer-directory-column-section-state-support"> <h3 class="footer-directory-column-section-title">Support</h3> </label> <a href="#footer-directory-column-section-state-support" class="footer-directory-column-section-anchor footer-directory-column-section-anchor-open"> <span class="footer-directory-column-section-anchor-label">Open Menu</span> </a> <a href="#" class="footer-directory-column-section-anchor footer-directory-column-section-anchor-close"> <span class="footer-directory-column-section-anchor-label">Close Menu</span> </a> <ul class="footer-directory-column-section-list"> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/support/articles/">Support Articles</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/contact/">Contact Us</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/bug-reporting/">Bug Reporting</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/system-status/">System Status</a></li> </ul> </div> <input class="footer-directory-column-section-state" type="checkbox" id="footer-directory-column-section-state-account" /> <div class="footer-directory-column-section"> <label class="footer-directory-column-section-label" for="footer-directory-column-section-state-account"> <h3 class="footer-directory-column-section-title">Account</h3> </label> <a href="#footer-directory-column-section-state-account" class="footer-directory-column-section-anchor footer-directory-column-section-anchor-open"> <span class="footer-directory-column-section-anchor-label">Open Menu</span> </a> <a href="#" class="footer-directory-column-section-anchor footer-directory-column-section-anchor-close"> <span class="footer-directory-column-section-anchor-label">Close Menu</span> </a> <ul class="footer-directory-column-section-list"> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/account/">Apple Developer</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="https://appstoreconnect.apple.com/">App Store Connect</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/account/ios/certificate/">Certificates, IDs, & Profiles</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="https://feedbackassistant.apple.com/">Feedback Assistant</a></li> </ul> </div> </div> <div class="footer-directory-column"> <input class="footer-directory-column-section-state" type="checkbox" id="footer-directory-column-section-state-programs" /> <div class="footer-directory-column-section"> <label class="footer-directory-column-section-label" for="footer-directory-column-section-state-programs"> <h3 class="footer-directory-column-section-title">Programs</h3> </label> <a href="#footer-directory-column-section-state-programs" class="footer-directory-column-section-anchor footer-directory-column-section-anchor-open"> <span class="footer-directory-column-section-anchor-label">Open Menu</span> </a> <a href="#" class="footer-directory-column-section-anchor footer-directory-column-section-anchor-close"> <span class="footer-directory-column-section-anchor-label">Close Menu</span> </a> <ul class="footer-directory-column-section-list"> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/programs/">Apple Developer Program</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/programs/enterprise/">Apple Developer Enterprise Program</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/app-store/small-business-program/">App Store Small Business Program</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="https://mfi.apple.com/">MFi Program</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/programs/news-partner/">News Partner Program</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/programs/video-partner/">Video Partner Program</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/security-bounty/">Security Bounty Program</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/programs/security-research-device/">Security Research Device Program</a></li> </ul> </div> <input class="footer-directory-column-section-state" type="checkbox" id="footer-directory-column-section-state-events" /> <div class="footer-directory-column-section"> <label class="footer-directory-column-section-label" for="footer-directory-column-section-state-events"> <h3 class="footer-directory-column-section-title">Events</h3> </label> <a href="#footer-directory-column-section-state-events" class="footer-directory-column-section-anchor footer-directory-column-section-anchor-open"> <span class="footer-directory-column-section-anchor-label">Open Menu</span> </a> <a href="#" class="footer-directory-column-section-anchor footer-directory-column-section-anchor-close"> <span class="footer-directory-column-section-anchor-label">Close Menu</span> </a> <ul class="footer-directory-column-section-list"> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/events/">Meet with Apple</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/events/developer-centers/">Apple Developer Centers</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/app-store/app-store-awards/">App Store Awards</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/design/awards/">Apple Design Awards</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/academies/">Apple Developer Academies</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/entrepreneur-camp/">Entrepreneur Camp</a></li> <li class="footer-directory-column-section-item"><a class="footer-directory-column-section-link" href="/wwdc/">WWDC</a></li> </ul> </div> </div> <!--googleon: all--> </nav> <section class="footer-mini" vocab="http://schema.org/" typeof="Organization"> <div class="footer-mini-news"> <div class="copy"> Get the <a href="https://apps.apple.com/us/app/apple-developer/id640199958">Apple Developer app</a>. </div> <div class="content"> <div class="color-scheme-toggle" role="radiogroup" tabindex="0" aria-label="Select a color scheme preference"> <label data-color-scheme-option="light"> <input type="radio" value="light" autocomplete="off" onchange="window.setPreferredColorScheme(event.target.value)" /> <div class="text">Light</div> </label> <label data-color-scheme-option="dark"> <input type="radio" value="dark" autocomplete="off" onchange="window.setPreferredColorScheme(event.target.value)" /> <div class="text">Dark</div> </label> <label data-color-scheme-option="auto"> <input type="radio" value="auto" autocomplete="off" onchange="window.setPreferredColorScheme(event.target.value)" /> <div class="text">Auto</div> </label> </div> <script async src="/assets/scripts/color-scheme-toggle.js"></script> </div> </div> <link rel="stylesheet" href="/assets/styles/language-dropdown.css?17182448067"> <div class="language-dropdown dropdown-container legacy-form hidden"> <select class="dropdown" aria-label="Language Dropdown"></select> <span class="dropdown-icon icon icon-chevrondown" aria-hidden="true"></span> </div> <script src="/assets/scripts/language-dropdown.js?17182448067"></script> <div class="footer-mini-legal"> <div class="footer-mini-legal-copyright">Copyright © 2024 <a href="https://www.apple.com">Apple Inc.</a> All rights reserved.</div> <div class="footer-mini-legal-links"> <a class="footer-mini-legal-link" href="https://www.apple.com/legal/internet-services/terms/site.html" class="first">Terms of Use</a> <a class="footer-mini-legal-link" href="https://www.apple.com/legal/privacy/">Privacy Policy</a> <a class="footer-mini-legal-link" href="/support/terms/">Agreements and Guidelines</a> </div> </div> </section> </div> </footer> <script src="/assets/scripts/params.js"></script> <script src="/assets/scripts/watchable.js"></script> <script src="/assets/scripts/timeout.js"></script> <script src="/videos/scripts/play/play.js?04012409282"></script> <script src="/videos/scripts/hls/rtc.min.js?59182451067"></script> <script src="/videos/scripts/hls/hls.min.js?59182451067"></script> </body> </html>