{"$schema": "https://raw.githubusercontent.com/guitaripod/wwdc-sessions/master/schema/transcript.schema.json", "id": "wwdc2026-295", "language": "eng", "source": "https://developer.apple.com/videos/play/wwdc2026/295", "wordCount": 3137, "segments": [{"start": 7.0, "text": "Hi. I am Venkatesh, a Software Engineer on the App Intents team."}, {"start": 11.0, "text": "Today I'm going to show you how to test your App Intents"}, {"start": 14.0, "text": "with a brand new framework called AppIntentsTesting."}, {"start": 18.0, "text": "The App Intents framework is a core part"}, {"start": 21.0, "text": "of how your app integrates with the system."}, {"start": 24.0, "text": "And every year, it powers more experiences, for example,"}, {"start": 29.0, "text": "App Intents enables people to interact with their apps using Siri."}, {"start": 34.0, "text": "It enables people to use the Shortcuts app to build powerful automations."}, {"start": 40.0, "text": "Spotlight uses it to surface your app's contents in system-wide search."}, {"start": 46.0, "text": "Widgets use it to display your data on the Home Screen."}, {"start": 51.0, "text": "If you are new to AppIntents, check out this talk from WWDC25."}, {"start": 56.0, "text": "And if you want to learn about new advances,"}, {"start": 60.0, "text": "check out this talk from WWDC26."}, {"start": 64.0, "text": "With App Intents powering so many experiences,"}, {"start": 67.0, "text": "every intent, entity, and query needs to work correctly."}, {"start": 71.0, "text": "AppIntentsTesting is a brand new framework"}, {"start": 74.0, "text": "that lets you test all of that, and more."}, {"start": 77.0, "text": "Here is what we will cover today."}, {"start": 79.0, "text": "First, I will introduce the sample app,"}, {"start": 82.0, "text": "and show you how to write your first test."}, {"start": 86.0, "text": "Next, we'll look at how AppIntentsTesting runs your tests."}, {"start": 91.0, "text": "We'll then level up, by testing entity queries,"}, {"start": 95.0, "text": "and combining multiple intents."}, {"start": 98.0, "text": "And finally, we'll test some parts of App Intents"}, {"start": 102.0, "text": "that reach beyond your app; Spotlight and view annotations"}, {"start": 108.0, "text": "I'll now dive right in and show you how easy it is"}, {"start": 111.0, "text": "to get started with AppIntentsTesting."}, {"start": 115.0, "text": "This is CometCal, a sample calendar app."}, {"start": 119.0, "text": "It's a SwiftUI app that manages calendars, events,"}, {"start": 122.0, "text": "and attendees, and it fully integrates with App Intents."}, {"start": 127.0, "text": "My colleague Justin and I have been working on it"}, {"start": 129.0, "text": "to make it easier for our crew of space"}, {"start": 133.0, "text": "adventurers to manage our missions."}, {"start": 135.0, "text": "In this session, we'll learn how to test it,"}, {"start": 138.0, "text": "using AppIntentsTesting."}, {"start": 141.0, "text": "To learn about how we made CometCal available to Siri,"}, {"start": 144.0, "text": "check out his code-along."}, {"start": 148.0, "text": "I'll start off by writing a simple test that exercises the Create Calendar flow."}, {"start": 155.0, "text": "All I need to get started with AppIntentsTesting"}, {"start": 158.0, "text": "is a UI Testing bundle."}, {"start": 160.0, "text": "I don't have one yet, so let me create one."}, {"start": 174.0, "text": "Let me give it a descriptive name."}, {"start": 177.0, "text": "Next, I pick a development team."}, {"start": 181.0, "text": "AppIntentsTesting requires the test runner and the app"}, {"start": 185.0, "text": "to use the same development team for code signing,"}, {"start": 187.0, "text": "so I'll make sure to pick the correct Team here."}, {"start": 191.0, "text": "Next, I'll put in a Bundle Identifier."}, {"start": 195.0, "text": "And select the correct Target."}, {"start": 198.0, "text": "Click Finish."}, {"start": 202.0, "text": "This creates my UI testing bundle for me."}, {"start": 205.0, "text": "Let me create a new file for all my tests related to intent execution."}, {"start": 218.0, "text": "Great, I am now ready to start writing some tests."}, {"start": 225.0, "text": "Let me navigate to the intent I'm testing."}, {"start": 235.0, "text": "CreateCalendar creates a new calendar with a name, and color."}, {"start": 241.0, "text": "To begin, I need to import the AppIntentsTesting framework."}, {"start": 248.0, "text": "I start with a standard XCTestCase and create a test method."}, {"start": 254.0, "text": "I start the testing flow by creating an IntentDefinitions object."}, {"start": 261.0, "text": "IntentDefinitions takes in the CometCal bundle identifier,"}, {"start": 266.0, "text": "and gives me access to every intent, entity,"}, {"start": 269.0, "text": "and query the app defines without importing the app."}, {"start": 274.0, "text": "To access the definition of a particular intent,"}, {"start": 277.0, "text": "I can use the intents property"}, {"start": 279.0, "text": "and pass in the Intent name as a subscript."}, {"start": 284.0, "text": "This gives me an IntentDefinition object."}, {"start": 287.0, "text": "I can call makeIntent on it, to create a populated instance of the Intent."}, {"start": 296.0, "text": "I can pass in any parameters the intent expects."}, {"start": 304.0, "text": "Here, CreateCalendarIntent takes in a name and a color."}, {"start": 309.0, "text": "I am in charge of a critical mission for humanity,"}, {"start": 312.0, "text": "and I need a descriptive name so my crew and I can fulfill this mission."}, {"start": 320.0, "text": "I'll call it Occupy Saturn."}, {"start": 323.0, "text": "And give it a color."}, {"start": 325.0, "text": "I'll make it red."}, {"start": 328.0, "text": "Here, color is an AppEnum."}, {"start": 331.0, "text": "All I need to do is pass in the AppEnum's raw string value,"}, {"start": 335.0, "text": "and the framework converts it to the correct type."}, {"start": 339.0, "text": "The test code doesn't build against your app,"}, {"start": 342.0, "text": "therefore the parameter names and types do not offer auto-completion."}, {"start": 348.0, "text": "I need to make sure to fill them out correctly based on the intent parameters."}, {"start": 353.0, "text": "This type conversion works out of the box with most parameter types."}, {"start": 358.0, "text": "However, if you want to pass in your own custom types,"}, {"start": 362.0, "text": "look up IntentValueConvertibleWrapper in the documentation."}, {"start": 367.0, "text": "Now that I created the intent,"}, {"start": 369.0, "text": "I call the .run() method to execute it in app."}, {"start": 375.0, "text": "If I need the return value from an intent's perform method,"}, {"start": 379.0, "text": "I can capture the result of the run method,"}, {"start": 382.0, "text": "and use the value property to get it."}, {"start": 386.0, "text": "Here, CreateCalendarIntent returns the new calendar as a CalendarEntity."}, {"start": 393.0, "text": "This is CalendarEntity."}, {"start": 396.0, "text": "It has a title property that we can check."}, {"start": 400.0, "text": "So, if I want to assert that the new event has the correct title,"}, {"start": 407.0, "text": "I first access the new calendar using result.value,"}, {"start": 411.0, "text": "and simply chain the required property name to it."}, {"start": 415.0, "text": "This uses dynamic member lookup to get the title from the new calendar."}, {"start": 421.0, "text": "I am now ready to run this test."}, {"start": 423.0, "text": "But before I do so, let me switch over to my phone,"}, {"start": 427.0, "text": "and check out which calendars I currently have."}, {"start": 431.0, "text": "The app currently has three calendars:"}, {"start": 433.0, "text": "Deep Space, Mission Control, and Stargazing."}, {"start": 437.0, "text": "After I run the test,"}, {"start": 438.0, "text": "I expect the Occupy Saturn calendar to be added to the list."}, {"start": 444.0, "text": "Let me switch back to Xcode, and run this test."}, {"start": 456.0, "text": "The test succeeded."}, {"start": 458.0, "text": "Let me check the phone to confirm that the new calendar exists."}, {"start": 463.0, "text": "Yup, the test created the Occupy Saturn calendar"}, {"start": 467.0, "text": "with the color red."}, {"start": 470.0, "text": "All it took was five lines of code,"}, {"start": 473.0, "text": "and I executed an on-device App Intent using a test."}, {"start": 478.0, "text": "Now that you've seen AppIntentsTesting in action,"}, {"start": 481.0, "text": "I'll talk about it in more detail."}, {"start": 485.0, "text": "AppIntentsTesting is an integration testing framework for your App Intents."}, {"start": 489.0, "text": "Similar to UI tests, it uses strings to find your intents,"}, {"start": 494.0, "text": "without any access to the app's internals."}, {"start": 498.0, "text": "Your tests live in a standard XCUITest bundle,"}, {"start": 502.0, "text": "running in their own process."}, {"start": 505.0, "text": "Your app runs in a separate process, and executes AppIntents on-device."}, {"start": 511.0, "text": "The test runner executes your App Intents across process boundaries."}, {"start": 516.0, "text": "And receives execution results, without sharing any state."}, {"start": 521.0, "text": "Here's what AppIntentsTesting means for you as a developer."}, {"start": 525.0, "text": "Your tests run through the full App Intents stack;"}, {"start": 529.0, "text": "the same code path people hit."}, {"start": 531.0, "text": "No mocks, no stubs."}, {"start": 534.0, "text": "Create an XCUITest bundle, or add it to your existing XCUI tests."}, {"start": 540.0, "text": "Your Continuous Integration pipeline picks it up automatically."}, {"start": 545.0, "text": "Structure your app code any way you want."}, {"start": 547.0, "text": "Your test target never imports any code from your application-"}, {"start": 551.0, "text": "you only pass a bundle identifier."}, {"start": 555.0, "text": "That means you don't need to compile your app code into your testing target."}, {"start": 561.0, "text": "All this gives you stable tests across releases."}, {"start": 565.0, "text": "They don't depend on any UI. Not your app's. Not your system's."}, {"start": 571.0, "text": "With that overview in mind, I'll move on to some more involved tests."}, {"start": 576.0, "text": "Your app's actions, data and queries"}, {"start": 579.0, "text": "are the building blocks of your App Intents integration."}, {"start": 583.0, "text": "They power Shortcuts, Siri interactions, and Spotlight search."}, {"start": 588.0, "text": "When this code regresses, it can often lead"}, {"start": 591.0, "text": "to people experiencing unexpected system behaviors."}, {"start": 596.0, "text": "Whenever your entities are looked up-"}, {"start": 598.0, "text": "in Shortcuts, through Siri,"}, {"start": 600.0, "text": "or anywhere else App Intents surfaces them-"}, {"start": 603.0, "text": "your entity queries are responsible for returning the right results."}, {"start": 608.0, "text": "AppIntentsTesting helps you verify that behavior for string queries,"}, {"start": 612.0, "text": "identifier lookups, and suggested entities."}, {"start": 616.0, "text": "Here's the situation."}, {"start": 618.0, "text": "I have a mostly harmless device called a cosmic ray."}, {"start": 621.0, "text": "Due to an unfortunate incident,"}, {"start": 624.0, "text": "I need to rename the Cosmic Ray Calibration event"}, {"start": 627.0, "text": "to Cosmic Ray Rebuilding."}, {"start": 629.0, "text": "In fact, it's been happening so often, I should probably create a Shortcut for it."}, {"start": 636.0, "text": "The event already exists in the app."}, {"start": 639.0, "text": "So I go to the Shortcuts app,"}, {"start": 642.0, "text": "go to my Shortcut, and search for the event."}, {"start": 647.0, "text": "Initially, it shows a list of suggested events."}, {"start": 651.0, "text": "If I want to search for some other events,"}, {"start": 653.0, "text": "I can use the search bar which calls EventEntity's string query"}, {"start": 657.0, "text": "to determine the results."}, {"start": 660.0, "text": "No Options Available; the string query isn't implemented yet."}, {"start": 665.0, "text": "I'll use AppIntentsTesting to implement this with a test-driven approach."}, {"start": 671.0, "text": "Here's my test."}, {"start": 672.0, "text": "I start off by creating the entity and intent definitions."}, {"start": 678.0, "text": "I want to ensure that the app contains a known set of events,"}, {"start": 681.0, "text": "so I run SeedSampleEventsIntent."}, {"start": 685.0, "text": "This resets app data, and adds events."}, {"start": 688.0, "text": "I will come back to this shortly."}, {"start": 691.0, "text": "Ideally, I'd lift the Entity and Intent definitions"}, {"start": 695.0, "text": "out of the test function so other tests can reuse them,"}, {"start": 699.0, "text": "and move the data seeding to the setUp method."}, {"start": 702.0, "text": "Let me do that."}, {"start": 704.0, "text": "Going forward, I focus purely on test functionality."}, {"start": 709.0, "text": "Here is the updated test."}, {"start": 711.0, "text": "I call the Entity string query"}, {"start": 713.0, "text": "using the entities(matching:) method on the Event entity definition."}, {"start": 718.0, "text": "This executes the string query on-device."}, {"start": 721.0, "text": "The method returns an array of EventEntity representations,"}, {"start": 725.0, "text": "so you can assert on the count."}, {"start": 729.0, "text": "Similar to intents, I can use dynamic member lookup"}, {"start": 732.0, "text": "to get the value of any entity property."}, {"start": 735.0, "text": "Here, I am getting the title of the event,"}, {"start": 739.0, "text": "and asserting on it to confirm that the query works correctly."}, {"start": 743.0, "text": "I'll run the test."}, {"start": 745.0, "text": "And it fails, just as expected."}, {"start": 748.0, "text": "The failure tells me exactly what I need to build."}, {"start": 753.0, "text": "Now I navigate to the EventEntityQuery."}, {"start": 756.0, "text": "Currently, it can return events by identifier,"}, {"start": 761.0, "text": "and return suggested events."}, {"start": 763.0, "text": "However, it has no EntityStringQuery capability."}, {"start": 768.0, "text": "Let me add one."}, {"start": 770.0, "text": "I'll add an EntityStringQuery conformance"}, {"start": 774.0, "text": "and implement the entities(matching:) method."}, {"start": 777.0, "text": "Filter events by title and return the matched EventEntities."}, {"start": 783.0, "text": "Now I go back to the test and run it,"}, {"start": 786.0, "text": "and it passes."}, {"start": 788.0, "text": "I have confirmed that the Entity string query works as expected."}, {"start": 794.0, "text": "Now, I verify the changes on device."}, {"start": 798.0, "text": "First, I make sure the event exists."}, {"start": 801.0, "text": "Next, I go to the Shortcuts app,"}, {"start": 804.0, "text": "open my Shortcut, and search for the same event again."}, {"start": 809.0, "text": "This time, it finds the event."}, {"start": 814.0, "text": "I wrote the test first, then built the feature to make it pass."}, {"start": 819.0, "text": "That's test-driven development with AppIntentsTesting."}, {"start": 823.0, "text": "Now, it's time to rebuild that Cosmic Ray!"}, {"start": 827.0, "text": "Time to level up, by combining multiple intents in one test."}, {"start": 832.0, "text": "Often, to build complex Shortcuts,"}, {"start": 835.0, "text": "people take the results of an intent, and pass it as a parameter into another."}, {"start": 841.0, "text": "This flow forms the basis of many useful automations."}, {"start": 846.0, "text": "Here's the situation;"}, {"start": 847.0, "text": "I had set up a practice session for my team"}, {"start": 850.0, "text": "to get good at Asteroid Dodgeball."}, {"start": 853.0, "text": "However, one of our teammates is new,"}, {"start": 855.0, "text": "so I ended up changing the purpose of the Event to go over the rules instead."}, {"start": 861.0, "text": "Since this is a core workflow of the app, I want to write a test to simulate it."}, {"start": 867.0, "text": "This test builds on our previous examples."}, {"start": 870.0, "text": "Let me walk you through it."}, {"start": 873.0, "text": "Start off by running CreateEventIntent with the required parameters."}, {"start": 879.0, "text": "I've gone through how makeIntent handles type conversion in the previous examples."}, {"start": 885.0, "text": "The first three parameters all take primitive types."}, {"start": 890.0, "text": "The last one is more interesting."}, {"start": 892.0, "text": "The calendar parameter takes a CalendarEntity."}, {"start": 896.0, "text": "However, I just pass in a string."}, {"start": 899.0, "text": "The AppIntents runtime automatically calls the EntityStringQuery"}, {"start": 904.0, "text": "associated with the CalendarEntity and fills in the first matching value."}, {"start": 910.0, "text": "After creating the event, I check the title just to make sure."}, {"start": 916.0, "text": "Next, I run UpdateEventIntent."}, {"start": 919.0, "text": "This takes in an event, and any updates I want to make."}, {"start": 924.0, "text": "Since I am just updating the title, I pass that in."}, {"start": 929.0, "text": "For the event to update, I can directly send in"}, {"start": 932.0, "text": "the EventEntity from the original run method."}, {"start": 936.0, "text": "This mirrors how people compose intents in Shortcuts."}, {"start": 942.0, "text": "And finally, I assert that the event has the updated title."}, {"start": 947.0, "text": "This time, during test setup, I am launching the app,"}, {"start": 951.0, "text": "just so I can confirm it's working correctly."}, {"start": 954.0, "text": "Time to run the test."}, {"start": 957.0, "text": "The app launches,"}, {"start": 960.0, "text": "the event is created,"}, {"start": 963.0, "text": "and then updated."}, {"start": 968.0, "text": "Boom!"}, {"start": 969.0, "text": "Entity creation, chaining, update, and assertion;"}, {"start": 973.0, "text": "all on device, in a single test."}, {"start": 976.0, "text": "Now where did I keep my rulebook for Asteroid Dodgeball …"}, {"start": 981.0, "text": "AppIntentsTesting tests are lightweight, fast and can run on every commit."}, {"start": 987.0, "text": "For the test results to be reliable, each test needs to be self-contained."}, {"start": 992.0, "text": "Test-Only intents help with that."}, {"start": 996.0, "text": "Test-only intents are simple,"}, {"start": 998.0, "text": "focused intents that only exist to help your tests."}, {"start": 1001.0, "text": "You can use them to improve test functionality."}, {"start": 1005.0, "text": "You can create exactly the data your tests need."}, {"start": 1009.0, "text": "No leftover data from previous runs causing flaky results."}, {"start": 1014.0, "text": "You can jump directly to any view in your app without UI navigation."}, {"start": 1019.0, "text": "If you redesign a screen, these tests still work."}, {"start": 1023.0, "text": "And here's the broader idea;"}, {"start": 1026.0, "text": "test-only intents can wrap any functionality in your app,"}, {"start": 1030.0, "text": "even things you haven't adopted App Intents for yet."}, {"start": 1034.0, "text": "Internal navigation, data management,"}, {"start": 1036.0, "text": "state manipulation- wrap it in a test-only intent,"}, {"start": 1040.0, "text": "and you can test it through AppIntentsTesting."}, {"start": 1043.0, "text": "CometCal's source code includes a number of test-only intents."}, {"start": 1048.0, "text": "One example is SeedSampleEventsIntent from the earlier string query test."}, {"start": 1054.0, "text": "It creates a known set of events in your calendar."}, {"start": 1058.0, "text": "Here's how you can make any intent a test-only intent."}, {"start": 1063.0, "text": "Mark it with isDiscoverable: false."}, {"start": 1066.0, "text": "This prevents the system from exposing it anywhere else."}, {"start": 1071.0, "text": "And Wrap it in #if DEBUG compiler directive"}, {"start": 1074.0, "text": "so only your tests can access this intent."}, {"start": 1079.0, "text": "Up to this point, you've learned how to test your App Intents in isolation."}, {"start": 1084.0, "text": "The real power, though, is how App Intents"}, {"start": 1086.0, "text": "lets your features reach people across the system,"}, {"start": 1090.0, "text": "so they can use your app's functionality outside your app."}, {"start": 1094.0, "text": "I'll now show you how to test those system-level integrations."}, {"start": 1099.0, "text": "Spotlight lets people search across the entire system from one place."}, {"start": 1104.0, "text": "When you index your entities, they show up right in those results,"}, {"start": 1108.0, "text": "so people can find your app's content without needing to open the app first."}, {"start": 1114.0, "text": "When CometCal creates an event, it should index the entity in Spotlight."}, {"start": 1119.0, "text": "However, I once got an urgent transmission"}, {"start": 1122.0, "text": "from my fleet commander asking me when the Dark Matter Symposium was."}, {"start": 1128.0, "text": "I know that nanoseconds are precious,"}, {"start": 1130.0, "text": "so I wanted to use Spotlight to get this information"}, {"start": 1133.0, "text": "and reply pronto."}, {"start": 1135.0, "text": "However, the Spotlight search surfaced nothing."}, {"start": 1139.0, "text": "This information on Dark Matter is fascinating,"}, {"start": 1141.0, "text": "but not what I'm looking for!"}, {"start": 1144.0, "text": "I went to the app, and the event did indeed exist."}, {"start": 1148.0, "text": "I quickly sent them the details, and got to work debugging."}, {"start": 1154.0, "text": "I navigated to the event creation code to debug."}, {"start": 1158.0, "text": "The bug is simple; during development,"}, {"start": 1161.0, "text": "I commented out the indexing code and never re-enabled it."}, {"start": 1165.0, "text": "The app's behavior didn't change, so the bug went undetected."}, {"start": 1171.0, "text": "And the fix is simple,"}, {"start": 1173.0, "text": "just uncomment the indexing call."}, {"start": 1176.0, "text": "But how to make sure this never happens again?"}, {"start": 1180.0, "text": "I can write a test, not to find the bug, but to prevent a regression."}, {"start": 1187.0, "text": "I start by using the spotlightQuery() method"}, {"start": 1190.0, "text": "to talk to Spotlight."}, {"start": 1192.0, "text": "This method takes a string,"}, {"start": 1194.0, "text": "and returns a list of spotlight indexed EventEntity representations matching it."}, {"start": 1200.0, "text": "Initially, the event does not exist,"}, {"start": 1203.0, "text": "so I ensure that spotlightQuery() returns no results."}, {"start": 1208.0, "text": "Next, I use CreateEventIntent to create an event,"}, {"start": 1212.0, "text": "exactly the same as before."}, {"start": 1216.0, "text": "Once created, I expect the event to exist in the Spotlight index,"}, {"start": 1220.0, "text": "so I query for it again."}, {"start": 1222.0, "text": "I assert that there is exactly one result, and the title matches."}, {"start": 1228.0, "text": "Now I run the test…"}, {"start": 1230.0, "text": "And it passes."}, {"start": 1232.0, "text": "This test can now run on every commit."}, {"start": 1235.0, "text": "So if the Spotlight integration ever breaks again,"}, {"start": 1238.0, "text": "this test will catch it immediately."}, {"start": 1242.0, "text": "Now, to perform a quick check to confirm"}, {"start": 1245.0, "text": "that the event is indeed indexed in Spotlight."}, {"start": 1249.0, "text": "Great!"}, {"start": 1250.0, "text": "Now, my fleet commander gets their answer at the speed of Spotlight!"}, {"start": 1256.0, "text": "View annotations tell the system which entity your view is currently showing,"}, {"start": 1260.0, "text": "so Siri can understand what's on screen and act on it directly."}, {"start": 1266.0, "text": "CometCal tells Siri which EventEntity is on screen"}, {"start": 1270.0, "text": "every time you navigate to an event."}, {"start": 1272.0, "text": "So, if I am viewing an event,"}, {"start": 1275.0, "text": "and an unexpected wormhole jump yanks the phone from my hands,"}, {"start": 1279.0, "text": "I can always call out and say: \"Siri. What time is this event?\"."}, {"start": 1285.0, "text": "Ideally, the view annotation tells Siri exactly which event is on screen,"}, {"start": 1291.0, "text": "and Siri answers right away."}, {"start": 1293.0, "text": "But if that annotation is broken, Siri has no idea what's on screen."}, {"start": 1300.0, "text": "Now, inter-dimensional travel happens to me more often than I'd like to admit,"}, {"start": 1305.0, "text": "so I really need this feature working."}, {"start": 1308.0, "text": "Let me fix the the bug with the help of AppIntentsTesting."}, {"start": 1313.0, "text": "I start off by navigating to the event page of an event"}, {"start": 1317.0, "text": "using OpenEventIntent."}, {"start": 1321.0, "text": "Because this test exists in an XCUITest bundle,"}, {"start": 1324.0, "text": "I can use XCUI Automation to confirm that the app"}, {"start": 1328.0, "text": "indeed renders the correct event on the screen."}, {"start": 1332.0, "text": "I can then call the viewAnnotations() method on the Event entity."}, {"start": 1337.0, "text": "This method retrieves the list of EventEntity view annotations"}, {"start": 1341.0, "text": "the system reports as currently on screen."}, {"start": 1346.0, "text": "Now for the assertions."}, {"start": 1348.0, "text": "First, since only one event is on screen,"}, {"start": 1351.0, "text": "I assert that the system returns only one ViewAnnotation object."}, {"start": 1357.0, "text": "Then I assert that the Event entity is correct based on the title."}, {"start": 1363.0, "text": "A ViewAnnotation object has an entity property"}, {"start": 1366.0, "text": "that holds the actual entity representation."}, {"start": 1369.0, "text": "So, I can use dynamic member lookup"}, {"start": 1372.0, "text": "to assert on specific properties such as title."}, {"start": 1377.0, "text": "Now I run the test."}, {"start": 1379.0, "text": "And it fails."}, {"start": 1382.0, "text": "Then, I navigate to the EventDetailView to find out why."}, {"start": 1387.0, "text": "The issue is with the EntityIdentifier."}, {"start": 1391.0, "text": "I accidentally passed the event's calendar id as the identifier."}, {"start": 1396.0, "text": "The fix is simple; I change it to the event's id."}, {"start": 1402.0, "text": "Now I rerun the test."}, {"start": 1404.0, "text": "And this time it passes."}, {"start": 1408.0, "text": "So now, the next time inter-dimensional travel catches me off-guard,"}, {"start": 1413.0, "text": "I can say \"Siri. When is this event?\","}, {"start": 1419.0, "text": "and get the correct response."}, {"start": 1422.0, "text": "I can even follow up with \"Where is it?\","}, {"start": 1426.0, "text": "and Siri responds with the location."}, {"start": 1432.0, "text": "I've covered a lot today."}, {"start": 1434.0, "text": "Now, take a step back"}, {"start": 1436.0, "text": "and look at how AppIntentsTesting"}, {"start": 1438.0, "text": "can augment your App Intents development workflow."}, {"start": 1443.0, "text": "Start your AppIntents journey by implementing the fundamental types."}, {"start": 1448.0, "text": "These are your app's actions, data and queries."}, {"start": 1452.0, "text": "Now with AppIntentsTesting, you verify the foundational behavior of these types."}, {"start": 1458.0, "text": "These tests act as unit tests for your App Intents integration."}, {"start": 1464.0, "text": "Once the fundamentals are tested,"}, {"start": 1466.0, "text": "integrate your app more deeply with the system."}, {"start": 1470.0, "text": "Annotate your views with entities, donate to Spotlight and pass data between apps."}, {"start": 1477.0, "text": "AppIntentsTesting lets you cover these integrations too,"}, {"start": 1480.0, "text": "validating your app's experiences"}, {"start": 1483.0, "text": "across the many ways people use it."}, {"start": 1486.0, "text": "These can be your App Intents integration tests."}, {"start": 1490.0, "text": "With coverage at every layer, your app is ready"}, {"start": 1493.0, "text": "to unlock some truly powerful experiences."}, {"start": 1497.0, "text": "At this stage, be sure to test your intents manually"}, {"start": 1501.0, "text": "with Siri and the Shortcuts app"}, {"start": 1503.0, "text": "to experience them just the way people would."}, {"start": 1507.0, "text": "That's AppIntentsTesting."}, {"start": 1508.0, "text": "A single framework that lets you test your intents, entities, enums,"}, {"start": 1513.0, "text": "queries, and system integrations- all out-of-process, all automated."}, {"start": 1519.0, "text": "Download the CometCal sample project to try it yourself,"}, {"start": 1523.0, "text": "and explore the complete set of tests for the app."}, {"start": 1527.0, "text": "This talk only scratches the surface of what AppIntentsTesting can do."}, {"start": 1532.0, "text": "Check out the documentation for the full API reference."}, {"start": 1537.0, "text": "And if you want to learn how to build Siri experiences with App Intents,"}, {"start": 1541.0, "text": "check out this talk."}, {"start": 1543.0, "text": "Now you are ready to go and build something that is out of this world!"}, {"start": 1547.0, "text": "Thanks for watching!"}]}